Accessing child relations when overriding save()

144 views
Skip to first unread message

Nick Emery

unread,
Feb 10, 2019, 1:34:44 PM2/10/19
to Django users
I am trying to wrap up my first app using Django (specifically Django Rest Framework which may change the save behavior), but have run into an issue that I haven't been able to solve for about 10 hours now.

I am trying to override the save() method of a model to modify a field on a bunch of child objects when it is created; however, I can't figure out any way get the objects or ids for the children.

My code looks something like:
class Parent(models.Model):
   """When I create this, I want the children to be modified."""
   def save(self, *args, **kwargs):
       if not self.pk:
           self._begin(*args, **kwargs)
       else:
           super(Parent, self).save(*args, **kwargs)

    def _begin(self, *args, **kwargs):
       super(Parent, self).save(*args, **kwargs)
       print(self.child_set.all())  # --> This gives: "<QuerySet []>"
       for i in self.child_set.all():
           i.last_name = self.last_name
           i.save()

    last_name = models.CharField(max_length=100)


class Child:
   parent = models.ForeignKey(
       'Parent',
       on_delete=models.SET_NULL,
       null=True,
    )
   last_name = models.CharField(max_length=100)

ANY way to modify the children when the parent is saved would be a life saver (even if hacky or sub-optimal).

Shashank Singh

unread,
Feb 10, 2019, 1:37:54 PM2/10/19
to django...@googlegroups.com
Override the save() of the child?

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/24c7156b-6873-4cd3-952e-dc819fa72be0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Nick Emery

unread,
Feb 10, 2019, 3:47:16 PM2/10/19
to Django users
Tried this too but save() never actually gets called on the child (it seems that the foreign key field of the child is updated at the database level and never goes through the Django orm Child).

Shashank Singh

unread,
Feb 10, 2019, 3:54:08 PM2/10/19
to django...@googlegroups.com
When do you create child objects??

Shashank Singh

unread,
Feb 10, 2019, 3:54:10 PM2/10/19
to django...@googlegroups.com
And how?

Nick Emery

unread,
Feb 10, 2019, 4:15:46 PM2/10/19
to Django users
The child objects are created separately beforehand with NULL parents (they don't have to have parents, they can be orphans). One other thing I noticed is that looking at the SQL statements executed by my app I can't even see the UPDATE on the foreign keys of the children (even though I KNOW it's happening because I can go look at those children and see that they do get updated). It's as if Django doesn't even know about this at all.

Nick Emery

unread,
Feb 10, 2019, 5:16:25 PM2/10/19
to Django users
UPDATE: Finally figured it out! I ultimately did this by overriding `perform_create()` in my [Django Rest Framework view](https://www.django-rest-framework.org/api-guide/generic-views/) and making my changes after saving the object.

Mike Dewhirst

unread,
Feb 11, 2019, 3:04:36 AM2/11/19
to django-users@googlegroups.com >> Django users
On 10/02/2019 6:22 pm, Nick Emery wrote:
> I am trying to wrap up my first app using Django (specifically Django
> Rest Framework which may change the save behavior), but have run into
> an issue that I haven't been able to solve for about 10 hours now.
>
> I am trying to override the save() method of a model to modify a field
> on a bunch of child objects when it is created; however, I can't
> figure out /any/ way get the objects or ids for the children.
>
> My code looks something like:

I usually have a couple of methods to simplify save operations like this ...

def save(self, *args, **kwargs):
    results = self._pre_save(self)
    super(Parent, self).save(*args, **kwargs)
    self._post_save(self, results)

Much easier for me to "see" what I'm doing.

In your case if orphans exist, how do you identify them as now related
to a new Parent? Perhaps a query looking for parent=None.

From what I understand of your use case you would need to select a
Child when creating a Parent. That sounds like a field widget to perform
that query before the Parent is first saved. If so, the Child object
would be available to _pre_save() but the Parent still does not yet
exist and Parent.id is still None so you can't populate the Child FK.

Parent.id is however available to _post_save() so if the Child object is
passed from _pre_save() via results (above) then _post_save can populate
its FK with self and just save the Child object to complete the deal.

The relationship is the foreign key which means the Child can have only
one Parent which doesn't yet exist. So it feels like a sky hook.

Parents and children (as data) are person objects with virtually
identical attributes. The interesting information is in the relationship
for which a foreign key seems too restrictive. Have you considered a
"through" table thus making a many-to-many relationship? That table can
carry heaps of information about the relationship and is very flexible.
For example, the child can now have two parents.

When a "through" record is created to establish a relationship
(selectable from a choices list), both parent and child already exist
which lets you delegate a fair bit of processing to the "though" model
save() method which in real world timing is probably where it belongs. I
prefer it because I can use queries rather than widgets.

Not sure if this helps

Mike

> |
> classParent(models.Model):
> """When I create this, I want the children to be modified."""
> defsave(self,*args,**kwargs):
> ifnotself.pk <http://self.pk/>:
> self._begin(*args,**kwargs)
> else:
> super(Parent,self).save(*args,**kwargs)
>
> def_begin(self,*args,**kwargs):
> super(Parent,self).save(*args,**kwargs)
> print(self.child_set.all())# --> This gives: "<QuerySet []>"
>    for i in self.child_set.all():
>        i.last_name = self.last_name
>        i.save()
>
> last_name =models.CharField(max_length=100)
>
>
> classChild:
>    parent =models.ForeignKey(
> 'Parent',
>        on_delete=models.SET_NULL,
> null=True,
> )
>  last_name =models.CharField(max_length=100)
> |
>
> ANY way to modify the children when the parent is saved would be a
> life saver (even if hacky or sub-optimal).
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> <https://groups.google.com/d/msgid/django-users/24c7156b-6873-4cd3-952e-dc819fa72be0%40googlegroups.com?utm_medium=email&utm_source=footer>.
Reply all
Reply to author
Forward
0 new messages