Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manager.py 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import copy
  2. import inspect
  3. from importlib import import_module
  4. from django.db import router
  5. from django.db.models.query import QuerySet
  6. class BaseManager:
  7. # To retain order, track each time a Manager instance is created.
  8. creation_counter = 0
  9. # Set to True for the 'objects' managers that are automatically created.
  10. auto_created = False
  11. #: If set to True the manager will be serialized into migrations and will
  12. #: thus be available in e.g. RunPython operations.
  13. use_in_migrations = False
  14. def __new__(cls, *args, **kwargs):
  15. # Capture the arguments to make returning them trivial.
  16. obj = super().__new__(cls)
  17. obj._constructor_args = (args, kwargs)
  18. return obj
  19. def __init__(self):
  20. super().__init__()
  21. self._set_creation_counter()
  22. self.model = None
  23. self.name = None
  24. self._db = None
  25. self._hints = {}
  26. def __str__(self):
  27. """Return "app_label.model_label.manager_name"."""
  28. return "%s.%s" % (self.model._meta.label, self.name)
  29. def __class_getitem__(cls, *args, **kwargs):
  30. return cls
  31. def deconstruct(self):
  32. """
  33. Return a 5-tuple of the form (as_manager (True), manager_class,
  34. queryset_class, args, kwargs).
  35. Raise a ValueError if the manager is dynamically generated.
  36. """
  37. qs_class = self._queryset_class
  38. if getattr(self, "_built_with_as_manager", False):
  39. # using MyQuerySet.as_manager()
  40. return (
  41. True, # as_manager
  42. None, # manager_class
  43. "%s.%s" % (qs_class.__module__, qs_class.__name__), # qs_class
  44. None, # args
  45. None, # kwargs
  46. )
  47. else:
  48. module_name = self.__module__
  49. name = self.__class__.__name__
  50. # Make sure it's actually there and not an inner class
  51. module = import_module(module_name)
  52. if not hasattr(module, name):
  53. raise ValueError(
  54. "Could not find manager %s in %s.\n"
  55. "Please note that you need to inherit from managers you "
  56. "dynamically generated with 'from_queryset()'."
  57. % (name, module_name)
  58. )
  59. return (
  60. False, # as_manager
  61. "%s.%s" % (module_name, name), # manager_class
  62. None, # qs_class
  63. self._constructor_args[0], # args
  64. self._constructor_args[1], # kwargs
  65. )
  66. def check(self, **kwargs):
  67. return []
  68. @classmethod
  69. def _get_queryset_methods(cls, queryset_class):
  70. def create_method(name, method):
  71. def manager_method(self, *args, **kwargs):
  72. return getattr(self.get_queryset(), name)(*args, **kwargs)
  73. manager_method.__name__ = method.__name__
  74. manager_method.__doc__ = method.__doc__
  75. return manager_method
  76. new_methods = {}
  77. for name, method in inspect.getmembers(
  78. queryset_class, predicate=inspect.isfunction
  79. ):
  80. # Only copy missing methods.
  81. if hasattr(cls, name):
  82. continue
  83. # Only copy public methods or methods with the attribute
  84. # queryset_only=False.
  85. queryset_only = getattr(method, "queryset_only", None)
  86. if queryset_only or (queryset_only is None and name.startswith("_")):
  87. continue
  88. # Copy the method onto the manager.
  89. new_methods[name] = create_method(name, method)
  90. return new_methods
  91. @classmethod
  92. def from_queryset(cls, queryset_class, class_name=None):
  93. if class_name is None:
  94. class_name = "%sFrom%s" % (cls.__name__, queryset_class.__name__)
  95. return type(
  96. class_name,
  97. (cls,),
  98. {
  99. "_queryset_class": queryset_class,
  100. **cls._get_queryset_methods(queryset_class),
  101. },
  102. )
  103. def contribute_to_class(self, cls, name):
  104. self.name = self.name or name
  105. self.model = cls
  106. setattr(cls, name, ManagerDescriptor(self))
  107. cls._meta.add_manager(self)
  108. def _set_creation_counter(self):
  109. """
  110. Set the creation counter value for this instance and increment the
  111. class-level copy.
  112. """
  113. self.creation_counter = BaseManager.creation_counter
  114. BaseManager.creation_counter += 1
  115. def db_manager(self, using=None, hints=None):
  116. obj = copy.copy(self)
  117. obj._db = using or self._db
  118. obj._hints = hints or self._hints
  119. return obj
  120. @property
  121. def db(self):
  122. return self._db or router.db_for_read(self.model, **self._hints)
  123. #######################
  124. # PROXIES TO QUERYSET #
  125. #######################
  126. def get_queryset(self):
  127. """
  128. Return a new QuerySet object. Subclasses can override this method to
  129. customize the behavior of the Manager.
  130. """
  131. return self._queryset_class(model=self.model, using=self._db, hints=self._hints)
  132. def all(self):
  133. # We can't proxy this method through the `QuerySet` like we do for the
  134. # rest of the `QuerySet` methods. This is because `QuerySet.all()`
  135. # works by creating a "copy" of the current queryset and in making said
  136. # copy, all the cached `prefetch_related` lookups are lost. See the
  137. # implementation of `RelatedManager.get_queryset()` for a better
  138. # understanding of how this comes into play.
  139. return self.get_queryset()
  140. def __eq__(self, other):
  141. return (
  142. isinstance(other, self.__class__)
  143. and self._constructor_args == other._constructor_args
  144. )
  145. def __hash__(self):
  146. return id(self)
  147. class Manager(BaseManager.from_queryset(QuerySet)):
  148. pass
  149. class ManagerDescriptor:
  150. def __init__(self, manager):
  151. self.manager = manager
  152. def __get__(self, instance, cls=None):
  153. if instance is not None:
  154. raise AttributeError(
  155. "Manager isn't accessible via %s instances" % cls.__name__
  156. )
  157. if cls._meta.abstract:
  158. raise AttributeError(
  159. "Manager isn't available; %s is abstract" % (cls._meta.object_name,)
  160. )
  161. if cls._meta.swapped:
  162. raise AttributeError(
  163. "Manager isn't available; '%s' has been swapped for '%s'"
  164. % (
  165. cls._meta.label,
  166. cls._meta.swapped,
  167. )
  168. )
  169. return cls._meta.managers_map[self.manager.name]
  170. class EmptyManager(Manager):
  171. def __init__(self, model):
  172. super().__init__()
  173. self.model = model
  174. def get_queryset(self):
  175. return super().get_queryset().none()