manager.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import copy
  2. import inspect
  3. from django.db import router
  4. from django.db.models.query import QuerySet
  5. from django.db.models import signals
  6. from django.db.models.fields import FieldDoesNotExist
  7. from django.utils import six
  8. from django.utils.deprecation import RenameMethodsBase, RemovedInDjango18Warning
  9. from django.utils.encoding import python_2_unicode_compatible
  10. def ensure_default_manager(sender, **kwargs):
  11. """
  12. Ensures that a Model subclass contains a default manager and sets the
  13. _default_manager attribute on the class. Also sets up the _base_manager
  14. points to a plain Manager instance (which could be the same as
  15. _default_manager if it's not a subclass of Manager).
  16. """
  17. cls = sender
  18. if cls._meta.abstract:
  19. setattr(cls, 'objects', AbstractManagerDescriptor(cls))
  20. return
  21. elif cls._meta.swapped:
  22. setattr(cls, 'objects', SwappedManagerDescriptor(cls))
  23. return
  24. if not getattr(cls, '_default_manager', None):
  25. # Create the default manager, if needed.
  26. try:
  27. cls._meta.get_field('objects')
  28. raise ValueError("Model %s must specify a custom Manager, because it has a field named 'objects'" % cls.__name__)
  29. except FieldDoesNotExist:
  30. pass
  31. cls.add_to_class('objects', Manager())
  32. cls._base_manager = cls.objects
  33. elif not getattr(cls, '_base_manager', None):
  34. default_mgr = cls._default_manager.__class__
  35. if (default_mgr is Manager or
  36. getattr(default_mgr, "use_for_related_fields", False)):
  37. cls._base_manager = cls._default_manager
  38. else:
  39. # Default manager isn't a plain Manager class, or a suitable
  40. # replacement, so we walk up the base class hierarchy until we hit
  41. # something appropriate.
  42. for base_class in default_mgr.mro()[1:]:
  43. if (base_class is Manager or
  44. getattr(base_class, "use_for_related_fields", False)):
  45. cls.add_to_class('_base_manager', base_class())
  46. return
  47. raise AssertionError("Should never get here. Please report a bug, including your model and model manager setup.")
  48. signals.class_prepared.connect(ensure_default_manager)
  49. class RenameManagerMethods(RenameMethodsBase):
  50. renamed_methods = (
  51. ('get_query_set', 'get_queryset', RemovedInDjango18Warning),
  52. ('get_prefetch_query_set', 'get_prefetch_queryset', RemovedInDjango18Warning),
  53. )
  54. @python_2_unicode_compatible
  55. class BaseManager(six.with_metaclass(RenameManagerMethods)):
  56. # Tracks each time a Manager instance is created. Used to retain order.
  57. creation_counter = 0
  58. def __init__(self):
  59. super(BaseManager, self).__init__()
  60. self._set_creation_counter()
  61. self.model = None
  62. self._inherited = False
  63. self._db = None
  64. self._hints = {}
  65. def __str__(self):
  66. """ Return "app_label.model_label.manager_name". """
  67. model = self.model
  68. opts = model._meta
  69. app = model._meta.app_label
  70. manager_name = next(name for (_, name, manager)
  71. in opts.concrete_managers + opts.abstract_managers
  72. if manager == self)
  73. return '%s.%s.%s' % (app, model._meta.object_name, manager_name)
  74. def check(self, **kwargs):
  75. return []
  76. @classmethod
  77. def _get_queryset_methods(cls, queryset_class):
  78. def create_method(name, method):
  79. def manager_method(self, *args, **kwargs):
  80. return getattr(self.get_queryset(), name)(*args, **kwargs)
  81. manager_method.__name__ = method.__name__
  82. manager_method.__doc__ = method.__doc__
  83. return manager_method
  84. new_methods = {}
  85. # Refs http://bugs.python.org/issue1785.
  86. predicate = inspect.isfunction if six.PY3 else inspect.ismethod
  87. for name, method in inspect.getmembers(queryset_class, predicate=predicate):
  88. # Only copy missing methods.
  89. if hasattr(cls, name):
  90. continue
  91. # Only copy public methods or methods with the attribute `queryset_only=False`.
  92. queryset_only = getattr(method, 'queryset_only', None)
  93. if queryset_only or (queryset_only is None and name.startswith('_')):
  94. continue
  95. # Copy the method onto the manager.
  96. new_methods[name] = create_method(name, method)
  97. return new_methods
  98. @classmethod
  99. def from_queryset(cls, queryset_class, class_name=None):
  100. if class_name is None:
  101. class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
  102. class_dict = {
  103. '_queryset_class': queryset_class,
  104. }
  105. class_dict.update(cls._get_queryset_methods(queryset_class))
  106. return type(class_name, (cls,), class_dict)
  107. def contribute_to_class(self, model, name):
  108. # TODO: Use weakref because of possible memory leak / circular reference.
  109. self.model = model
  110. # Only contribute the manager if the model is concrete
  111. if model._meta.abstract:
  112. setattr(model, name, AbstractManagerDescriptor(model))
  113. elif model._meta.swapped:
  114. setattr(model, name, SwappedManagerDescriptor(model))
  115. else:
  116. # if not model._meta.abstract and not model._meta.swapped:
  117. setattr(model, name, ManagerDescriptor(self))
  118. if not getattr(model, '_default_manager', None) or self.creation_counter < model._default_manager.creation_counter:
  119. model._default_manager = self
  120. if model._meta.abstract or (self._inherited and not self.model._meta.proxy):
  121. model._meta.abstract_managers.append((self.creation_counter, name,
  122. self))
  123. else:
  124. model._meta.concrete_managers.append((self.creation_counter, name,
  125. self))
  126. def _set_creation_counter(self):
  127. """
  128. Sets the creation counter value for this instance and increments the
  129. class-level copy.
  130. """
  131. self.creation_counter = BaseManager.creation_counter
  132. BaseManager.creation_counter += 1
  133. def _copy_to_model(self, model):
  134. """
  135. Makes a copy of the manager and assigns it to 'model', which should be
  136. a child of the existing model (used when inheriting a manager from an
  137. abstract base class).
  138. """
  139. assert issubclass(model, self.model)
  140. mgr = copy.copy(self)
  141. mgr._set_creation_counter()
  142. mgr.model = model
  143. mgr._inherited = True
  144. return mgr
  145. def db_manager(self, using=None, hints=None):
  146. obj = copy.copy(self)
  147. obj._db = using or self._db
  148. obj._hints = hints or self._hints
  149. return obj
  150. @property
  151. def db(self):
  152. return self._db or router.db_for_read(self.model, **self._hints)
  153. #######################
  154. # PROXIES TO QUERYSET #
  155. #######################
  156. def get_queryset(self):
  157. """
  158. Returns a new QuerySet object. Subclasses can override this method to
  159. easily customize the behavior of the Manager.
  160. """
  161. return self._queryset_class(self.model, using=self._db, hints=self._hints)
  162. def all(self):
  163. # We can't proxy this method through the `QuerySet` like we do for the
  164. # rest of the `QuerySet` methods. This is because `QuerySet.all()`
  165. # works by creating a "copy" of the current queryset and in making said
  166. # copy, all the cached `prefetch_related` lookups are lost. See the
  167. # implementation of `RelatedManager.get_queryset()` for a better
  168. # understanding of how this comes into play.
  169. return self.get_queryset()
  170. class Manager(BaseManager.from_queryset(QuerySet)):
  171. pass
  172. class ManagerDescriptor(object):
  173. # This class ensures managers aren't accessible via model instances.
  174. # For example, Poll.objects works, but poll_obj.objects raises AttributeError.
  175. def __init__(self, manager):
  176. self.manager = manager
  177. def __get__(self, instance, type=None):
  178. if instance is not None:
  179. raise AttributeError("Manager isn't accessible via %s instances" % type.__name__)
  180. return self.manager
  181. class AbstractManagerDescriptor(object):
  182. # This class provides a better error message when you try to access a
  183. # manager on an abstract model.
  184. def __init__(self, model):
  185. self.model = model
  186. def __get__(self, instance, type=None):
  187. raise AttributeError("Manager isn't available; %s is abstract" % (
  188. self.model._meta.object_name,
  189. ))
  190. class SwappedManagerDescriptor(object):
  191. # This class provides a better error message when you try to access a
  192. # manager on a swapped model.
  193. def __init__(self, model):
  194. self.model = model
  195. def __get__(self, instance, type=None):
  196. raise AttributeError("Manager isn't available; %s has been swapped for '%s'" % (
  197. self.model._meta.object_name, self.model._meta.swapped
  198. ))
  199. class EmptyManager(Manager):
  200. def __init__(self, model):
  201. super(EmptyManager, self).__init__()
  202. self.model = model
  203. def get_queryset(self):
  204. return super(EmptyManager, self).get_queryset().none()