get_or_create value of created in context

In a class I’ve got the following code:

def get_object(self, queryset=None):
    obj, created = models.HomePage.objects.get_or_create(node_id=self.request.user.node_id)

    return obj

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['delete_url'] = 'home-page-delete'

    return context

How do I return ‘created’ in the context to the template. In the template I want to whether the object was created or not.

Kind regards,

Johanna

I’m assuming that by “In a class” you mean in a Class-Based View.

If so, then the CBVs store a lot of data as attributes of itself (self).
(For example, if you look at the get method of the CBV, you’ll see this line:
self.object = self.get_object())

The easiest way to do this then would be to add the following line after the get_or_create call:

self.object_created = created

which then allows you to add that to the context in get_context_data:

context['was_created'] = self.object_created

(Change these variable names to names of your choosing - they’re just an example, intended to be descriptive, not prescriptive.)

Side note: If you’re not familiar with the Classy Class-Based Views site, it’s an extremely useful reference for understanding how the CBVs are put together.

Hi Ken,

Thanks again for answering my question.

I use CCBV a lot to get my head around CBVs, however, this time I wasn’t sure about my understanding of self.

Kind regards,

Johanna

Hi Ken,

I tried what you recommended:

def get_object(self, queryset=None):
    obj, created = models.HomePage.objects.get_or_create(node_id=self.request.user.node_id)
    self.object_created = created
    print(obj)
    print(created)
    return obj

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['was_created'] = self.object_created
    context['delete_url'] = 'home-page-delete'

For some reason the Boolean specifying whether a new object was created or not is False in both cases.

Could this be an SQLite issue?

Kind regards,

Johanna

What is this “node_id” attribute that you’re trying to reference on the user object?

How is it being set?

In models.py I have a class Node(models.Model) with an attribute id:

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

Then in models.py I have a class CustomUser(AbstractUser) with an attribute node:

node = models.OneToOneField(Node, on_delete=models.CASCADE, blank=True, null=True, related_name=‘custom_users’, related_query_name=‘custom_user’)

So there is a one-to-one relationship between user and node. There is also a one-to-one relationship between node and homepage:

I have a class HomePage(models.Model) with and attribute node:

node = models.OneToOneField(Node, on_delete=models.CASCADE, primary_key=True, related_name=‘home_pages’, related_query_name=‘home_page’)

In views.py I have a class HomePageEditView(UpdateView) in which I overwrite the get_object(self, queryset=None) method (see the code above)

The node_id is being set in the call to get_or_create():

obj, created = models.HomePage.objects.get_or_create(node_id=self.request.user.node_id)

getting or creating the home page works, the issue is that the Boolean specifying whether a new object was created or not is False in both cases.

I hope you know what’s causing the issue.

Kind regards,

Johanna

I’d toss in one more set of print statements there before the get_or_create call, just to verify I have what I think I have. I’d print each of self.request.user and self.request.user.node_id to verify the data being seen is what I’m expecting to see.

Please check your database. HomePage objects you are querying may already exists in the database or alternatively your code not using the correct database.

get_or_create(defaults=None, **kwargs)

A convenience method for looking up an object with the given kwargs (may be empty if your model has defaults for all fields), creating one if necessary.

Returns a tuple of (object, created), where objectis the retrieved or created object and created is a boolean specifying whether a new object was created.

You can pick an object where created is returning False and check with get method on class to know if it already exists.

Thank you both for your replies.

My code is using the correct database.

The models for the home page and about pages are identical:

class HomePage(models.Model):
node = models.OneToOneField(Node, on_delete=models.CASCADE, primary_key=True, related_name=‘home_pages’, related_query_name=‘home_page’)
content = models.TextField()

class AboutWhoPage(models.Model):
node = models.OneToOneField(Node, on_delete=models.CASCADE, primary_key=True, related_name=‘about_who_pages’, related_query_name=‘about_who_page’)
content = models.TextField()

When there is no data in the database and I request the HomePageEditView(UpdateView), the node_id, object and created attributes have these values:

node id abf2ff22-c4d2-4e59-aba5-f64ff7b3665e
object
created True
[11/Jul/2022 09:39:41] “GET /pages/homepage/ HTTP/1.1” 200 4112

When I enter a value for content and submit the form the attributes have these values:

node id abf2ff22-c4d2-4e59-aba5-f64ff7b3665e
object
created False
[11/Jul/2022 09:44:49] “POST /pages/homepage/ HTTP/1.1” 302 0

When I request the HomePageEditView(UpdateView) again, the attributes have these values:

node id abf2ff22-c4d2-4e59-aba5-f64ff7b3665e
object This is the home page content
created False
[11/Jul/2022 09:51:34] “GET /pages/homepage/ HTTP/1.1” 200 4280

When I want to edit the content for the ‘about who’ page and I request the AboutWhoPageEditView(UpdateView) this is the output:

node id: abf2ff22-c4d2-4e59-aba5-f64ff7b3665e
object:
created: False
[11/Jul/2022 09:54:26] “GET /pages/aboutwhopage/ HTTP/1.1” 200 4255

Although the AboutWhoPage table is empty the value of the created attributes is False, where I expect it to be True.

It’s as if the get_or_create() is ignoring the fact that I query a different table for node_id=self.request.user.node_id each time.

The following the output for adding content to an empty AboutWhoPage table:

object:
created: False
[11/Jul/2022 09:54:26] “GET /pages/aboutwhopage/ HTTP/1.1” 200 4255
node id: abf2ff22-c4d2-4e59-aba5-f64ff7b3665e
object:
created: False
[11/Jul/2022 10:02:03] “POST /pages/aboutwhopage/ HTTP/1.1” 302 0
[11/Jul/2022 10:02:03] “GET /pages/ HTTP/1.1” 200 5353
node id: abf2ff22-c4d2-4e59-aba5-f64ff7b3665e
object: This is the about who page conte
created: False
[11/Jul/2022 10:02:15] “GET /pages/aboutwhopage/ HTTP/1.1” 200 4289

As you can see it’s only the created attribute that doesn’t have the correct value.

Kind regards,

Johanna

Ok, now I think I’m more confused than before.

Those results look right to me.

Your get_object call is calling get_or_create on the HomePage model.

The first time you call it, there is no HomePage object with node_id abf2ff22-c4d2-4e59-aba5-f64ff7b3665e, and so the object is created. (created=True)

Every time after that, since the HomePage object already exists. it’s doing a get to retrieve that existing object, and so created = False.

I don’t understand how any other tables are going to affect this or how they’re supposed to affect this. I don’t see any references to any other models in what you’ve posted.

I found this solution on the Internet;

After starting form scratch with just the pages app and an empty SQLite database, I found that The problems are even worse.

This is the home page model:

class HomePage(models.Model):
node = models.OneToOneField(Node, on_delete=models.CASCADE, primary_key=True, related_name=‘home_pages’, related_query_name=‘home_page’)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
    return self.content[:32]

def get_absolute_url(self):
    return reverse('page-index')

and the view:

class HomePageEditView(UpdateView):
model = models.HomePage
fields = (
‘content’,
‘active’,
)
template_name = ‘pages/edit_form.html’
success_url = reverse_lazy(‘page-index’)

def get_object(self, queryset=None):
    obj, created = models.HomePage.objects.get_or_create(node_id=self.request.user.node_id)
    self.object_created = created
    print("node id", self.request.user.node_id)
    print("object", obj)
    print("created", created)

    return obj

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['was_created'] = self.object_created
    context['delete_url'] = 'home-page-delete'

    return context

Now, when I submit an empty form, I get a warning on the content field: Please fill in this field, however, when I browse for the data in the SQLIte database there is a home page record with an empty content field. I have no idea why this record is added even before submitting the form?

I think I’d better opt for an other solution, like maybe, having a class HomePageUpdateView(UpdateView) with a try except where except redirects to a HomePageCreateView(CreateView) in case there is no object.

Thanks for trying to help me solve this issue, It’s much appreciated.

Johanna

If you follow the flow of the UpdateView, you will see that the get_object method is called before the form is even created. (It happens in the get for the page.) So regardless of whether the form is ever submitted, your get_object method will create the HomePage instance.

Actually, in this case, I’m not sure I’d go that route. Since you’re talking about a effective one-to-one relationship with the user, I’d look at identifying a suitable default for that model that could be used to initialize it when the user is created. That way you don’t need to make this distinction between the two, and will generally simplify things overall.