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