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

Fixed #30493 -- Fixed prefetch_related() for GenericRelation with different content types. #11423

Merged
merged 2 commits into from May 31, 2019

Conversation

cansarigol
Copy link
Contributor

Ticket: #30493

Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

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

@cansarigol Thanks for this patch 👍 I left some comments.

django/contrib/contenttypes/fields.py Outdated Show resolved Hide resolved
tests/generic_relations_regress/models.py Outdated Show resolved Hide resolved
tests/generic_relations_regress/tests.py Outdated Show resolved Hide resolved
django/contrib/contenttypes/fields.py Outdated Show resolved Hide resolved
django/contrib/contenttypes/fields.py Outdated Show resolved Hide resolved
@cansarigol
Copy link
Contributor Author

@felixxm thanks for your review. I applied your comments, could you review it?

@felixxm felixxm force-pushed the ticket-30493 branch 3 times, most recently from ba3b8d1 to ece29ed Compare May 30, 2019 09:57
@felixxm
Copy link
Member

felixxm commented May 30, 2019

I rebased from master, moved GenericRelatedObjectManager.get_content_type() to a separate commit, edited tests and grouped related objects by content types (it is more optimal than adding separate condition for each object).

@felixxm felixxm changed the title Fixed #30493 -- Fixed GenericRelation's prefetch queryset Fixed #30493 -- Fixed #30493 -- Fixed prefetch_related() for GenericRelation with different content types. May 30, 2019
@felixxm felixxm changed the title Fixed #30493 -- Fixed #30493 -- Fixed prefetch_related() for GenericRelation with different content types. Fixed #30493 -- Fixed prefetch_related() for GenericRelation with different content types. May 30, 2019
@cansarigol
Copy link
Contributor Author

Should we use subTest for forloop test?

@felixxm
Copy link
Member

felixxm commented May 30, 2019

I don't think so because it is not a "sub-test" (we're not testing different conditions here), all three will work or not.

@cansarigol
Copy link
Contributor Author

I got it thanks

Copy link
Member

@charettes charettes left a comment

Choose a reason for hiding this comment

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

I think it might be worth adding some form of memoization to get_content_type calls by (obj._state.db, obj.__class__) in get_prefetch_queryset as there will potentially be lot of them and they'll result in a non negligible amount of work.

django/contrib/contenttypes/fields.py Outdated Show resolved Hide resolved
django/contrib/contenttypes/fields.py Outdated Show resolved Hide resolved
django/contrib/contenttypes/fields.py Outdated Show resolved Hide resolved
@felixxm
Copy link
Member

felixxm commented May 30, 2019

@charettes Thanks for the review 👍 get_for_model() is already cached.

@charettes
Copy link
Member

@felixxm I know but each ContentType.objects.db_manager(obj._state.db) results in three attributes lookup and a Manager instance. That's not even counting the function call and the cache lookup in get_for_models. That can grow to a large amount of time if instances is large.

@felixxm
Copy link
Member

felixxm commented May 30, 2019

I will add it tomorrow.

@felixxm
Copy link
Member

felixxm commented May 31, 2019

@charettes I added basic memoization. What do you think?

@cansarigol
Copy link
Contributor Author

What if we add self.manager in __init__()to use the get_for_models cache?
self.manager = ContentType.objects.db_manager(instance._state.db)

Copy link
Member

@charettes charettes left a comment

Choose a reason for hiding this comment

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

This memoize_content_type seems to be working but is not kept in sync with ContentType.objects.clear_cache().

I like @cansarigol's idea of caching it at __init__ though which should be doable given all objects should be on the same database.

e.g. in __init__

self.get_content_type = partial(
    ContentType.objects.db_manager(instance._state.db).get_for_model,
    for_concrete_model=rel.field.for_concrete_model,
)
self.content_type = self.get_content_type(instance)

django/contrib/contenttypes/fields.py Outdated Show resolved Hide resolved
felixxm and others added 2 commits May 31, 2019 18:11
…ferent content types.

Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>

Thanks Simon Charette for the review.
@felixxm
Copy link
Member

felixxm commented May 31, 2019

@charettes Thanks 🏆 I updated PR.

@felixxm felixxm merged commit dffa3e1 into django:master May 31, 2019
})
for content_type_id, objs in itertools.groupby(
sorted(instances, key=lambda obj: self.get_content_type(obj).pk),
lambda obj: self.get_content_type(obj).pk,
Copy link
Member

Choose a reason for hiding this comment

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

This seems rather inefficient... Doesn't self.get_content_type(obj) execute a query?

Copy link
Member

Choose a reason for hiding this comment

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

@pope1ni no, it's heavily cached as it's using ContentType.objects.get_for_model.

@cansarigol cansarigol deleted the ticket-30493 branch June 25, 2019 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants