2013-04-06 22:55:42 +00:00
|
|
|
from __future__ import unicode_literals
|
2013-02-02 05:38:14 +00:00
|
|
|
import django
|
|
|
|
|
from django.db import models
|
2011-03-29 12:59:50 +00:00
|
|
|
from django.db.models.fields.related import OneToOneField
|
2010-07-31 02:09:46 +00:00
|
|
|
from django.db.models.query import QuerySet
|
2012-11-05 12:43:42 +00:00
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
2009-07-30 21:11:36 +00:00
|
|
|
|
2013-01-31 14:27:16 +00:00
|
|
|
try:
|
|
|
|
|
from django.db.models.constants import LOOKUP_SEP
|
2013-02-02 05:38:14 +00:00
|
|
|
except ImportError: # Django < 1.5
|
2013-01-31 14:27:16 +00:00
|
|
|
from django.db.models.sql.constants import LOOKUP_SEP
|
2012-01-03 22:47:41 +00:00
|
|
|
|
2013-02-02 05:38:14 +00:00
|
|
|
|
|
|
|
|
|
2010-11-23 17:48:23 +00:00
|
|
|
class InheritanceQuerySet(QuerySet):
|
|
|
|
|
def select_subclasses(self, *subclasses):
|
|
|
|
|
if not subclasses:
|
2013-02-02 05:38:14 +00:00
|
|
|
# only recurse one level on Django < 1.6 to avoid triggering
|
|
|
|
|
# https://code.djangoproject.com/ticket/16572
|
|
|
|
|
levels = None
|
|
|
|
|
if django.VERSION < (1, 6, 0):
|
|
|
|
|
levels = 1
|
|
|
|
|
subclasses = self._get_subclasses_recurse(self.model, levels=levels)
|
2013-02-02 05:53:11 +00:00
|
|
|
# workaround https://code.djangoproject.com/ticket/16855
|
|
|
|
|
field_dict = self.query.select_related
|
2010-11-23 17:48:23 +00:00
|
|
|
new_qs = self.select_related(*subclasses)
|
2013-02-02 05:53:11 +00:00
|
|
|
if isinstance(new_qs.query.select_related, dict) and isinstance(field_dict, dict):
|
|
|
|
|
new_qs.query.select_related.update(field_dict)
|
2010-11-23 17:48:23 +00:00
|
|
|
new_qs.subclasses = subclasses
|
|
|
|
|
return new_qs
|
|
|
|
|
|
2013-02-02 05:38:14 +00:00
|
|
|
|
2010-11-23 17:48:23 +00:00
|
|
|
def _clone(self, klass=None, setup=False, **kwargs):
|
2012-01-03 23:47:33 +00:00
|
|
|
for name in ['subclasses', '_annotated']:
|
|
|
|
|
if hasattr(self, name):
|
|
|
|
|
kwargs[name] = getattr(self, name)
|
2010-11-23 17:48:23 +00:00
|
|
|
return super(InheritanceQuerySet, self)._clone(klass, setup, **kwargs)
|
|
|
|
|
|
2013-02-02 05:38:14 +00:00
|
|
|
|
2012-01-03 22:47:41 +00:00
|
|
|
def annotate(self, *args, **kwargs):
|
|
|
|
|
qset = super(InheritanceQuerySet, self).annotate(*args, **kwargs)
|
2013-04-06 22:55:22 +00:00
|
|
|
qset._annotated = [a.default_alias for a in args] + list(kwargs.keys())
|
2012-01-03 22:47:41 +00:00
|
|
|
return qset
|
|
|
|
|
|
2013-02-02 05:38:14 +00:00
|
|
|
|
2010-11-23 17:48:23 +00:00
|
|
|
def iterator(self):
|
|
|
|
|
iter = super(InheritanceQuerySet, self).iterator()
|
|
|
|
|
if getattr(self, 'subclasses', False):
|
2013-09-20 16:04:10 +00:00
|
|
|
# sort the subclass names longest first,
|
|
|
|
|
# so with 'a' and 'a__b' it goes as deep as possible
|
|
|
|
|
subclasses = sorted(self.subclasses, key=len, reverse=True)
|
2010-11-23 17:48:23 +00:00
|
|
|
for obj in iter:
|
2013-01-31 14:27:16 +00:00
|
|
|
sub_obj = None
|
2013-09-20 16:04:10 +00:00
|
|
|
for s in subclasses:
|
2013-01-31 14:27:16 +00:00
|
|
|
sub_obj = self._get_sub_obj_recurse(obj, s)
|
2013-01-27 00:46:51 +00:00
|
|
|
if sub_obj:
|
|
|
|
|
break
|
|
|
|
|
if not sub_obj:
|
|
|
|
|
sub_obj = obj
|
2013-01-31 14:27:16 +00:00
|
|
|
|
2012-01-03 23:47:33 +00:00
|
|
|
if getattr(self, '_annotated', False):
|
2012-01-03 22:47:41 +00:00
|
|
|
for k in self._annotated:
|
|
|
|
|
setattr(sub_obj, k, getattr(obj, k))
|
|
|
|
|
|
|
|
|
|
yield sub_obj
|
2010-11-23 17:48:23 +00:00
|
|
|
else:
|
|
|
|
|
for obj in iter:
|
|
|
|
|
yield obj
|
|
|
|
|
|
2013-02-02 05:38:14 +00:00
|
|
|
|
|
|
|
|
def _get_subclasses_recurse(self, model, levels=None):
|
2013-01-31 14:27:16 +00:00
|
|
|
rels = [rel for rel in model._meta.get_all_related_objects()
|
|
|
|
|
if isinstance(rel.field, OneToOneField)
|
|
|
|
|
and issubclass(rel.field.model, model)]
|
|
|
|
|
subclasses = []
|
2013-02-02 05:38:14 +00:00
|
|
|
if levels:
|
|
|
|
|
levels -= 1
|
2013-01-31 14:27:16 +00:00
|
|
|
for rel in rels:
|
2013-02-02 05:38:14 +00:00
|
|
|
if levels or levels is None:
|
|
|
|
|
for subclass in self._get_subclasses_recurse(
|
|
|
|
|
rel.field.model, levels=levels):
|
|
|
|
|
subclasses.append(rel.var_name + LOOKUP_SEP + subclass)
|
2013-01-31 14:27:16 +00:00
|
|
|
subclasses.append(rel.var_name)
|
|
|
|
|
return subclasses
|
|
|
|
|
|
2013-02-02 05:38:14 +00:00
|
|
|
|
2013-01-31 14:27:16 +00:00
|
|
|
def _get_sub_obj_recurse(self, obj, s):
|
|
|
|
|
rel, _, s = s.partition(LOOKUP_SEP)
|
|
|
|
|
try:
|
|
|
|
|
node = getattr(obj, rel)
|
|
|
|
|
except ObjectDoesNotExist:
|
|
|
|
|
return None
|
|
|
|
|
if s:
|
|
|
|
|
child = self._get_sub_obj_recurse(node, s)
|
2013-05-21 20:23:23 +00:00
|
|
|
return child
|
2013-01-31 14:27:16 +00:00
|
|
|
else:
|
|
|
|
|
return node
|
|
|
|
|
|
2012-01-03 22:47:41 +00:00
|
|
|
|
2013-02-02 05:38:14 +00:00
|
|
|
|
2010-11-23 17:48:23 +00:00
|
|
|
class InheritanceManager(models.Manager):
|
2011-04-16 21:19:55 +00:00
|
|
|
use_for_related_fields = True
|
|
|
|
|
|
2010-11-23 17:48:23 +00:00
|
|
|
def get_query_set(self):
|
|
|
|
|
return InheritanceQuerySet(self.model)
|
|
|
|
|
|
|
|
|
|
def select_subclasses(self, *subclasses):
|
|
|
|
|
return self.get_query_set().select_subclasses(*subclasses)
|
|
|
|
|
|
2011-10-26 06:01:37 +00:00
|
|
|
def get_subclass(self, *args, **kwargs):
|
|
|
|
|
return self.get_query_set().select_subclasses().get(*args, **kwargs)
|
|
|
|
|
|
2010-10-01 18:43:22 +00:00
|
|
|
|
|
|
|
|
|
2009-07-30 21:11:36 +00:00
|
|
|
class QueryManager(models.Manager):
|
2013-01-27 01:09:16 +00:00
|
|
|
use_for_related_fields = True
|
|
|
|
|
|
2009-07-30 21:11:36 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
if args:
|
|
|
|
|
self._q = args[0]
|
|
|
|
|
else:
|
|
|
|
|
self._q = models.Q(**kwargs)
|
2013-01-27 21:41:03 +00:00
|
|
|
self._order_by = None
|
2009-07-30 21:11:36 +00:00
|
|
|
super(QueryManager, self).__init__()
|
|
|
|
|
|
|
|
|
|
def order_by(self, *args):
|
|
|
|
|
self._order_by = args
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def get_query_set(self):
|
|
|
|
|
qs = super(QueryManager, self).get_query_set().filter(self._q)
|
2013-01-27 21:41:03 +00:00
|
|
|
if self._order_by is not None:
|
2009-07-30 21:11:36 +00:00
|
|
|
return qs.order_by(*self._order_by)
|
|
|
|
|
return qs
|
2010-07-31 02:09:46 +00:00
|
|
|
|
|
|
|
|
|
2011-03-08 18:52:32 +00:00
|
|
|
class PassThroughManager(models.Manager):
|
2011-03-29 17:10:50 +00:00
|
|
|
"""
|
2011-03-08 18:52:32 +00:00
|
|
|
Inherit from this Manager to enable you to call any methods from your
|
|
|
|
|
custom QuerySet class from your manager. Simply define your QuerySet
|
|
|
|
|
class, and return an instance of it from your manager's `get_query_set`
|
|
|
|
|
method.
|
2011-03-29 17:10:50 +00:00
|
|
|
|
2011-03-08 18:52:32 +00:00
|
|
|
Alternately, if you don't need any extra methods on your manager that
|
2012-09-27 15:48:04 +00:00
|
|
|
aren't on your QuerySet, then just pass your QuerySet class to the
|
|
|
|
|
``for_queryset_class`` class method.
|
2011-03-29 17:10:50 +00:00
|
|
|
|
2011-03-08 18:52:32 +00:00
|
|
|
class PostQuerySet(QuerySet):
|
|
|
|
|
def enabled(self):
|
|
|
|
|
return self.filter(disabled=False)
|
2011-03-29 17:10:50 +00:00
|
|
|
|
2011-03-08 18:52:32 +00:00
|
|
|
class Post(models.Model):
|
2012-09-27 15:48:04 +00:00
|
|
|
objects = PassThroughManager.for_queryset_class(PostQuerySet)()
|
2011-03-29 17:10:50 +00:00
|
|
|
|
|
|
|
|
"""
|
2011-03-08 18:52:32 +00:00
|
|
|
# pickling causes recursion errors
|
2012-11-08 08:18:19 +00:00
|
|
|
_deny_methods = ['__getstate__', '__setstate__', '__getinitargs__',
|
|
|
|
|
'__getnewargs__', '__copy__', '__deepcopy__', '_db']
|
2011-03-29 17:10:50 +00:00
|
|
|
|
2011-03-08 18:52:32 +00:00
|
|
|
def __init__(self, queryset_cls=None):
|
|
|
|
|
self._queryset_cls = queryset_cls
|
2011-03-09 19:44:10 +00:00
|
|
|
super(PassThroughManager, self).__init__()
|
2011-03-29 17:10:50 +00:00
|
|
|
|
2011-03-08 18:52:32 +00:00
|
|
|
def __getattr__(self, name):
|
|
|
|
|
if name in self._deny_methods:
|
|
|
|
|
raise AttributeError(name)
|
|
|
|
|
return getattr(self.get_query_set(), name)
|
2011-03-29 17:10:50 +00:00
|
|
|
|
2011-03-08 18:52:32 +00:00
|
|
|
def get_query_set(self):
|
2013-05-24 19:24:37 +00:00
|
|
|
qs = super(PassThroughManager, self).get_query_set()
|
2011-03-08 18:52:32 +00:00
|
|
|
if self._queryset_cls is not None:
|
2013-05-24 19:24:37 +00:00
|
|
|
qs = qs._clone(klass=self._queryset_cls)
|
|
|
|
|
return qs
|
2011-03-08 18:52:32 +00:00
|
|
|
|
2011-12-04 13:57:16 +00:00
|
|
|
@classmethod
|
|
|
|
|
def for_queryset_class(cls, queryset_cls):
|
2013-01-27 06:10:14 +00:00
|
|
|
return create_pass_through_manager_for_queryset_class(cls, queryset_cls)
|
2011-12-06 00:08:51 +00:00
|
|
|
|
2011-12-04 13:57:16 +00:00
|
|
|
|
2013-01-27 06:10:14 +00:00
|
|
|
def create_pass_through_manager_for_queryset_class(base, queryset_cls):
|
|
|
|
|
class _PassThroughManager(base):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
return super(_PassThroughManager, self).__init__()
|
|
|
|
|
|
|
|
|
|
def get_query_set(self):
|
2013-05-24 19:24:37 +00:00
|
|
|
qs = super(_PassThroughManager, self).get_query_set()
|
|
|
|
|
return qs._clone(klass=queryset_cls)
|
2013-01-27 06:10:14 +00:00
|
|
|
|
|
|
|
|
def __reduce__(self):
|
2013-02-12 21:05:31 +00:00
|
|
|
# our pickling support breaks for subclasses (e.g. RelatedManager)
|
|
|
|
|
if self.__class__ is not _PassThroughManager:
|
|
|
|
|
return super(_PassThroughManager, self).__reduce__()
|
2013-01-27 06:10:14 +00:00
|
|
|
return (
|
|
|
|
|
unpickle_pass_through_manager_for_queryset_class,
|
|
|
|
|
(base, queryset_cls),
|
|
|
|
|
self.__dict__,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return _PassThroughManager
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def unpickle_pass_through_manager_for_queryset_class(base, queryset_cls):
|
|
|
|
|
cls = create_pass_through_manager_for_queryset_class(base, queryset_cls)
|
|
|
|
|
return cls.__new__(cls)
|