Loading django/contrib/contenttypes/fields.py +9 −2 Original line number Diff line number Diff line Loading @@ -521,12 +521,19 @@ def create_generic_related_manager(superclass, rel): def __str__(self): return repr(self) def _apply_rel_filters(self, queryset): """ Filter the queryset for the instance this manager is bound to. """ db = self._db or router.db_for_read(self.model, instance=self.instance) return queryset.using(db).filter(**self.core_filters) def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.prefetch_cache_name] except (AttributeError, KeyError): db = self._db or router.db_for_read(self.model, instance=self.instance) return super(GenericRelatedObjectManager, self).get_queryset().using(db).filter(**self.core_filters) queryset = super(GenericRelatedObjectManager, self).get_queryset() return self._apply_rel_filters(queryset) def get_prefetch_queryset(self, instances, queryset=None): if queryset is None: Loading django/db/models/fields/related_descriptors.py +30 −18 Original line number Diff line number Diff line Loading @@ -502,23 +502,29 @@ def create_reverse_many_to_one_manager(superclass, rel): return manager_class(self.instance) do_not_call_in_templates = True def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.field.related_query_name()] except (AttributeError, KeyError): def _apply_rel_filters(self, queryset): """ Filter the queryset for the instance this manager is bound to. """ db = self._db or router.db_for_read(self.model, instance=self.instance) empty_strings_as_null = connections[db].features.interprets_empty_strings_as_nulls qs = super(RelatedManager, self).get_queryset() qs._add_hints(instance=self.instance) queryset._add_hints(instance=self.instance) if self._db: qs = qs.using(self._db) qs = qs.filter(**self.core_filters) queryset = queryset.using(self._db) queryset = queryset.filter(**self.core_filters) for field in self.field.foreign_related_fields: val = getattr(self.instance, field.attname) if val is None or (val == '' and empty_strings_as_null): return qs.none() qs._known_related_objects = {self.field: {self.instance.pk: self.instance}} return qs return queryset.none() queryset._known_related_objects = {self.field: {self.instance.pk: self.instance}} return queryset def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.field.related_query_name()] except (AttributeError, KeyError): queryset = super(RelatedManager, self).get_queryset() return self._apply_rel_filters(queryset) def get_prefetch_queryset(self, instances, queryset=None): if queryset is None: Loading Loading @@ -776,15 +782,21 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): filters |= symmetrical_filters return filters def _apply_rel_filters(self, queryset): """ Filter the queryset for the instance this manager is bound to. """ queryset._add_hints(instance=self.instance) if self._db: queryset = queryset.using(self._db) return queryset._next_is_sticky().filter(**self.core_filters) def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.prefetch_cache_name] except (AttributeError, KeyError): qs = super(ManyRelatedManager, self).get_queryset() qs._add_hints(instance=self.instance) if self._db: qs = qs.using(self._db) return qs._next_is_sticky().filter(**self.core_filters) queryset = super(ManyRelatedManager, self).get_queryset() return self._apply_rel_filters(queryset) def get_prefetch_queryset(self, instances, queryset=None): if queryset is None: Loading django/db/models/query.py +21 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ from django.db.models.query_utils import ( ) from django.db.models.sql.constants import CURSOR from django.utils import six, timezone from django.utils.deprecation import RemovedInDjango20Warning from django.utils.functional import partition from django.utils.version import get_version Loading Loading @@ -1608,6 +1609,9 @@ def prefetch_one_level(instances, prefetcher, lookup, level): msg = 'to_attr={} conflicts with a field on the {} model.' raise ValueError(msg.format(to_attr, model.__name__)) # Whether or not we're prefetching the last part of the lookup. leaf = len(lookup.prefetch_through.split(LOOKUP_SEP)) - 1 == level for obj in instances: instance_attr_val = instance_attr(obj) vals = rel_obj_cache.get(instance_attr_val, []) Loading @@ -1621,8 +1625,23 @@ def prefetch_one_level(instances, prefetcher, lookup, level): setattr(obj, to_attr, vals) obj._prefetched_objects_cache[cache_name] = vals else: # Cache in the QuerySet.all(). qs = getattr(obj, to_attr).all() manager = getattr(obj, to_attr) if leaf and lookup.queryset is not None: try: apply_rel_filter = manager._apply_rel_filters except AttributeError: warnings.warn( "The `%s.%s` class must implement a `_apply_rel_filters()` " "method that accepts a `QuerySet` as its single " "argument and returns an appropriately filtered version " "of it." % (manager.__class__.__module__, manager.__class__.__name__), RemovedInDjango20Warning, ) qs = manager.get_queryset() else: qs = apply_rel_filter(lookup.queryset) else: qs = manager.get_queryset() qs._result_cache = vals # We don't want the individual qs doing prefetch_related now, # since we have merged this into the current work. Loading docs/internals/deprecation.txt +3 −0 Original line number Diff line number Diff line Loading @@ -144,6 +144,9 @@ details on these changes. * The ``__search`` query lookup and the ``DatabaseOperations.fulltext_search_sql()`` method will be removed. * The shim for supporting custom related manager classes without a ``_apply_rel_filters()`` method will be removed. .. _deprecation-removed-in-1.10: 1.10 Loading docs/releases/1.10.txt +11 −0 Original line number Diff line number Diff line Loading @@ -762,6 +762,17 @@ features, is deprecated. Replace it with a custom lookup:: models.CharField.register_lookup(Search) models.TextField.register_lookup(Search) Custom manager classes available through ``prefetch_related`` must define a ``_apply_rel_filters()`` method ----------------------------------------------------------------------------------------------------------- If you defined a custom manager class available through :meth:`~django.db.models.query.QuerySet.prefetch_related` you must make sure it defines a ``_apply_rel_filters()`` method. This method must accept a :class:`~django.db.models.query.QuerySet` instance as its single argument and return a filtered version of the queryset for the model instance the manager is bound to. Miscellaneous ------------- Loading Loading
django/contrib/contenttypes/fields.py +9 −2 Original line number Diff line number Diff line Loading @@ -521,12 +521,19 @@ def create_generic_related_manager(superclass, rel): def __str__(self): return repr(self) def _apply_rel_filters(self, queryset): """ Filter the queryset for the instance this manager is bound to. """ db = self._db or router.db_for_read(self.model, instance=self.instance) return queryset.using(db).filter(**self.core_filters) def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.prefetch_cache_name] except (AttributeError, KeyError): db = self._db or router.db_for_read(self.model, instance=self.instance) return super(GenericRelatedObjectManager, self).get_queryset().using(db).filter(**self.core_filters) queryset = super(GenericRelatedObjectManager, self).get_queryset() return self._apply_rel_filters(queryset) def get_prefetch_queryset(self, instances, queryset=None): if queryset is None: Loading
django/db/models/fields/related_descriptors.py +30 −18 Original line number Diff line number Diff line Loading @@ -502,23 +502,29 @@ def create_reverse_many_to_one_manager(superclass, rel): return manager_class(self.instance) do_not_call_in_templates = True def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.field.related_query_name()] except (AttributeError, KeyError): def _apply_rel_filters(self, queryset): """ Filter the queryset for the instance this manager is bound to. """ db = self._db or router.db_for_read(self.model, instance=self.instance) empty_strings_as_null = connections[db].features.interprets_empty_strings_as_nulls qs = super(RelatedManager, self).get_queryset() qs._add_hints(instance=self.instance) queryset._add_hints(instance=self.instance) if self._db: qs = qs.using(self._db) qs = qs.filter(**self.core_filters) queryset = queryset.using(self._db) queryset = queryset.filter(**self.core_filters) for field in self.field.foreign_related_fields: val = getattr(self.instance, field.attname) if val is None or (val == '' and empty_strings_as_null): return qs.none() qs._known_related_objects = {self.field: {self.instance.pk: self.instance}} return qs return queryset.none() queryset._known_related_objects = {self.field: {self.instance.pk: self.instance}} return queryset def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.field.related_query_name()] except (AttributeError, KeyError): queryset = super(RelatedManager, self).get_queryset() return self._apply_rel_filters(queryset) def get_prefetch_queryset(self, instances, queryset=None): if queryset is None: Loading Loading @@ -776,15 +782,21 @@ def create_forward_many_to_many_manager(superclass, rel, reverse): filters |= symmetrical_filters return filters def _apply_rel_filters(self, queryset): """ Filter the queryset for the instance this manager is bound to. """ queryset._add_hints(instance=self.instance) if self._db: queryset = queryset.using(self._db) return queryset._next_is_sticky().filter(**self.core_filters) def get_queryset(self): try: return self.instance._prefetched_objects_cache[self.prefetch_cache_name] except (AttributeError, KeyError): qs = super(ManyRelatedManager, self).get_queryset() qs._add_hints(instance=self.instance) if self._db: qs = qs.using(self._db) return qs._next_is_sticky().filter(**self.core_filters) queryset = super(ManyRelatedManager, self).get_queryset() return self._apply_rel_filters(queryset) def get_prefetch_queryset(self, instances, queryset=None): if queryset is None: Loading
django/db/models/query.py +21 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ from django.db.models.query_utils import ( ) from django.db.models.sql.constants import CURSOR from django.utils import six, timezone from django.utils.deprecation import RemovedInDjango20Warning from django.utils.functional import partition from django.utils.version import get_version Loading Loading @@ -1608,6 +1609,9 @@ def prefetch_one_level(instances, prefetcher, lookup, level): msg = 'to_attr={} conflicts with a field on the {} model.' raise ValueError(msg.format(to_attr, model.__name__)) # Whether or not we're prefetching the last part of the lookup. leaf = len(lookup.prefetch_through.split(LOOKUP_SEP)) - 1 == level for obj in instances: instance_attr_val = instance_attr(obj) vals = rel_obj_cache.get(instance_attr_val, []) Loading @@ -1621,8 +1625,23 @@ def prefetch_one_level(instances, prefetcher, lookup, level): setattr(obj, to_attr, vals) obj._prefetched_objects_cache[cache_name] = vals else: # Cache in the QuerySet.all(). qs = getattr(obj, to_attr).all() manager = getattr(obj, to_attr) if leaf and lookup.queryset is not None: try: apply_rel_filter = manager._apply_rel_filters except AttributeError: warnings.warn( "The `%s.%s` class must implement a `_apply_rel_filters()` " "method that accepts a `QuerySet` as its single " "argument and returns an appropriately filtered version " "of it." % (manager.__class__.__module__, manager.__class__.__name__), RemovedInDjango20Warning, ) qs = manager.get_queryset() else: qs = apply_rel_filter(lookup.queryset) else: qs = manager.get_queryset() qs._result_cache = vals # We don't want the individual qs doing prefetch_related now, # since we have merged this into the current work. Loading
docs/internals/deprecation.txt +3 −0 Original line number Diff line number Diff line Loading @@ -144,6 +144,9 @@ details on these changes. * The ``__search`` query lookup and the ``DatabaseOperations.fulltext_search_sql()`` method will be removed. * The shim for supporting custom related manager classes without a ``_apply_rel_filters()`` method will be removed. .. _deprecation-removed-in-1.10: 1.10 Loading
docs/releases/1.10.txt +11 −0 Original line number Diff line number Diff line Loading @@ -762,6 +762,17 @@ features, is deprecated. Replace it with a custom lookup:: models.CharField.register_lookup(Search) models.TextField.register_lookup(Search) Custom manager classes available through ``prefetch_related`` must define a ``_apply_rel_filters()`` method ----------------------------------------------------------------------------------------------------------- If you defined a custom manager class available through :meth:`~django.db.models.query.QuerySet.prefetch_related` you must make sure it defines a ``_apply_rel_filters()`` method. This method must accept a :class:`~django.db.models.query.QuerySet` instance as its single argument and return a filtered version of the queryset for the model instance the manager is bound to. Miscellaneous ------------- Loading