4

Consider the following code, in which I have one parent, and all the child models related to the parent by ForeignKey relationship. Each child may have their own child, making the whole family look like a tree structure.

class Parent(models.Model):
    field = models.CharField(max_length=100, primary_key=True)

class Child_1(models.Model):
    parent = models.ForeignKey(Parent, models.CASCADE, related_name='aa')

class Child_2(models.Model):
    parent = models.ForeignKey(Parent, models.CASCADE, related_name='aa')

class Child_1_Child_1(models.Model):
    parent = models.ForeignKey(Child_1, models.CASCADE, related_name='aa')

class Child_1_Child_2(models.Model):
    parent = models.ForeignKey(Child_1, models.CASCADE, related_name='aa')

Upon making an object for Parent, I want to create all the child objects.

I guess I can create all the child objects like this:

parent = Parent.objects.create(**kwargs)
child_1 = Child_1.objects.create(parent=parent)
child_2 = Child_2.objects.create(parent=parent)
child_1_child_1 = Child_1_Child_1.objects.create(parent=child_1)
child_1_child_2 = Child_1_Child_2.objects.create(parent=child_1)
...

But you know, this doesn't look very good. Is there any built-in Django method that handles this kind of parent-child object creation in chain?

Eric Kim
  • 2,493
  • 6
  • 33
  • 69
  • Well personally I wonder why you want to construct all those objects in the first place. What if some models require extra columns (without a default value), then what to fill in? – Willem Van Onsem Aug 10 '18 at 07:33
  • @WillemVanOnsem This for data management system. When user clicks a parent object, it will redirect to a page where a user can edit all the child fields for that parent object. Initially all the child fields will be blank, and the user can fill the fields if its blank, else, user can update field values. But to do that, child objects needs to be present in the DB, so that the user can edit those child fields – Eric Kim Aug 10 '18 at 07:39

3 Answers3

8

Override save() method of model as,

class Parent(models.Model):
    field = models.CharField(max_length=100, primary_key=True)

    def save(self, *args, **kwargs):
        is_new = not self.pk
        super().save(*args, **kwargs)
        if is_new:
            Child_1.objects.create(parent=self)
            Child_2.objects.create(parent=self)


class Child_1(models.Model):
    parent = models.ForeignKey(Parent, models.CASCADE, related_name='aa')

    def save(self, *args, **kwargs):
        is_new = not self.pk
        super().save(*args, **kwargs)
        if is_new:
            Child_1_Child_1.objects.create(parent=self)


class Child_2(models.Model):
    parent = models.ForeignKey(Parent, models.CASCADE, related_name='aa')

    def save(self, *args, **kwargs):
        is_new = not self.pk
        super().save(*args, **kwargs)
        if is_new:
            Child_1_Child_2.objects.create(parent=self)


class Child_1_Child_1(models.Model):
    parent = models.ForeignKey(Child_1, models.CASCADE, related_name='aa')


class Child_1_Child_2(models.Model):
    parent = models.ForeignKey(Child_1, models.CASCADE, related_name='aa')
JPG
  • 82,442
  • 19
  • 127
  • 206
5

In Django, you would usually use Signals for this. A standard situation is when you create a new User and want their Profile to be automatically created.

Example from this answer:

def create_profile(sender, **kwargs):
    user = kwargs["instance"]
    if kwargs["created"]:
        up = UserProfile(user=user, stuff=1, thing=2)
        up.save()

post_save.connect(create_profile, sender=User)

So in your case, you can use post_save to connect the Parent with a function that creates all the child items.

C14L
  • 12,153
  • 4
  • 39
  • 52
1

I would recommend using something like django-mptt to deal with trees in Django.

If you really want to keep doing things manually, see @C14L answer. Or just put your code in a Parent method and then call something like parent.create_children().

Cyrlop
  • 1,894
  • 1
  • 17
  • 31