Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/db/models/options.py: 86%
464 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
1import bisect
2import copy
3import inspect
4from collections import defaultdict
6from django.apps import apps
7from django.conf import settings
8from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
9from django.db import connections
10from django.db.models import AutoField, Manager, OrderWrt, UniqueConstraint
11from django.db.models.query_utils import PathInfo
12from django.utils.datastructures import ImmutableList, OrderedSet
13from django.utils.functional import cached_property
14from django.utils.module_loading import import_string
15from django.utils.text import camel_case_to_spaces, format_lazy
16from django.utils.translation import override
18PROXY_PARENTS = object()
20EMPTY_RELATION_TREE = ()
22IMMUTABLE_WARNING = (
23 "The return type of '%s' should never be mutated. If you want to manipulate this "
24 "list for your own use, make a copy first."
25)
27DEFAULT_NAMES = (
28 "verbose_name",
29 "verbose_name_plural",
30 "db_table",
31 "ordering",
32 "unique_together",
33 "permissions",
34 "get_latest_by",
35 "order_with_respect_to",
36 "app_label",
37 "db_tablespace",
38 "abstract",
39 "managed",
40 "proxy",
41 "swappable",
42 "auto_created",
43 "index_together",
44 "apps",
45 "default_permissions",
46 "select_on_save",
47 "default_related_name",
48 "required_db_features",
49 "required_db_vendor",
50 "base_manager_name",
51 "default_manager_name",
52 "indexes",
53 "constraints",
54)
57def normalize_together(option_together):
58 """
59 option_together can be either a tuple of tuples, or a single
60 tuple of two strings. Normalize it to a tuple of tuples, so that
61 calling code can uniformly expect that.
62 """
63 try:
64 if not option_together:
65 return ()
66 if not isinstance(option_together, (tuple, list)):
67 raise TypeError
68 first_element = option_together[0]
69 if not isinstance(first_element, (tuple, list)):
70 option_together = (option_together,)
71 # Normalize everything to tuples
72 return tuple(tuple(ot) for ot in option_together)
73 except TypeError:
74 # If the value of option_together isn't valid, return it
75 # verbatim; this will be picked up by the check framework later.
76 return option_together
79def make_immutable_fields_list(name, data):
80 return ImmutableList(data, warning=IMMUTABLE_WARNING % name)
83class Options:
84 FORWARD_PROPERTIES = {
85 "fields",
86 "many_to_many",
87 "concrete_fields",
88 "local_concrete_fields",
89 "_forward_fields_map",
90 "managers",
91 "managers_map",
92 "base_manager",
93 "default_manager",
94 }
95 REVERSE_PROPERTIES = {"related_objects", "fields_map", "_relation_tree"}
97 default_apps = apps
99 def __init__(self, meta, app_label=None):
100 self._get_fields_cache = {}
101 self.local_fields = []
102 self.local_many_to_many = []
103 self.private_fields = []
104 self.local_managers = []
105 self.base_manager_name = None
106 self.default_manager_name = None
107 self.model_name = None
108 self.verbose_name = None
109 self.verbose_name_plural = None
110 self.db_table = ""
111 self.ordering = []
112 self._ordering_clash = False
113 self.indexes = []
114 self.constraints = []
115 self.unique_together = []
116 self.index_together = []
117 self.select_on_save = False
118 self.default_permissions = ("add", "change", "delete", "view")
119 self.permissions = []
120 self.object_name = None
121 self.app_label = app_label
122 self.get_latest_by = None
123 self.order_with_respect_to = None
124 self.db_tablespace = settings.DEFAULT_TABLESPACE
125 self.required_db_features = []
126 self.required_db_vendor = None
127 self.meta = meta
128 self.pk = None
129 self.auto_field = None
130 self.abstract = False
131 self.managed = True
132 self.proxy = False
133 # For any class that is a proxy (including automatically created
134 # classes for deferred object loading), proxy_for_model tells us
135 # which class this model is proxying. Note that proxy_for_model
136 # can create a chain of proxy models. For non-proxy models, the
137 # variable is always None.
138 self.proxy_for_model = None
139 # For any non-abstract class, the concrete class is the model
140 # in the end of the proxy_for_model chain. In particular, for
141 # concrete models, the concrete_model is always the class itself.
142 self.concrete_model = None
143 self.swappable = None
144 self.parents = {}
145 self.auto_created = False
147 # List of all lookups defined in ForeignKey 'limit_choices_to' options
148 # from *other* models. Needed for some admin checks. Internal use only.
149 self.related_fkey_lookups = []
151 # A custom app registry to use, if you're making a separate model set.
152 self.apps = self.default_apps
154 self.default_related_name = None
156 @property
157 def label(self):
158 return "%s.%s" % (self.app_label, self.object_name)
160 @property
161 def label_lower(self):
162 return "%s.%s" % (self.app_label, self.model_name)
164 @property
165 def app_config(self):
166 # Don't go through get_app_config to avoid triggering imports.
167 return self.apps.app_configs.get(self.app_label)
169 @property
170 def installed(self):
171 return self.app_config is not None
173 def contribute_to_class(self, cls, name):
174 from django.db import connection
175 from django.db.backends.utils import truncate_name
177 cls._meta = self
178 self.model = cls
179 # First, construct the default values for these options.
180 self.object_name = cls.__name__
181 self.model_name = self.object_name.lower()
182 self.verbose_name = camel_case_to_spaces(self.object_name)
184 # Store the original user-defined values for each option,
185 # for use when serializing the model definition
186 self.original_attrs = {}
188 # Next, apply any overridden values from 'class Meta'.
189 if self.meta: 189 ↛ 228line 189 didn't jump to line 228, because the condition on line 189 was never false
190 meta_attrs = self.meta.__dict__.copy()
191 for name in self.meta.__dict__:
192 # Ignore any private attributes that Django doesn't care about.
193 # NOTE: We can't modify a dictionary's contents while looping
194 # over it, so we loop over the *original* dictionary instead.
195 if name.startswith("_"):
196 del meta_attrs[name]
197 for attr_name in DEFAULT_NAMES:
198 if attr_name in meta_attrs:
199 setattr(self, attr_name, meta_attrs.pop(attr_name))
200 self.original_attrs[attr_name] = getattr(self, attr_name)
201 elif hasattr(self.meta, attr_name):
202 setattr(self, attr_name, getattr(self.meta, attr_name))
203 self.original_attrs[attr_name] = getattr(self, attr_name)
205 self.unique_together = normalize_together(self.unique_together)
206 self.index_together = normalize_together(self.index_together)
207 # App label/class name interpolation for names of constraints and
208 # indexes.
209 if not getattr(cls._meta, "abstract", False):
210 for attr_name in {"constraints", "indexes"}:
211 objs = getattr(self, attr_name, [])
212 setattr(self, attr_name, self._format_names_with_class(cls, objs))
214 # verbose_name_plural is a special case because it uses a 's'
215 # by default.
216 if self.verbose_name_plural is None:
217 self.verbose_name_plural = format_lazy("{}s", self.verbose_name)
219 # order_with_respect_and ordering are mutually exclusive.
220 self._ordering_clash = bool(self.ordering and self.order_with_respect_to)
222 # Any leftover attributes must be invalid.
223 if meta_attrs != {}: 223 ↛ 224line 223 didn't jump to line 224, because the condition on line 223 was never true
224 raise TypeError(
225 "'class Meta' got invalid attribute(s): %s" % ",".join(meta_attrs)
226 )
227 else:
228 self.verbose_name_plural = format_lazy("{}s", self.verbose_name)
229 del self.meta
231 # If the db_table wasn't provided, use the app_label + model_name.
232 if not self.db_table:
233 self.db_table = "%s_%s" % (self.app_label, self.model_name)
234 self.db_table = truncate_name(
235 self.db_table, connection.ops.max_name_length()
236 )
238 def _format_names_with_class(self, cls, objs):
239 """App label/class name interpolation for object names."""
240 new_objs = []
241 for obj in objs:
242 obj = obj.clone()
243 obj.name = obj.name % {
244 "app_label": cls._meta.app_label.lower(),
245 "class": cls.__name__.lower(),
246 }
247 new_objs.append(obj)
248 return new_objs
250 def _get_default_pk_class(self):
251 pk_class_path = getattr(
252 self.app_config,
253 "default_auto_field",
254 settings.DEFAULT_AUTO_FIELD,
255 )
256 if self.app_config and self.app_config._is_default_auto_field_overridden:
257 app_config_class = type(self.app_config)
258 source = (
259 f"{app_config_class.__module__}."
260 f"{app_config_class.__qualname__}.default_auto_field"
261 )
262 else:
263 source = "DEFAULT_AUTO_FIELD"
264 if not pk_class_path: 264 ↛ 265line 264 didn't jump to line 265, because the condition on line 264 was never true
265 raise ImproperlyConfigured(f"{source} must not be empty.")
266 try:
267 pk_class = import_string(pk_class_path)
268 except ImportError as e:
269 msg = (
270 f"{source} refers to the module '{pk_class_path}' that could "
271 f"not be imported."
272 )
273 raise ImproperlyConfigured(msg) from e
274 if not issubclass(pk_class, AutoField): 274 ↛ 275line 274 didn't jump to line 275, because the condition on line 274 was never true
275 raise ValueError(
276 f"Primary key '{pk_class_path}' referred by {source} must "
277 f"subclass AutoField."
278 )
279 return pk_class
281 def _prepare(self, model):
282 if self.order_with_respect_to: 282 ↛ 285line 282 didn't jump to line 285, because the condition on line 282 was never true
283 # The app registry will not be ready at this point, so we cannot
284 # use get_field().
285 query = self.order_with_respect_to
286 try:
287 self.order_with_respect_to = next(
288 f
289 for f in self._get_fields(reverse=False)
290 if f.name == query or f.attname == query
291 )
292 except StopIteration:
293 raise FieldDoesNotExist(
294 "%s has no field named '%s'" % (self.object_name, query)
295 )
297 self.ordering = ("_order",)
298 if not any(
299 isinstance(field, OrderWrt) for field in model._meta.local_fields
300 ):
301 model.add_to_class("_order", OrderWrt())
302 else:
303 self.order_with_respect_to = None
305 if self.pk is None:
306 if self.parents:
307 # Promote the first parent link in lieu of adding yet another
308 # field.
309 field = next(iter(self.parents.values()))
310 # Look for a local field with the same name as the
311 # first parent link. If a local field has already been
312 # created, use it instead of promoting the parent
313 already_created = [
314 fld for fld in self.local_fields if fld.name == field.name
315 ]
316 if already_created: 316 ↛ 318line 316 didn't jump to line 318, because the condition on line 316 was never false
317 field = already_created[0]
318 field.primary_key = True
319 self.setup_pk(field)
320 else:
321 pk_class = self._get_default_pk_class()
322 auto = pk_class(verbose_name="ID", primary_key=True, auto_created=True)
323 model.add_to_class("id", auto)
325 def add_manager(self, manager):
326 self.local_managers.append(manager)
327 self._expire_cache()
329 def add_field(self, field, private=False):
330 # Insert the given field in the order in which it was created, using
331 # the "creation_counter" attribute of the field.
332 # Move many-to-many related fields from self.fields into
333 # self.many_to_many.
334 if private:
335 self.private_fields.append(field)
336 elif field.is_relation and field.many_to_many:
337 bisect.insort(self.local_many_to_many, field)
338 else:
339 bisect.insort(self.local_fields, field)
340 self.setup_pk(field)
342 # If the field being added is a relation to another known field,
343 # expire the cache on this field and the forward cache on the field
344 # being referenced, because there will be new relationships in the
345 # cache. Otherwise, expire the cache of references *to* this field.
346 # The mechanism for getting at the related model is slightly odd -
347 # ideally, we'd just ask for field.related_model. However, related_model
348 # is a cached property, and all the models haven't been loaded yet, so
349 # we need to make sure we don't cache a string reference.
350 if (
351 field.is_relation
352 and hasattr(field.remote_field, "model")
353 and field.remote_field.model
354 ):
355 try:
356 field.remote_field.model._meta._expire_cache(forward=False)
357 except AttributeError:
358 pass
359 self._expire_cache()
360 else:
361 self._expire_cache(reverse=False)
363 def setup_pk(self, field):
364 if not self.pk and field.primary_key:
365 self.pk = field
366 field.serialize = False
368 def setup_proxy(self, target):
369 """
370 Do the internal setup so that the current model is a proxy for
371 "target".
372 """
373 self.pk = target._meta.pk
374 self.proxy_for_model = target
375 self.db_table = target._meta.db_table
377 def __repr__(self):
378 return "<Options for %s>" % self.object_name
380 def __str__(self):
381 return self.label_lower
383 def can_migrate(self, connection):
384 """
385 Return True if the model can/should be migrated on the `connection`.
386 `connection` can be either a real connection or a connection alias.
387 """
388 if self.proxy or self.swapped or not self.managed:
389 return False
390 if isinstance(connection, str): 390 ↛ 392line 390 didn't jump to line 392, because the condition on line 390 was never false
391 connection = connections[connection]
392 if self.required_db_vendor: 392 ↛ 393line 392 didn't jump to line 393, because the condition on line 392 was never true
393 return self.required_db_vendor == connection.vendor
394 if self.required_db_features: 394 ↛ 395line 394 didn't jump to line 395, because the condition on line 394 was never true
395 return all(
396 getattr(connection.features, feat, False)
397 for feat in self.required_db_features
398 )
399 return True
401 @property
402 def verbose_name_raw(self):
403 """Return the untranslated verbose name."""
404 with override(None):
405 return str(self.verbose_name)
407 @property
408 def swapped(self):
409 """
410 Has this model been swapped out for another? If so, return the model
411 name of the replacement; otherwise, return None.
413 For historical reasons, model name lookups using get_model() are
414 case insensitive, so we make sure we are case insensitive here.
415 """
416 if self.swappable:
417 swapped_for = getattr(settings, self.swappable, None)
418 if swapped_for: 418 ↛ 433line 418 didn't jump to line 433, because the condition on line 418 was never false
419 try:
420 swapped_label, swapped_object = swapped_for.split(".")
421 except ValueError:
422 # setting not in the format app_label.model_name
423 # raising ImproperlyConfigured here causes problems with
424 # test cleanup code - instead it is raised in get_user_model
425 # or as part of validation.
426 return swapped_for
428 if ( 428 ↛ 433line 428 didn't jump to line 433
429 "%s.%s" % (swapped_label, swapped_object.lower())
430 != self.label_lower
431 ):
432 return swapped_for
433 return None
435 @cached_property
436 def managers(self):
437 managers = []
438 seen_managers = set()
439 bases = (b for b in self.model.mro() if hasattr(b, "_meta"))
440 for depth, base in enumerate(bases):
441 for manager in base._meta.local_managers:
442 if manager.name in seen_managers:
443 continue
445 manager = copy.copy(manager)
446 manager.model = self.model
447 seen_managers.add(manager.name)
448 managers.append((depth, manager.creation_counter, manager))
450 return make_immutable_fields_list(
451 "managers",
452 (m[2] for m in sorted(managers)),
453 )
455 @cached_property
456 def managers_map(self):
457 return {manager.name: manager for manager in self.managers}
459 @cached_property
460 def base_manager(self):
461 base_manager_name = self.base_manager_name
462 if not base_manager_name:
463 # Get the first parent's base_manager_name if there's one.
464 for parent in self.model.mro()[1:]:
465 if hasattr(parent, "_meta"):
466 if parent._base_manager.name != "_base_manager":
467 base_manager_name = parent._base_manager.name
468 break
470 if base_manager_name:
471 try:
472 return self.managers_map[base_manager_name]
473 except KeyError:
474 raise ValueError(
475 "%s has no manager named %r"
476 % (
477 self.object_name,
478 base_manager_name,
479 )
480 )
482 manager = Manager()
483 manager.name = "_base_manager"
484 manager.model = self.model
485 manager.auto_created = True
486 return manager
488 @cached_property
489 def default_manager(self):
490 default_manager_name = self.default_manager_name
491 if not default_manager_name and not self.local_managers:
492 # Get the first parent's default_manager_name if there's one.
493 for parent in self.model.mro()[1:]: 493 ↛ 498line 493 didn't jump to line 498, because the loop on line 493 didn't complete
494 if hasattr(parent, "_meta"): 494 ↛ 493line 494 didn't jump to line 493, because the condition on line 494 was never false
495 default_manager_name = parent._meta.default_manager_name
496 break
498 if default_manager_name: 498 ↛ 499line 498 didn't jump to line 499, because the condition on line 498 was never true
499 try:
500 return self.managers_map[default_manager_name]
501 except KeyError:
502 raise ValueError(
503 "%s has no manager named %r"
504 % (
505 self.object_name,
506 default_manager_name,
507 )
508 )
510 if self.managers: 510 ↛ exitline 510 didn't return from function 'default_manager', because the condition on line 510 was never false
511 return self.managers[0]
513 @cached_property
514 def fields(self):
515 """
516 Return a list of all forward fields on the model and its parents,
517 excluding ManyToManyFields.
519 Private API intended only to be used by Django itself; get_fields()
520 combined with filtering of field properties is the public API for
521 obtaining this field list.
522 """
523 # For legacy reasons, the fields property should only contain forward
524 # fields that are not private or with a m2m cardinality. Therefore we
525 # pass these three filters as filters to the generator.
526 # The third lambda is a longwinded way of checking f.related_model - we don't
527 # use that property directly because related_model is a cached property,
528 # and all the models may not have been loaded yet; we don't want to cache
529 # the string reference to the related_model.
530 def is_not_an_m2m_field(f):
531 return not (f.is_relation and f.many_to_many)
533 def is_not_a_generic_relation(f):
534 return not (f.is_relation and f.one_to_many)
536 def is_not_a_generic_foreign_key(f):
537 return not (
538 f.is_relation
539 and f.many_to_one
540 and not (hasattr(f.remote_field, "model") and f.remote_field.model)
541 )
543 return make_immutable_fields_list(
544 "fields",
545 (
546 f
547 for f in self._get_fields(reverse=False)
548 if is_not_an_m2m_field(f)
549 and is_not_a_generic_relation(f)
550 and is_not_a_generic_foreign_key(f)
551 ),
552 )
554 @cached_property
555 def concrete_fields(self):
556 """
557 Return a list of all concrete fields on the model and its parents.
559 Private API intended only to be used by Django itself; get_fields()
560 combined with filtering of field properties is the public API for
561 obtaining this field list.
562 """
563 return make_immutable_fields_list(
564 "concrete_fields", (f for f in self.fields if f.concrete)
565 )
567 @cached_property
568 def local_concrete_fields(self):
569 """
570 Return a list of all concrete fields on the model.
572 Private API intended only to be used by Django itself; get_fields()
573 combined with filtering of field properties is the public API for
574 obtaining this field list.
575 """
576 return make_immutable_fields_list(
577 "local_concrete_fields", (f for f in self.local_fields if f.concrete)
578 )
580 @cached_property
581 def many_to_many(self):
582 """
583 Return a list of all many to many fields on the model and its parents.
585 Private API intended only to be used by Django itself; get_fields()
586 combined with filtering of field properties is the public API for
587 obtaining this list.
588 """
589 return make_immutable_fields_list(
590 "many_to_many",
591 (
592 f
593 for f in self._get_fields(reverse=False)
594 if f.is_relation and f.many_to_many
595 ),
596 )
598 @cached_property
599 def related_objects(self):
600 """
601 Return all related objects pointing to the current model. The related
602 objects can come from a one-to-one, one-to-many, or many-to-many field
603 relation type.
605 Private API intended only to be used by Django itself; get_fields()
606 combined with filtering of field properties is the public API for
607 obtaining this field list.
608 """
609 all_related_fields = self._get_fields(
610 forward=False, reverse=True, include_hidden=True
611 )
612 return make_immutable_fields_list(
613 "related_objects",
614 (
615 obj
616 for obj in all_related_fields
617 if not obj.hidden or obj.field.many_to_many
618 ),
619 )
621 @cached_property
622 def _forward_fields_map(self):
623 res = {}
624 fields = self._get_fields(reverse=False)
625 for field in fields:
626 res[field.name] = field
627 # Due to the way Django's internals work, get_field() should also
628 # be able to fetch a field by attname. In the case of a concrete
629 # field with relation, includes the *_id name too
630 try:
631 res[field.attname] = field
632 except AttributeError:
633 pass
634 return res
636 @cached_property
637 def fields_map(self):
638 res = {}
639 fields = self._get_fields(forward=False, include_hidden=True)
640 for field in fields:
641 res[field.name] = field
642 # Due to the way Django's internals work, get_field() should also
643 # be able to fetch a field by attname. In the case of a concrete
644 # field with relation, includes the *_id name too
645 try:
646 res[field.attname] = field
647 except AttributeError:
648 pass
649 return res
651 def get_field(self, field_name):
652 """
653 Return a field instance given the name of a forward or reverse field.
654 """
655 try:
656 # In order to avoid premature loading of the relation tree
657 # (expensive) we prefer checking if the field is a forward field.
658 return self._forward_fields_map[field_name]
659 except KeyError:
660 # If the app registry is not ready, reverse fields are
661 # unavailable, therefore we throw a FieldDoesNotExist exception.
662 if not self.apps.models_ready: 662 ↛ 663line 662 didn't jump to line 663, because the condition on line 662 was never true
663 raise FieldDoesNotExist(
664 "%s has no field named '%s'. The app cache isn't ready yet, "
665 "so if this is an auto-created related field, it won't "
666 "be available yet." % (self.object_name, field_name)
667 )
669 try:
670 # Retrieve field instance by name from cached or just-computed
671 # field map.
672 return self.fields_map[field_name]
673 except KeyError:
674 raise FieldDoesNotExist(
675 "%s has no field named '%s'" % (self.object_name, field_name)
676 )
678 def get_base_chain(self, model):
679 """
680 Return a list of parent classes leading to `model` (ordered from
681 closest to most distant ancestor). This has to handle the case where
682 `model` is a grandparent or even more distant relation.
683 """
684 if not self.parents: 684 ↛ 685line 684 didn't jump to line 685, because the condition on line 684 was never true
685 return []
686 if model in self.parents: 686 ↛ 688line 686 didn't jump to line 688, because the condition on line 686 was never false
687 return [model]
688 for parent in self.parents:
689 res = parent._meta.get_base_chain(model)
690 if res:
691 res.insert(0, parent)
692 return res
693 return []
695 def get_parent_list(self):
696 """
697 Return all the ancestors of this model as a list ordered by MRO.
698 Useful for determining if something is an ancestor, regardless of lineage.
699 """
700 result = OrderedSet(self.parents)
701 for parent in self.parents:
702 for ancestor in parent._meta.get_parent_list(): 702 ↛ 703line 702 didn't jump to line 703, because the loop on line 702 never started
703 result.add(ancestor)
704 return list(result)
706 def get_ancestor_link(self, ancestor):
707 """
708 Return the field on the current model which points to the given
709 "ancestor". This is possible an indirect link (a pointer to a parent
710 model, which points, eventually, to the ancestor). Used when
711 constructing table joins for model inheritance.
713 Return None if the model isn't an ancestor of this one.
714 """
715 if ancestor in self.parents:
716 return self.parents[ancestor]
717 for parent in self.parents:
718 # Tries to get a link field from the immediate parent
719 parent_link = parent._meta.get_ancestor_link(ancestor)
720 if parent_link: 720 ↛ 724line 720 didn't jump to line 724, because the condition on line 720 was never true
721 # In case of a proxied model, the first link
722 # of the chain to the ancestor is that parent
723 # links
724 return self.parents[parent] or parent_link
726 def get_path_to_parent(self, parent):
727 """
728 Return a list of PathInfos containing the path from the current
729 model to the parent model, or an empty list if parent is not a
730 parent of the current model.
731 """
732 if self.model is parent: 732 ↛ 733line 732 didn't jump to line 733, because the condition on line 732 was never true
733 return []
734 # Skip the chain of proxy to the concrete proxied model.
735 proxied_model = self.concrete_model
736 path = []
737 opts = self
738 for int_model in self.get_base_chain(parent):
739 if int_model is proxied_model:
740 opts = int_model._meta
741 else:
742 final_field = opts.parents[int_model]
743 targets = (final_field.remote_field.get_related_field(),)
744 opts = int_model._meta
745 path.append(
746 PathInfo(
747 from_opts=final_field.model._meta,
748 to_opts=opts,
749 target_fields=targets,
750 join_field=final_field,
751 m2m=False,
752 direct=True,
753 filtered_relation=None,
754 )
755 )
756 return path
758 def get_path_from_parent(self, parent):
759 """
760 Return a list of PathInfos containing the path from the parent
761 model to the current model, or an empty list if parent is not a
762 parent of the current model.
763 """
764 if self.model is parent:
765 return []
766 model = self.concrete_model
767 # Get a reversed base chain including both the current and parent
768 # models.
769 chain = model._meta.get_base_chain(parent)
770 chain.reverse()
771 chain.append(model)
772 # Construct a list of the PathInfos between models in chain.
773 path = []
774 for i, ancestor in enumerate(chain[:-1]):
775 child = chain[i + 1]
776 link = child._meta.get_ancestor_link(ancestor)
777 path.extend(link.get_reverse_path_info())
778 return path
780 def _populate_directed_relation_graph(self):
781 """
782 This method is used by each model to find its reverse objects. As this
783 method is very expensive and is accessed frequently (it looks up every
784 field in a model, in every app), it is computed on first access and then
785 is set as a property on every model.
786 """
787 related_objects_graph = defaultdict(list)
789 all_models = self.apps.get_models(include_auto_created=True)
790 for model in all_models:
791 opts = model._meta
792 # Abstract model's fields are copied to child models, hence we will
793 # see the fields from the child models.
794 if opts.abstract: 794 ↛ 795line 794 didn't jump to line 795, because the condition on line 794 was never true
795 continue
796 fields_with_relations = (
797 f
798 for f in opts._get_fields(reverse=False, include_parents=False)
799 if f.is_relation and f.related_model is not None
800 )
801 for f in fields_with_relations:
802 if not isinstance(f.remote_field.model, str): 802 ↛ 801line 802 didn't jump to line 801, because the condition on line 802 was never false
803 remote_label = f.remote_field.model._meta.concrete_model._meta.label
804 related_objects_graph[remote_label].append(f)
806 for model in all_models:
807 # Set the relation_tree using the internal __dict__. In this way
808 # we avoid calling the cached property. In attribute lookup,
809 # __dict__ takes precedence over a data descriptor (such as
810 # @cached_property). This means that the _meta._relation_tree is
811 # only called if related_objects is not in __dict__.
812 related_objects = related_objects_graph[
813 model._meta.concrete_model._meta.label
814 ]
815 model._meta.__dict__["_relation_tree"] = related_objects
816 # It seems it is possible that self is not in all_models, so guard
817 # against that with default for get().
818 return self.__dict__.get("_relation_tree", EMPTY_RELATION_TREE)
820 @cached_property
821 def _relation_tree(self):
822 return self._populate_directed_relation_graph()
824 def _expire_cache(self, forward=True, reverse=True):
825 # This method is usually called by apps.cache_clear(), when the
826 # registry is finalized, or when a new field is added.
827 if forward:
828 for cache_key in self.FORWARD_PROPERTIES:
829 if cache_key in self.__dict__:
830 delattr(self, cache_key)
831 if reverse and not self.abstract:
832 for cache_key in self.REVERSE_PROPERTIES:
833 if cache_key in self.__dict__:
834 delattr(self, cache_key)
835 self._get_fields_cache = {}
837 def get_fields(self, include_parents=True, include_hidden=False):
838 """
839 Return a list of fields associated to the model. By default, include
840 forward and reverse fields, fields derived from inheritance, but not
841 hidden fields. The returned fields can be changed using the parameters:
843 - include_parents: include fields derived from inheritance
844 - include_hidden: include fields that have a related_name that
845 starts with a "+"
846 """
847 if include_parents is False: 847 ↛ 848line 847 didn't jump to line 848, because the condition on line 847 was never true
848 include_parents = PROXY_PARENTS
849 return self._get_fields(
850 include_parents=include_parents, include_hidden=include_hidden
851 )
853 def _get_fields(
854 self,
855 forward=True,
856 reverse=True,
857 include_parents=True,
858 include_hidden=False,
859 seen_models=None,
860 ):
861 """
862 Internal helper function to return fields of the model.
863 * If forward=True, then fields defined on this model are returned.
864 * If reverse=True, then relations pointing to this model are returned.
865 * If include_hidden=True, then fields with is_hidden=True are returned.
866 * The include_parents argument toggles if fields from parent models
867 should be included. It has three values: True, False, and
868 PROXY_PARENTS. When set to PROXY_PARENTS, the call will return all
869 fields defined for the current model or any of its parents in the
870 parent chain to the model's concrete model.
871 """
872 if include_parents not in (True, False, PROXY_PARENTS): 872 ↛ 873line 872 didn't jump to line 873, because the condition on line 872 was never true
873 raise TypeError(
874 "Invalid argument for include_parents: %s" % (include_parents,)
875 )
876 # This helper function is used to allow recursion in ``get_fields()``
877 # implementation and to provide a fast way for Django's internals to
878 # access specific subsets of fields.
880 # We must keep track of which models we have already seen. Otherwise we
881 # could include the same field multiple times from different models.
882 topmost_call = seen_models is None
883 if topmost_call:
884 seen_models = set()
885 seen_models.add(self.model)
887 # Creates a cache key composed of all arguments
888 cache_key = (forward, reverse, include_parents, include_hidden, topmost_call)
890 try:
891 # In order to avoid list manipulation. Always return a shallow copy
892 # of the results.
893 return self._get_fields_cache[cache_key]
894 except KeyError:
895 pass
897 fields = []
898 # Recursively call _get_fields() on each parent, with the same
899 # options provided in this call.
900 if include_parents is not False:
901 for parent in self.parents:
902 # In diamond inheritance it is possible that we see the same
903 # model from two different routes. In that case, avoid adding
904 # fields from the same parent again.
905 if parent in seen_models: 905 ↛ 906line 905 didn't jump to line 906, because the condition on line 905 was never true
906 continue
907 if (
908 parent._meta.concrete_model != self.concrete_model
909 and include_parents == PROXY_PARENTS
910 ):
911 continue
912 for obj in parent._meta._get_fields(
913 forward=forward,
914 reverse=reverse,
915 include_parents=include_parents,
916 include_hidden=include_hidden,
917 seen_models=seen_models,
918 ):
919 if (
920 not getattr(obj, "parent_link", False)
921 or obj.model == self.concrete_model
922 ):
923 fields.append(obj)
924 if reverse and not self.proxy:
925 # Tree is computed once and cached until the app cache is expired.
926 # It is composed of a list of fields pointing to the current model
927 # from other models.
928 all_fields = self._relation_tree
929 for field in all_fields:
930 # If hidden fields should be included or the relation is not
931 # intentionally hidden, add to the fields dict.
932 if include_hidden or not field.remote_field.hidden:
933 fields.append(field.remote_field)
935 if forward:
936 fields += self.local_fields
937 fields += self.local_many_to_many
938 # Private fields are recopied to each child model, and they get a
939 # different model as field.model in each child. Hence we have to
940 # add the private fields separately from the topmost call. If we
941 # did this recursively similar to local_fields, we would get field
942 # instances with field.model != self.model.
943 if topmost_call:
944 fields += self.private_fields
946 # In order to avoid list manipulation. Always
947 # return a shallow copy of the results
948 fields = make_immutable_fields_list("get_fields()", fields)
950 # Store result into cache for later access
951 self._get_fields_cache[cache_key] = fields
952 return fields
954 @cached_property
955 def total_unique_constraints(self):
956 """
957 Return a list of total unique constraints. Useful for determining set
958 of fields guaranteed to be unique for all rows.
959 """
960 return [
961 constraint
962 for constraint in self.constraints
963 if (
964 isinstance(constraint, UniqueConstraint)
965 and constraint.condition is None
966 and not constraint.contains_expressions
967 )
968 ]
970 @cached_property
971 def _property_names(self):
972 """Return a set of the names of the properties defined on the model."""
973 names = []
974 for name in dir(self.model):
975 attr = inspect.getattr_static(self.model, name)
976 if isinstance(attr, property):
977 names.append(name)
978 return frozenset(names)
980 @cached_property
981 def db_returning_fields(self):
982 """
983 Private API intended only to be used by Django itself.
984 Fields to be returned after a database insert.
985 """
986 return [
987 field
988 for field in self._get_fields(
989 forward=True, reverse=False, include_parents=PROXY_PARENTS
990 )
991 if getattr(field, "db_returning", False)
992 ]