Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: django/django
...
head fork: loic/django
compare: ticket20625_optout
Checking mergeability… Don't worry, you can still create the pull request.
  • 2 commits
  • 3 files changed
  • 0 commit comments
  • 1 contributor
View
2  django/contrib/contenttypes/generic.py
@@ -368,7 +368,7 @@ def create(self, **kwargs):
kwargs[self.content_type_field_name] = self.content_type
kwargs[self.object_id_field_name] = self.pk_val
db = router.db_for_write(self.model, instance=self.instance)
- return super(GenericRelatedObjectManager, self).using(db).create(**kwargs)
+ return self.get_queryset().using(db).create(**kwargs)
create.alters_data = True
return GenericRelatedObjectManager
View
115 django/db/models/manager.py
@@ -59,13 +59,19 @@ class Manager(six.with_metaclass(RenameManagerMethods)):
# Tracks each time a Manager instance is created. Used to retain order.
creation_counter = 0
- def __init__(self):
+ def __init__(self, queryset_class=QuerySet):
super(Manager, self).__init__()
self._set_creation_counter()
self.model = None
self._inherited = False
self._db = None
+ self.queryset_class = type(
+ queryset_class.__name__,
+ (queryset_class,),
+ {'base_queryset_class': queryset_class},
+ )
+
def contribute_to_class(self, model, name):
# TODO: Use weakref because of possible memory leak / circular reference.
self.model = model
@@ -124,119 +130,32 @@ def get_queryset(self):
"""Returns a new QuerySet object. Subclasses can override this method
to easily customize the behavior of the Manager.
"""
- return QuerySet(self.model, using=self._db)
-
- def none(self):
- return self.get_queryset().none()
+ return self.queryset_class(self.model, using=self._db)
+ # If we proxy to `Queryset.all()` we bust the cache used by `prefetch_related`.
def all(self):
return self.get_queryset()
- def count(self):
- return self.get_queryset().count()
-
- def dates(self, *args, **kwargs):
- return self.get_queryset().dates(*args, **kwargs)
-
- def datetimes(self, *args, **kwargs):
- return self.get_queryset().datetimes(*args, **kwargs)
-
- def distinct(self, *args, **kwargs):
- return self.get_queryset().distinct(*args, **kwargs)
-
- def extra(self, *args, **kwargs):
- return self.get_queryset().extra(*args, **kwargs)
-
- def get(self, *args, **kwargs):
- return self.get_queryset().get(*args, **kwargs)
-
def get_or_create(self, **kwargs):
return self.get_queryset().get_or_create(**kwargs)
def create(self, **kwargs):
return self.get_queryset().create(**kwargs)
- def bulk_create(self, *args, **kwargs):
- return self.get_queryset().bulk_create(*args, **kwargs)
-
- def filter(self, *args, **kwargs):
- return self.get_queryset().filter(*args, **kwargs)
-
- def aggregate(self, *args, **kwargs):
- return self.get_queryset().aggregate(*args, **kwargs)
-
- def annotate(self, *args, **kwargs):
- return self.get_queryset().annotate(*args, **kwargs)
-
- def complex_filter(self, *args, **kwargs):
- return self.get_queryset().complex_filter(*args, **kwargs)
-
- def exclude(self, *args, **kwargs):
- return self.get_queryset().exclude(*args, **kwargs)
-
- def in_bulk(self, *args, **kwargs):
- return self.get_queryset().in_bulk(*args, **kwargs)
-
- def iterator(self, *args, **kwargs):
- return self.get_queryset().iterator(*args, **kwargs)
-
- def earliest(self, *args, **kwargs):
- return self.get_queryset().earliest(*args, **kwargs)
-
- def latest(self, *args, **kwargs):
- return self.get_queryset().latest(*args, **kwargs)
-
- def first(self):
- return self.get_queryset().first()
-
- def last(self):
- return self.get_queryset().last()
-
- def order_by(self, *args, **kwargs):
- return self.get_queryset().order_by(*args, **kwargs)
-
- def select_for_update(self, *args, **kwargs):
- return self.get_queryset().select_for_update(*args, **kwargs)
-
- def select_related(self, *args, **kwargs):
- return self.get_queryset().select_related(*args, **kwargs)
-
- def prefetch_related(self, *args, **kwargs):
- return self.get_queryset().prefetch_related(*args, **kwargs)
-
- def values(self, *args, **kwargs):
- return self.get_queryset().values(*args, **kwargs)
-
- def values_list(self, *args, **kwargs):
- return self.get_queryset().values_list(*args, **kwargs)
-
- def update(self, *args, **kwargs):
- return self.get_queryset().update(*args, **kwargs)
-
- def reverse(self, *args, **kwargs):
- return self.get_queryset().reverse(*args, **kwargs)
-
- def defer(self, *args, **kwargs):
- return self.get_queryset().defer(*args, **kwargs)
-
- def only(self, *args, **kwargs):
- return self.get_queryset().only(*args, **kwargs)
-
- def using(self, *args, **kwargs):
- return self.get_queryset().using(*args, **kwargs)
-
- def exists(self, *args, **kwargs):
- return self.get_queryset().exists(*args, **kwargs)
-
def _insert(self, objs, fields, **kwargs):
return insert_query(self.model, objs, fields, **kwargs)
- def _update(self, values, **kwargs):
- return self.get_queryset()._update(values, **kwargs)
-
def raw(self, raw_query, params=None, *args, **kwargs):
return RawQuerySet(raw_query=raw_query, model=self.model, params=params, using=self._db, *args, **kwargs)
+ def __getattr__(self, name):
+ queryset_class = object.__getattribute__(self, 'queryset_class')
+ queryset_attr = getattr(queryset_class, name, None)
+ if callable(queryset_attr):
+ do_proxy = getattr(queryset_attr, 'manager', None)
+ if do_proxy is True or do_proxy is None and not name.startswith('_'):
+ return getattr(self.get_queryset(), name)
+ raise AttributeError
class ManagerDescriptor(object):
# This class ensures managers aren't accessible via model instances.
View
29 django/db/models/query.py
@@ -11,7 +11,7 @@
from django.core import exceptions
from django.db import connections, router, transaction, DatabaseError
from django.db.models.constants import LOOKUP_SEP
-from django.db.models.fields import AutoField
+from django.db.models.fields import AutoField, Empty
from django.db.models.query_utils import (Q, select_related_descend,
deferred_class_factory, InvalidQuery)
from django.db.models.deletion import Collector
@@ -27,6 +27,12 @@
EmptyResultSet = sql.EmptyResultSet
+def _pickle_queryset(klass_bases, klass_dict):
+ new = Empty()
+ new.__class__ = type(klass_bases[0].__name__, klass_bases, klass_dict)
+ return new
+
+
class QuerySet(object):
"""
Represents a lazy database lookup for a set of objects.
@@ -58,6 +64,16 @@ def __deepcopy__(self, memo):
obj.__dict__[k] = copy.deepcopy(v, memo)
return obj
+ def __reduce__(self):
+ if hasattr(self, 'base_queryset_class'):
+ klass_bases = [self.base_queryset_class]
+ klass_dict = {'base_queryset_class': self.base_queryset_class}
+ if hasattr(self, 'current_queryset_class'):
+ klass_bases.insert(0, self.current_queryset_class)
+ klass_dict['current_queryset_class'] = self.current_queryset_class
+ return _pickle_queryset, (tuple(klass_bases), klass_dict), self.__getstate__()
+ return super(QuerySet, self).__reduce__()
+
def __getstate__(self):
"""
Allows the QuerySet to be pickled.
@@ -469,6 +485,7 @@ def delete(self):
# Clear the result cache, in case this QuerySet gets reused.
self._result_cache = None
delete.alters_data = True
+ delete.manager = False
def _raw_delete(self, using):
"""
@@ -508,6 +525,7 @@ def _update(self, values):
self._result_cache = None
return query.get_compiler(self.db).execute_sql(None)
_update.alters_data = True
+ _update.manager = True
def exists(self):
if self._result_cache is None:
@@ -838,6 +856,15 @@ def _batched_insert(self, objs, fields, batch_size):
def _clone(self, klass=None, setup=False, **kwargs):
if klass is None:
klass = self.__class__
+ elif not issubclass(self.__class__, klass):
+ if hasattr(self, 'base_queryset_class'):
+ klass_bases = (klass, self.base_queryset_class)
+ klass_dict = {
+ 'base_queryset_class': self.base_queryset_class,
+ 'current_queryset_class': klass,
+ }
+ klass = type(klass.__name__, klass_bases, klass_dict)
+
query = self.query.clone()
if self._sticky_filter:
query.filter_is_sticky = True

No commit comments for this range

Something went wrong with that request. Please try again.