Opened 5 years ago

Last modified 22 months ago

#31539 assigned New feature

Add support for bulk operations on reverse many-to-one manager

Reported by: Baptiste Mispelon Owned by: Rahul Biswas
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no
Pull Requests:How to create a pull request

Description

Using the Book and Author models from the docs:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
    title = models.CharField(max_length=200)

This works and creates two books linked to their author correctly:

toni = Author.objects.create(name="Toni Morrison")

toni.books.create(title="The Bluest Eye")
toni.books.create(title="Beloved")

But if you try to use bulk_create to achieve the same, you get an error:

toni = Author.objects.create(name="Toni Morrison")

toni.books.bulk_create([
    Book(title="The Bluest Eye"),
    Book(title="Beloved"),
])

This fails with an IntegrityError because bulk_create doesn't automatically fill in the value of Book.author (the way it does it for create()).

The documentation for RelatedManager [1] doesn't mention bulk_create() but it's not clear to me if that means the operation is unsupported or not (other methods like count() or filter() are not listed either but are clearly supported). Because of this I wasn't sure whether to mark this ticket as a bug or as a new feature.

Looking at the code [2], I can't find an explanation of why bulk_create or bulk_update are not implemented either.
I've come up with the following implementation which seems to work (I haven't tested it extensively):

def bulk_create(self, objs, **kwargs):
    def set_field_to_instance(instance):
        setattr(instance, self.field.name, self.instance)
        return instance

    objs = map(set_field_to_instance, objs)
    db = router.db_for_write(self.model, instance=self.instance)
    return super(RelatedManager, self.db_manager(db)).bulk_create(objs, **kwargs)

[1] https://docs.djangoproject.com/en/dev/ref/models/relations/#django.db.models.fields.related.RelatedManager
[2] https://github.com/django/django/blob/aff7a58aef0264e5b2740e5df07894ecc0d7a580/django/db/models/fields/related_descriptors.py#L559

According to the ticket's flags, the next step(s) to move this issue forward are:

  • To provide a patch by sending a pull request. Claim the ticket when you start working so that someone else doesn't duplicate effort. Before sending a pull request, review your work against the patch review checklist. Check the "Has patch" flag on the ticket after sending a pull request and include a link to the pull request in the ticket comment when making that update. The usual format is: [https://github.com/django/django/pull/#### PR].

Change History (4)

comment:1 by Baptiste Mispelon, 5 years ago

Owner: changed from nobody to Baptiste Mispelon
Status: newassigned

I'll try to come up with a PR. If you don't see any activity from me within a week you can assume I've given up and anyone can feel free to reassign the ticket to themselves :)

comment:2 by Simon Charette, 5 years ago

Triage Stage: UnreviewedAccepted

I think #27408 should be closed in favour of this ticket as we should only do this automatically for reverse many-to-one managers of saved objects and not systematically for all reverse relationship a model has. I think the scope of this ticket is better defined and more in line with the rest of the Manager interface.

Last edited 5 years ago by Simon Charette (previous) (diff)

comment:3 by Mariusz Felisiak, 2 years ago

Owner: Baptiste Mispelon removed
Status: assignednew

comment:4 by Rahul Biswas, 22 months ago

Owner: set to Rahul Biswas
Status: newassigned
Note: See TracTickets for help on using tickets.
Back to Top