models.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. from collections import defaultdict
  2. from django.apps import apps
  3. from django.db import models
  4. from django.utils.translation import gettext_lazy as _
  5. class ContentTypeManager(models.Manager):
  6. use_in_migrations = True
  7. def __init__(self, *args, **kwargs):
  8. super().__init__(*args, **kwargs)
  9. # Cache shared by all the get_for_* methods to speed up
  10. # ContentType retrieval.
  11. self._cache = {}
  12. def get_by_natural_key(self, app_label, model):
  13. try:
  14. ct = self._cache[self.db][(app_label, model)]
  15. except KeyError:
  16. ct = self.get(app_label=app_label, model=model)
  17. self._add_to_cache(self.db, ct)
  18. return ct
  19. def _get_opts(self, model, for_concrete_model):
  20. if for_concrete_model:
  21. model = model._meta.concrete_model
  22. return model._meta
  23. def _get_from_cache(self, opts):
  24. key = (opts.app_label, opts.model_name)
  25. return self._cache[self.db][key]
  26. def get_for_model(self, model, for_concrete_model=True):
  27. """
  28. Return the ContentType object for a given model, creating the
  29. ContentType if necessary. Lookups are cached so that subsequent lookups
  30. for the same model don't hit the database.
  31. """
  32. opts = self._get_opts(model, for_concrete_model)
  33. try:
  34. return self._get_from_cache(opts)
  35. except KeyError:
  36. pass
  37. # The ContentType entry was not found in the cache, therefore we
  38. # proceed to load or create it.
  39. try:
  40. # Start with get() and not get_or_create() in order to use
  41. # the db_for_read (see #20401).
  42. ct = self.get(app_label=opts.app_label, model=opts.model_name)
  43. except self.model.DoesNotExist:
  44. # Not found in the database; we proceed to create it. This time
  45. # use get_or_create to take care of any race conditions.
  46. ct, created = self.get_or_create(
  47. app_label=opts.app_label,
  48. model=opts.model_name,
  49. )
  50. self._add_to_cache(self.db, ct)
  51. return ct
  52. def get_for_models(self, *models, for_concrete_models=True):
  53. """
  54. Given *models, return a dictionary mapping {model: content_type}.
  55. """
  56. results = {}
  57. # Models that aren't already in the cache.
  58. needed_app_labels = set()
  59. needed_models = set()
  60. # Mapping of opts to the list of models requiring it.
  61. needed_opts = defaultdict(list)
  62. for model in models:
  63. opts = self._get_opts(model, for_concrete_models)
  64. try:
  65. ct = self._get_from_cache(opts)
  66. except KeyError:
  67. needed_app_labels.add(opts.app_label)
  68. needed_models.add(opts.model_name)
  69. needed_opts[opts].append(model)
  70. else:
  71. results[model] = ct
  72. if needed_opts:
  73. # Lookup required content types from the DB.
  74. cts = self.filter(app_label__in=needed_app_labels, model__in=needed_models)
  75. for ct in cts:
  76. opts_models = needed_opts.pop(ct.model_class()._meta, [])
  77. for model in opts_models:
  78. results[model] = ct
  79. self._add_to_cache(self.db, ct)
  80. # Create content types that weren't in the cache or DB.
  81. for opts, opts_models in needed_opts.items():
  82. ct = self.create(
  83. app_label=opts.app_label,
  84. model=opts.model_name,
  85. )
  86. self._add_to_cache(self.db, ct)
  87. for model in opts_models:
  88. results[model] = ct
  89. return results
  90. def get_for_id(self, id):
  91. """
  92. Lookup a ContentType by ID. Use the same shared cache as get_for_model
  93. (though ContentTypes are not created on-the-fly by get_by_id).
  94. """
  95. try:
  96. ct = self._cache[self.db][id]
  97. except KeyError:
  98. # This could raise a DoesNotExist; that's correct behavior and will
  99. # make sure that only correct ctypes get stored in the cache dict.
  100. ct = self.get(pk=id)
  101. self._add_to_cache(self.db, ct)
  102. return ct
  103. def clear_cache(self):
  104. """
  105. Clear out the content-type cache.
  106. """
  107. self._cache.clear()
  108. def _add_to_cache(self, using, ct):
  109. """Insert a ContentType into the cache."""
  110. # Note it's possible for ContentType objects to be stale; model_class()
  111. # will return None. Hence, there is no reliance on
  112. # model._meta.app_label here, just using the model fields instead.
  113. key = (ct.app_label, ct.model)
  114. self._cache.setdefault(using, {})[key] = ct
  115. self._cache.setdefault(using, {})[ct.id] = ct
  116. class ContentType(models.Model):
  117. app_label = models.CharField(max_length=100)
  118. model = models.CharField(_("python model class name"), max_length=100)
  119. objects = ContentTypeManager()
  120. class Meta:
  121. verbose_name = _("content type")
  122. verbose_name_plural = _("content types")
  123. db_table = "django_content_type"
  124. unique_together = [["app_label", "model"]]
  125. def __str__(self):
  126. return self.app_labeled_name
  127. @property
  128. def name(self):
  129. model = self.model_class()
  130. if not model:
  131. return self.model
  132. return str(model._meta.verbose_name)
  133. @property
  134. def app_labeled_name(self):
  135. model = self.model_class()
  136. if not model:
  137. return self.model
  138. return "%s | %s" % (model._meta.app_label, model._meta.verbose_name)
  139. def model_class(self):
  140. """Return the model class for this type of content."""
  141. try:
  142. return apps.get_model(self.app_label, self.model)
  143. except LookupError:
  144. return None
  145. def get_object_for_this_type(self, **kwargs):
  146. """
  147. Return an object of this type for the keyword arguments given.
  148. Basically, this is a proxy around this object_type's get_object() model
  149. method. The ObjectNotExist exception, if thrown, will not be caught,
  150. so code that calls this method should catch it.
  151. """
  152. return self.model_class()._base_manager.using(self._state.db).get(**kwargs)
  153. def get_all_objects_for_this_type(self, **kwargs):
  154. """
  155. Return all objects of this type for the keyword arguments given.
  156. """
  157. return self.model_class()._base_manager.using(self._state.db).filter(**kwargs)
  158. def natural_key(self):
  159. return (self.app_label, self.model)