Opened 3 years ago

Closed 3 years ago

#32190 closed Bug (duplicate)

Support for model relationships defined pre-save

Reported by: Ryan Vinzent Owned by: nobody
Component: Database layer (models, ORM) Version: 3.1
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

There are many situations where it is optimal to define a bunch of objects, and then later commit them in bulk. When the objects define a FK relationship, this strategy requires a bit of hacking that I would prefer not to do. Take the following example:

class Parent(models.Model):
    name = models.TextField()

class Child(models.Model):
    name = models.TextField()
    parent = models.ForeignKey(Parent, on_delete=models.RESTRICT)

Now if we have some function that defines a bunch of these objects to later commit them in bulk:

def build_objects(parent_child_mapping):
    parents = []
    children = []
    # {"father": ["son", "daughter"], ...}
    for parent_name, child_names in parent_child_mapping.items():
        parent = Parent(name=parent_name)
        parents.append(parent)
        children.extend(Child(parent=parent, name=child_name) for child_name in child_names)

    # now commit all objects in bulk
    Parent.objects.bulk_create(parents)
    
    # fails with IntegrityError: parent_id is not nullable
    Child.objects.bulk_create(children)

I would expect the above to work fine, given the parent ID's are known when inserting the children, however this would throw an IntegrityError because parent_id on the Child objects is still None.

Adding a simple hack to the function will let it work fine, but it's annoying to do this everywhere where this pattern of creating objects is followed.

def build_objects(parent_child_mapping):
    parents = []
    children = []
    for parent_name, child_names in parent_child_mapping.items():
        parent = Parent(name=parent_name)
        parents.append(parent)
        children.extend(Child(parent=parent, name=child_name) for child_name in child_names)

    # now commit all objects in bulk
    Parent.objects.bulk_create(parents)
    
    # add hack to make sure object IDs are properly assigned
    for child in children:
        child.parent_id = child.parent.id

    # now this call can succeed
    Child.objects.bulk_create(children)

This seems like something that should be supported by Django, and I believe this would make the ORM more consistent overall.

Thanks.

Change History (1)

comment:1 by Mariusz Felisiak, 3 years ago

Resolution: duplicate
Status: newclosed
Type: New featureBug

Duplicate of #29497. It was fixed in 10f8b82d195caa3745ba37d9424893763f89653e (Django 3.2+).

Note: See TracTickets for help on using tickets.
Back to Top