Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

_bulk_create flag is now creating related objects as well #206

Merged
merged 8 commits into from
Jun 22, 2021

Conversation

berinhard
Copy link
Member

Fixes #202

@amureki and @anapaulagomes please take a read before on Djangos bulk_create docs and you'll understand more the context behind this PR:

https://docs.djangoproject.com/en/3.0/ref/models/querysets/#bulk-create

Short summary: Django does not update the created objects via bulk and, because of that, there no way to avoid subsequent save calls on related objects. I've implemented with recursion because the same is true for the related of the related objects =}

Copy link
Contributor

@anapaulagomes anapaulagomes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good, @berinhard! 🎉 (please note that the pipeline is broken for py38 mypy)

@berinhard
Copy link
Member Author

Thanks @anapaulagomes! I noticed the mypy issue and I'll fix it as soon as possible! Thanks for the review =)

@berinhard
Copy link
Member Author

Also, @timjklein36 can review this one too 👍

Copy link
Collaborator

@timjklein36 timjklein36 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just had one question and a suggestion about replacing QueryCount with Django's assertNumQueries.

Also, any chance we could use bulk_create for all the related models as well? From the depth of the nested related objects up the tree?

Comment on lines +643 to +645
for i, fk_obj in enumerate(fk_objects):
fk_obj.save()
setattr(objects[i], fk.name, fk_obj)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the fk_objects do not match up 1-to-1 with objects? (One way the code looks like this could happen is if the condition on line 638 is not True and a given object's related model for this iteration's fk is not appended to the list. Could that ever happen? If not, then is the condition on line 638 necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the condition on 638 just as an explicit safeguard. For example, that condition enables people to write tests such as:

user = baker.make(User)
logs = baker.make(LogEntries, user=user, _quantity=10, _bulk_create=True)

Every log entry will already have a associated user with and id because it has already been previously created. If we remove 638 condition, baker can perform unexpected triggers on the user object. For example, what if's there a post_save method on the User model.

I mean, I just think it was safer to add this check to not perform extra and unnecessary save operations.

from django.db import connection


class QueryCount:
Copy link
Collaborator

@timjklein36 timjklein36 Jun 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I originally introduced this class when I was unaware of a built-in TransactionTestCase (Django's base TestCase class) way of doing it: https://docs.djangoproject.com/en/3.2/topics/testing/tools/#django.test.TransactionTestCase.assertNumQueries.

We could replace our use of this class like so:

Old Style

queries = QueryCount()

with queries.start_count():
    baker.make(models.LonelyPerson, _quantity=5, _bulk_create=True)
    assert queries.count == 6

With built-in assertNumQueries

with self.assertNumQueries(6):
    baker.make(models.LonelyPerson, _quantity=5, _bulk_create=True)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can refactor this to use django's assertNumQueries. Can you open an issue documenting it and label it as a good first issue?

@berinhard
Copy link
Member Author

@timjklein36 unfortunatelly we can't use bulk_create for the FKs because the method doesn't return the created objects nor update the existing one with their IDs. That's the reason why I had to call save manually: to be able to get the referenced IDs from the related objects without having to query for them after creating them using django's bulk_create.

Thanks for you review =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

_bulk_create does not create Relate Instance using OneToOneField
3 participants