Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/db/models/fields/related.py: 25%
760 statements
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
1import functools
2import inspect
3from functools import partial
5from django import forms
6from django.apps import apps
7from django.conf import SettingsReference, settings
8from django.core import checks, exceptions
9from django.db import connection, router
10from django.db.backends import utils
11from django.db.models import Q
12from django.db.models.constants import LOOKUP_SEP
13from django.db.models.deletion import CASCADE, SET_DEFAULT, SET_NULL
14from django.db.models.query_utils import PathInfo
15from django.db.models.utils import make_model_tuple
16from django.utils.functional import cached_property
17from django.utils.translation import gettext_lazy as _
19from . import Field
20from .mixins import FieldCacheMixin
21from .related_descriptors import (
22 ForeignKeyDeferredAttribute,
23 ForwardManyToOneDescriptor,
24 ForwardOneToOneDescriptor,
25 ManyToManyDescriptor,
26 ReverseManyToOneDescriptor,
27 ReverseOneToOneDescriptor,
28)
29from .related_lookups import (
30 RelatedExact,
31 RelatedGreaterThan,
32 RelatedGreaterThanOrEqual,
33 RelatedIn,
34 RelatedIsNull,
35 RelatedLessThan,
36 RelatedLessThanOrEqual,
37)
38from .reverse_related import ForeignObjectRel, ManyToManyRel, ManyToOneRel, OneToOneRel
40RECURSIVE_RELATIONSHIP_CONSTANT = "self"
43def resolve_relation(scope_model, relation):
44 """
45 Transform relation into a model or fully-qualified model string of the form
46 "app_label.ModelName", relative to scope_model.
48 The relation argument can be:
49 * RECURSIVE_RELATIONSHIP_CONSTANT, i.e. the string "self", in which case
50 the model argument will be returned.
51 * A bare model name without an app_label, in which case scope_model's
52 app_label will be prepended.
53 * An "app_label.ModelName" string.
54 * A model class, which will be returned unchanged.
55 """
56 # Check for recursive relations
57 if relation == RECURSIVE_RELATIONSHIP_CONSTANT:
58 relation = scope_model
60 # Look for an "app.Model" relation
61 if isinstance(relation, str):
62 if "." not in relation:
63 relation = "%s.%s" % (scope_model._meta.app_label, relation)
65 return relation
68def lazy_related_operation(function, model, *related_models, **kwargs):
69 """
70 Schedule `function` to be called once `model` and all `related_models`
71 have been imported and registered with the app registry. `function` will
72 be called with the newly-loaded model classes as its positional arguments,
73 plus any optional keyword arguments.
75 The `model` argument must be a model class. Each subsequent positional
76 argument is another model, or a reference to another model - see
77 `resolve_relation()` for the various forms these may take. Any relative
78 references will be resolved relative to `model`.
80 This is a convenience wrapper for `Apps.lazy_model_operation` - the app
81 registry model used is the one found in `model._meta.apps`.
82 """
83 models = [model] + [resolve_relation(model, rel) for rel in related_models]
84 model_keys = (make_model_tuple(m) for m in models)
85 apps = model._meta.apps
86 return apps.lazy_model_operation(partial(function, **kwargs), *model_keys)
89class RelatedField(FieldCacheMixin, Field):
90 """Base class that all relational fields inherit from."""
92 # Field flags
93 one_to_many = False
94 one_to_one = False
95 many_to_many = False
96 many_to_one = False
98 def __init__(
99 self,
100 related_name=None,
101 related_query_name=None,
102 limit_choices_to=None,
103 **kwargs,
104 ):
105 self._related_name = related_name
106 self._related_query_name = related_query_name
107 self._limit_choices_to = limit_choices_to
108 super().__init__(**kwargs)
110 @cached_property
111 def related_model(self):
112 # Can't cache this property until all the models are loaded.
113 apps.check_models_ready()
114 return self.remote_field.model
116 def check(self, **kwargs):
117 return [
118 *super().check(**kwargs),
119 *self._check_related_name_is_valid(),
120 *self._check_related_query_name_is_valid(),
121 *self._check_relation_model_exists(),
122 *self._check_referencing_to_swapped_model(),
123 *self._check_clashes(),
124 ]
126 def _check_related_name_is_valid(self):
127 import keyword
129 related_name = self.remote_field.related_name
130 if related_name is None:
131 return []
132 is_valid_id = (
133 not keyword.iskeyword(related_name) and related_name.isidentifier()
134 )
135 if not (is_valid_id or related_name.endswith("+")):
136 return [
137 checks.Error(
138 "The name '%s' is invalid related_name for field %s.%s"
139 % (
140 self.remote_field.related_name,
141 self.model._meta.object_name,
142 self.name,
143 ),
144 hint=(
145 "Related name must be a valid Python identifier or end with a "
146 "'+'"
147 ),
148 obj=self,
149 id="fields.E306",
150 )
151 ]
152 return []
154 def _check_related_query_name_is_valid(self):
155 if self.remote_field.is_hidden():
156 return []
157 rel_query_name = self.related_query_name()
158 errors = []
159 if rel_query_name.endswith("_"):
160 errors.append(
161 checks.Error(
162 "Reverse query name '%s' must not end with an underscore."
163 % rel_query_name,
164 hint=(
165 "Add or change a related_name or related_query_name "
166 "argument for this field."
167 ),
168 obj=self,
169 id="fields.E308",
170 )
171 )
172 if LOOKUP_SEP in rel_query_name:
173 errors.append(
174 checks.Error(
175 "Reverse query name '%s' must not contain '%s'."
176 % (rel_query_name, LOOKUP_SEP),
177 hint=(
178 "Add or change a related_name or related_query_name "
179 "argument for this field."
180 ),
181 obj=self,
182 id="fields.E309",
183 )
184 )
185 return errors
187 def _check_relation_model_exists(self):
188 rel_is_missing = self.remote_field.model not in self.opts.apps.get_models()
189 rel_is_string = isinstance(self.remote_field.model, str)
190 model_name = (
191 self.remote_field.model
192 if rel_is_string
193 else self.remote_field.model._meta.object_name
194 )
195 if rel_is_missing and (
196 rel_is_string or not self.remote_field.model._meta.swapped
197 ):
198 return [
199 checks.Error(
200 "Field defines a relation with model '%s', which is either "
201 "not installed, or is abstract." % model_name,
202 obj=self,
203 id="fields.E300",
204 )
205 ]
206 return []
208 def _check_referencing_to_swapped_model(self):
209 if (
210 self.remote_field.model not in self.opts.apps.get_models()
211 and not isinstance(self.remote_field.model, str)
212 and self.remote_field.model._meta.swapped
213 ):
214 return [
215 checks.Error(
216 "Field defines a relation with the model '%s', which has "
217 "been swapped out." % self.remote_field.model._meta.label,
218 hint="Update the relation to point at 'settings.%s'."
219 % self.remote_field.model._meta.swappable,
220 obj=self,
221 id="fields.E301",
222 )
223 ]
224 return []
226 def _check_clashes(self):
227 """Check accessor and reverse query name clashes."""
228 from django.db.models.base import ModelBase
230 errors = []
231 opts = self.model._meta
233 # f.remote_field.model may be a string instead of a model. Skip if
234 # model name is not resolved.
235 if not isinstance(self.remote_field.model, ModelBase):
236 return []
238 # Consider that we are checking field `Model.foreign` and the models
239 # are:
240 #
241 # class Target(models.Model):
242 # model = models.IntegerField()
243 # model_set = models.IntegerField()
244 #
245 # class Model(models.Model):
246 # foreign = models.ForeignKey(Target)
247 # m2m = models.ManyToManyField(Target)
249 # rel_opts.object_name == "Target"
250 rel_opts = self.remote_field.model._meta
251 # If the field doesn't install a backward relation on the target model
252 # (so `is_hidden` returns True), then there are no clashes to check
253 # and we can skip these fields.
254 rel_is_hidden = self.remote_field.is_hidden()
255 rel_name = self.remote_field.get_accessor_name() # i. e. "model_set"
256 rel_query_name = self.related_query_name() # i. e. "model"
257 # i.e. "app_label.Model.field".
258 field_name = "%s.%s" % (opts.label, self.name)
260 # Check clashes between accessor or reverse query name of `field`
261 # and any other field name -- i.e. accessor for Model.foreign is
262 # model_set and it clashes with Target.model_set.
263 potential_clashes = rel_opts.fields + rel_opts.many_to_many
264 for clash_field in potential_clashes:
265 # i.e. "app_label.Target.model_set".
266 clash_name = "%s.%s" % (rel_opts.label, clash_field.name)
267 if not rel_is_hidden and clash_field.name == rel_name:
268 errors.append(
269 checks.Error(
270 f"Reverse accessor '{rel_opts.object_name}.{rel_name}' "
271 f"for '{field_name}' clashes with field name "
272 f"'{clash_name}'.",
273 hint=(
274 "Rename field '%s', or add/change a related_name "
275 "argument to the definition for field '%s'."
276 )
277 % (clash_name, field_name),
278 obj=self,
279 id="fields.E302",
280 )
281 )
283 if clash_field.name == rel_query_name:
284 errors.append(
285 checks.Error(
286 "Reverse query name for '%s' clashes with field name '%s'."
287 % (field_name, clash_name),
288 hint=(
289 "Rename field '%s', or add/change a related_name "
290 "argument to the definition for field '%s'."
291 )
292 % (clash_name, field_name),
293 obj=self,
294 id="fields.E303",
295 )
296 )
298 # Check clashes between accessors/reverse query names of `field` and
299 # any other field accessor -- i. e. Model.foreign accessor clashes with
300 # Model.m2m accessor.
301 potential_clashes = (r for r in rel_opts.related_objects if r.field is not self)
302 for clash_field in potential_clashes:
303 # i.e. "app_label.Model.m2m".
304 clash_name = "%s.%s" % (
305 clash_field.related_model._meta.label,
306 clash_field.field.name,
307 )
308 if not rel_is_hidden and clash_field.get_accessor_name() == rel_name:
309 errors.append(
310 checks.Error(
311 f"Reverse accessor '{rel_opts.object_name}.{rel_name}' "
312 f"for '{field_name}' clashes with reverse accessor for "
313 f"'{clash_name}'.",
314 hint=(
315 "Add or change a related_name argument "
316 "to the definition for '%s' or '%s'."
317 )
318 % (field_name, clash_name),
319 obj=self,
320 id="fields.E304",
321 )
322 )
324 if clash_field.get_accessor_name() == rel_query_name:
325 errors.append(
326 checks.Error(
327 "Reverse query name for '%s' clashes with reverse query name "
328 "for '%s'." % (field_name, clash_name),
329 hint=(
330 "Add or change a related_name argument "
331 "to the definition for '%s' or '%s'."
332 )
333 % (field_name, clash_name),
334 obj=self,
335 id="fields.E305",
336 )
337 )
339 return errors
341 def db_type(self, connection):
342 # By default related field will not have a column as it relates to
343 # columns from another table.
344 return None
346 def contribute_to_class(self, cls, name, private_only=False, **kwargs):
348 super().contribute_to_class(cls, name, private_only=private_only, **kwargs)
350 self.opts = cls._meta
352 if not cls._meta.abstract:
353 if self.remote_field.related_name:
354 related_name = self.remote_field.related_name
355 else:
356 related_name = self.opts.default_related_name
357 if related_name:
358 related_name %= {
359 "class": cls.__name__.lower(),
360 "model_name": cls._meta.model_name.lower(),
361 "app_label": cls._meta.app_label.lower(),
362 }
363 self.remote_field.related_name = related_name
365 if self.remote_field.related_query_name:
366 related_query_name = self.remote_field.related_query_name % {
367 "class": cls.__name__.lower(),
368 "app_label": cls._meta.app_label.lower(),
369 }
370 self.remote_field.related_query_name = related_query_name
372 def resolve_related_class(model, related, field):
373 field.remote_field.model = related
374 field.do_related_class(related, model)
376 lazy_related_operation(
377 resolve_related_class, cls, self.remote_field.model, field=self
378 )
380 def deconstruct(self):
381 name, path, args, kwargs = super().deconstruct()
382 if self._limit_choices_to:
383 kwargs["limit_choices_to"] = self._limit_choices_to
384 if self._related_name is not None:
385 kwargs["related_name"] = self._related_name
386 if self._related_query_name is not None:
387 kwargs["related_query_name"] = self._related_query_name
388 return name, path, args, kwargs
390 def get_forward_related_filter(self, obj):
391 """
392 Return the keyword arguments that when supplied to
393 self.model.object.filter(), would select all instances related through
394 this field to the remote obj. This is used to build the querysets
395 returned by related descriptors. obj is an instance of
396 self.related_field.model.
397 """
398 return {
399 "%s__%s" % (self.name, rh_field.name): getattr(obj, rh_field.attname)
400 for _, rh_field in self.related_fields
401 }
403 def get_reverse_related_filter(self, obj):
404 """
405 Complement to get_forward_related_filter(). Return the keyword
406 arguments that when passed to self.related_field.model.object.filter()
407 select all instances of self.related_field.model related through
408 this field to obj. obj is an instance of self.model.
409 """
410 base_q = Q.create(
411 [
412 (rh_field.attname, getattr(obj, lh_field.attname))
413 for lh_field, rh_field in self.related_fields
414 ]
415 )
416 descriptor_filter = self.get_extra_descriptor_filter(obj)
417 if isinstance(descriptor_filter, dict):
418 return base_q & Q(**descriptor_filter)
419 elif descriptor_filter:
420 return base_q & descriptor_filter
421 return base_q
423 @property
424 def swappable_setting(self):
425 """
426 Get the setting that this is powered from for swapping, or None
427 if it's not swapped in / marked with swappable=False.
428 """
429 if self.swappable:
430 # Work out string form of "to"
431 if isinstance(self.remote_field.model, str):
432 to_string = self.remote_field.model
433 else:
434 to_string = self.remote_field.model._meta.label
435 return apps.get_swappable_settings_name(to_string)
436 return None
438 def set_attributes_from_rel(self):
439 self.name = self.name or (
440 self.remote_field.model._meta.model_name
441 + "_"
442 + self.remote_field.model._meta.pk.name
443 )
444 if self.verbose_name is None:
445 self.verbose_name = self.remote_field.model._meta.verbose_name
446 self.remote_field.set_field_name()
448 def do_related_class(self, other, cls):
449 self.set_attributes_from_rel()
450 self.contribute_to_related_class(other, self.remote_field)
452 def get_limit_choices_to(self):
453 """
454 Return ``limit_choices_to`` for this model field.
456 If it is a callable, it will be invoked and the result will be
457 returned.
458 """
459 if callable(self.remote_field.limit_choices_to):
460 return self.remote_field.limit_choices_to()
461 return self.remote_field.limit_choices_to
463 def formfield(self, **kwargs):
464 """
465 Pass ``limit_choices_to`` to the field being constructed.
467 Only passes it if there is a type that supports related fields.
468 This is a similar strategy used to pass the ``queryset`` to the field
469 being constructed.
470 """
471 defaults = {}
472 if hasattr(self.remote_field, "get_related_field"):
473 # If this is a callable, do not invoke it here. Just pass
474 # it in the defaults for when the form class will later be
475 # instantiated.
476 limit_choices_to = self.remote_field.limit_choices_to
477 defaults.update(
478 {
479 "limit_choices_to": limit_choices_to,
480 }
481 )
482 defaults.update(kwargs)
483 return super().formfield(**defaults)
485 def related_query_name(self):
486 """
487 Define the name that can be used to identify this related object in a
488 table-spanning query.
489 """
490 return (
491 self.remote_field.related_query_name
492 or self.remote_field.related_name
493 or self.opts.model_name
494 )
496 @property
497 def target_field(self):
498 """
499 When filtering against this relation, return the field on the remote
500 model against which the filtering should happen.
501 """
502 target_fields = self.path_infos[-1].target_fields
503 if len(target_fields) > 1:
504 raise exceptions.FieldError(
505 "The relation has multiple target fields, but only single target field "
506 "was asked for"
507 )
508 return target_fields[0]
510 def get_cache_name(self):
511 return self.name
514class ForeignObject(RelatedField):
515 """
516 Abstraction of the ForeignKey relation to support multi-column relations.
517 """
519 # Field flags
520 many_to_many = False
521 many_to_one = True
522 one_to_many = False
523 one_to_one = False
525 requires_unique_target = True
526 related_accessor_class = ReverseManyToOneDescriptor
527 forward_related_accessor_class = ForwardManyToOneDescriptor
528 rel_class = ForeignObjectRel
530 def __init__(
531 self,
532 to,
533 on_delete,
534 from_fields,
535 to_fields,
536 rel=None,
537 related_name=None,
538 related_query_name=None,
539 limit_choices_to=None,
540 parent_link=False,
541 swappable=True,
542 **kwargs,
543 ):
545 if rel is None:
546 rel = self.rel_class(
547 self,
548 to,
549 related_name=related_name,
550 related_query_name=related_query_name,
551 limit_choices_to=limit_choices_to,
552 parent_link=parent_link,
553 on_delete=on_delete,
554 )
556 super().__init__(
557 rel=rel,
558 related_name=related_name,
559 related_query_name=related_query_name,
560 limit_choices_to=limit_choices_to,
561 **kwargs,
562 )
564 self.from_fields = from_fields
565 self.to_fields = to_fields
566 self.swappable = swappable
568 def __copy__(self):
569 obj = super().__copy__()
570 # Remove any cached PathInfo values.
571 obj.__dict__.pop("path_infos", None)
572 obj.__dict__.pop("reverse_path_infos", None)
573 return obj
575 def check(self, **kwargs):
576 return [
577 *super().check(**kwargs),
578 *self._check_to_fields_exist(),
579 *self._check_unique_target(),
580 ]
582 def _check_to_fields_exist(self):
583 # Skip nonexistent models.
584 if isinstance(self.remote_field.model, str):
585 return []
587 errors = []
588 for to_field in self.to_fields:
589 if to_field:
590 try:
591 self.remote_field.model._meta.get_field(to_field)
592 except exceptions.FieldDoesNotExist:
593 errors.append(
594 checks.Error(
595 "The to_field '%s' doesn't exist on the related "
596 "model '%s'."
597 % (to_field, self.remote_field.model._meta.label),
598 obj=self,
599 id="fields.E312",
600 )
601 )
602 return errors
604 def _check_unique_target(self):
605 rel_is_string = isinstance(self.remote_field.model, str)
606 if rel_is_string or not self.requires_unique_target:
607 return []
609 try:
610 self.foreign_related_fields
611 except exceptions.FieldDoesNotExist:
612 return []
614 if not self.foreign_related_fields:
615 return []
617 unique_foreign_fields = {
618 frozenset([f.name])
619 for f in self.remote_field.model._meta.get_fields()
620 if getattr(f, "unique", False)
621 }
622 unique_foreign_fields.update(
623 {frozenset(ut) for ut in self.remote_field.model._meta.unique_together}
624 )
625 unique_foreign_fields.update(
626 {
627 frozenset(uc.fields)
628 for uc in self.remote_field.model._meta.total_unique_constraints
629 }
630 )
631 foreign_fields = {f.name for f in self.foreign_related_fields}
632 has_unique_constraint = any(u <= foreign_fields for u in unique_foreign_fields)
634 if not has_unique_constraint and len(self.foreign_related_fields) > 1:
635 field_combination = ", ".join(
636 "'%s'" % rel_field.name for rel_field in self.foreign_related_fields
637 )
638 model_name = self.remote_field.model.__name__
639 return [
640 checks.Error(
641 "No subset of the fields %s on model '%s' is unique."
642 % (field_combination, model_name),
643 hint=(
644 "Mark a single field as unique=True or add a set of "
645 "fields to a unique constraint (via unique_together "
646 "or a UniqueConstraint (without condition) in the "
647 "model Meta.constraints)."
648 ),
649 obj=self,
650 id="fields.E310",
651 )
652 ]
653 elif not has_unique_constraint:
654 field_name = self.foreign_related_fields[0].name
655 model_name = self.remote_field.model.__name__
656 return [
657 checks.Error(
658 "'%s.%s' must be unique because it is referenced by "
659 "a foreign key." % (model_name, field_name),
660 hint=(
661 "Add unique=True to this field or add a "
662 "UniqueConstraint (without condition) in the model "
663 "Meta.constraints."
664 ),
665 obj=self,
666 id="fields.E311",
667 )
668 ]
669 else:
670 return []
672 def deconstruct(self):
673 name, path, args, kwargs = super().deconstruct()
674 kwargs["on_delete"] = self.remote_field.on_delete
675 kwargs["from_fields"] = self.from_fields
676 kwargs["to_fields"] = self.to_fields
678 if self.remote_field.parent_link:
679 kwargs["parent_link"] = self.remote_field.parent_link
680 if isinstance(self.remote_field.model, str):
681 if "." in self.remote_field.model:
682 app_label, model_name = self.remote_field.model.split(".")
683 kwargs["to"] = "%s.%s" % (app_label, model_name.lower())
684 else:
685 kwargs["to"] = self.remote_field.model.lower()
686 else:
687 kwargs["to"] = self.remote_field.model._meta.label_lower
688 # If swappable is True, then see if we're actually pointing to the target
689 # of a swap.
690 swappable_setting = self.swappable_setting
691 if swappable_setting is not None:
692 # If it's already a settings reference, error
693 if hasattr(kwargs["to"], "setting_name"):
694 if kwargs["to"].setting_name != swappable_setting:
695 raise ValueError(
696 "Cannot deconstruct a ForeignKey pointing to a model "
697 "that is swapped in place of more than one model (%s and %s)"
698 % (kwargs["to"].setting_name, swappable_setting)
699 )
700 # Set it
701 kwargs["to"] = SettingsReference(
702 kwargs["to"],
703 swappable_setting,
704 )
705 return name, path, args, kwargs
707 def resolve_related_fields(self):
708 if not self.from_fields or len(self.from_fields) != len(self.to_fields):
709 raise ValueError(
710 "Foreign Object from and to fields must be the same non-zero length"
711 )
712 if isinstance(self.remote_field.model, str):
713 raise ValueError(
714 "Related model %r cannot be resolved" % self.remote_field.model
715 )
716 related_fields = []
717 for index in range(len(self.from_fields)):
718 from_field_name = self.from_fields[index]
719 to_field_name = self.to_fields[index]
720 from_field = (
721 self
722 if from_field_name == RECURSIVE_RELATIONSHIP_CONSTANT
723 else self.opts.get_field(from_field_name)
724 )
725 to_field = (
726 self.remote_field.model._meta.pk
727 if to_field_name is None
728 else self.remote_field.model._meta.get_field(to_field_name)
729 )
730 related_fields.append((from_field, to_field))
731 return related_fields
733 @cached_property
734 def related_fields(self):
735 return self.resolve_related_fields()
737 @cached_property
738 def reverse_related_fields(self):
739 return [(rhs_field, lhs_field) for lhs_field, rhs_field in self.related_fields]
741 @cached_property
742 def local_related_fields(self):
743 return tuple(lhs_field for lhs_field, rhs_field in self.related_fields)
745 @cached_property
746 def foreign_related_fields(self):
747 return tuple(
748 rhs_field for lhs_field, rhs_field in self.related_fields if rhs_field
749 )
751 def get_local_related_value(self, instance):
752 return self.get_instance_value_for_fields(instance, self.local_related_fields)
754 def get_foreign_related_value(self, instance):
755 return self.get_instance_value_for_fields(instance, self.foreign_related_fields)
757 @staticmethod
758 def get_instance_value_for_fields(instance, fields):
759 ret = []
760 opts = instance._meta
761 for field in fields:
762 # Gotcha: in some cases (like fixture loading) a model can have
763 # different values in parent_ptr_id and parent's id. So, use
764 # instance.pk (that is, parent_ptr_id) when asked for instance.id.
765 if field.primary_key:
766 possible_parent_link = opts.get_ancestor_link(field.model)
767 if (
768 not possible_parent_link
769 or possible_parent_link.primary_key
770 or possible_parent_link.model._meta.abstract
771 ):
772 ret.append(instance.pk)
773 continue
774 ret.append(getattr(instance, field.attname))
775 return tuple(ret)
777 def get_attname_column(self):
778 attname, column = super().get_attname_column()
779 return attname, None
781 def get_joining_columns(self, reverse_join=False):
782 source = self.reverse_related_fields if reverse_join else self.related_fields
783 return tuple(
784 (lhs_field.column, rhs_field.column) for lhs_field, rhs_field in source
785 )
787 def get_reverse_joining_columns(self):
788 return self.get_joining_columns(reverse_join=True)
790 def get_extra_descriptor_filter(self, instance):
791 """
792 Return an extra filter condition for related object fetching when
793 user does 'instance.fieldname', that is the extra filter is used in
794 the descriptor of the field.
796 The filter should be either a dict usable in .filter(**kwargs) call or
797 a Q-object. The condition will be ANDed together with the relation's
798 joining columns.
800 A parallel method is get_extra_restriction() which is used in
801 JOIN and subquery conditions.
802 """
803 return {}
805 def get_extra_restriction(self, alias, related_alias):
806 """
807 Return a pair condition used for joining and subquery pushdown. The
808 condition is something that responds to as_sql(compiler, connection)
809 method.
811 Note that currently referring both the 'alias' and 'related_alias'
812 will not work in some conditions, like subquery pushdown.
814 A parallel method is get_extra_descriptor_filter() which is used in
815 instance.fieldname related object fetching.
816 """
817 return None
819 def get_path_info(self, filtered_relation=None):
820 """Get path from this field to the related model."""
821 opts = self.remote_field.model._meta
822 from_opts = self.model._meta
823 return [
824 PathInfo(
825 from_opts=from_opts,
826 to_opts=opts,
827 target_fields=self.foreign_related_fields,
828 join_field=self,
829 m2m=False,
830 direct=True,
831 filtered_relation=filtered_relation,
832 )
833 ]
835 @cached_property
836 def path_infos(self):
837 return self.get_path_info()
839 def get_reverse_path_info(self, filtered_relation=None):
840 """Get path from the related model to this field's model."""
841 opts = self.model._meta
842 from_opts = self.remote_field.model._meta
843 return [
844 PathInfo(
845 from_opts=from_opts,
846 to_opts=opts,
847 target_fields=(opts.pk,),
848 join_field=self.remote_field,
849 m2m=not self.unique,
850 direct=False,
851 filtered_relation=filtered_relation,
852 )
853 ]
855 @cached_property
856 def reverse_path_infos(self):
857 return self.get_reverse_path_info()
859 @classmethod
860 @functools.lru_cache(maxsize=None)
861 def get_class_lookups(cls):
862 bases = inspect.getmro(cls)
863 bases = bases[: bases.index(ForeignObject) + 1]
864 class_lookups = [parent.__dict__.get("class_lookups", {}) for parent in bases]
865 return cls.merge_dicts(class_lookups)
867 def contribute_to_class(self, cls, name, private_only=False, **kwargs):
868 super().contribute_to_class(cls, name, private_only=private_only, **kwargs)
869 setattr(cls, self.name, self.forward_related_accessor_class(self))
871 def contribute_to_related_class(self, cls, related):
872 # Internal FK's - i.e., those with a related name ending with '+' -
873 # and swapped models don't get a related descriptor.
874 if (
875 not self.remote_field.is_hidden()
876 and not related.related_model._meta.swapped
877 ):
878 setattr(
879 cls._meta.concrete_model,
880 related.get_accessor_name(),
881 self.related_accessor_class(related),
882 )
883 # While 'limit_choices_to' might be a callable, simply pass
884 # it along for later - this is too early because it's still
885 # model load time.
886 if self.remote_field.limit_choices_to:
887 cls._meta.related_fkey_lookups.append(
888 self.remote_field.limit_choices_to
889 )
892ForeignObject.register_lookup(RelatedIn)
893ForeignObject.register_lookup(RelatedExact)
894ForeignObject.register_lookup(RelatedLessThan)
895ForeignObject.register_lookup(RelatedGreaterThan)
896ForeignObject.register_lookup(RelatedGreaterThanOrEqual)
897ForeignObject.register_lookup(RelatedLessThanOrEqual)
898ForeignObject.register_lookup(RelatedIsNull)
901class ForeignKey(ForeignObject):
902 """
903 Provide a many-to-one relation by adding a column to the local model
904 to hold the remote value.
906 By default ForeignKey will target the pk of the remote model but this
907 behavior can be changed by using the ``to_field`` argument.
908 """
910 descriptor_class = ForeignKeyDeferredAttribute
911 # Field flags
912 many_to_many = False
913 many_to_one = True
914 one_to_many = False
915 one_to_one = False
917 rel_class = ManyToOneRel
919 empty_strings_allowed = False
920 default_error_messages = {
921 "invalid": _("%(model)s instance with %(field)s %(value)r does not exist.")
922 }
923 description = _("Foreign Key (type determined by related field)")
925 def __init__(
926 self,
927 to,
928 on_delete,
929 related_name=None,
930 related_query_name=None,
931 limit_choices_to=None,
932 parent_link=False,
933 to_field=None,
934 db_constraint=True,
935 **kwargs,
936 ):
937 try:
938 to._meta.model_name
939 except AttributeError:
940 if not isinstance(to, str):
941 raise TypeError(
942 "%s(%r) is invalid. First parameter to ForeignKey must be "
943 "either a model, a model name, or the string %r"
944 % (
945 self.__class__.__name__,
946 to,
947 RECURSIVE_RELATIONSHIP_CONSTANT,
948 )
949 )
950 else:
951 # For backwards compatibility purposes, we need to *try* and set
952 # the to_field during FK construction. It won't be guaranteed to
953 # be correct until contribute_to_class is called. Refs #12190.
954 to_field = to_field or (to._meta.pk and to._meta.pk.name)
955 if not callable(on_delete):
956 raise TypeError("on_delete must be callable.")
958 kwargs["rel"] = self.rel_class(
959 self,
960 to,
961 to_field,
962 related_name=related_name,
963 related_query_name=related_query_name,
964 limit_choices_to=limit_choices_to,
965 parent_link=parent_link,
966 on_delete=on_delete,
967 )
968 kwargs.setdefault("db_index", True)
970 super().__init__(
971 to,
972 on_delete,
973 related_name=related_name,
974 related_query_name=related_query_name,
975 limit_choices_to=limit_choices_to,
976 from_fields=[RECURSIVE_RELATIONSHIP_CONSTANT],
977 to_fields=[to_field],
978 **kwargs,
979 )
980 self.db_constraint = db_constraint
982 def __class_getitem__(cls, *args, **kwargs):
983 return cls
985 def check(self, **kwargs):
986 return [
987 *super().check(**kwargs),
988 *self._check_on_delete(),
989 *self._check_unique(),
990 ]
992 def _check_on_delete(self):
993 on_delete = getattr(self.remote_field, "on_delete", None)
994 if on_delete == SET_NULL and not self.null:
995 return [
996 checks.Error(
997 "Field specifies on_delete=SET_NULL, but cannot be null.",
998 hint=(
999 "Set null=True argument on the field, or change the on_delete "
1000 "rule."
1001 ),
1002 obj=self,
1003 id="fields.E320",
1004 )
1005 ]
1006 elif on_delete == SET_DEFAULT and not self.has_default():
1007 return [
1008 checks.Error(
1009 "Field specifies on_delete=SET_DEFAULT, but has no default value.",
1010 hint="Set a default value, or change the on_delete rule.",
1011 obj=self,
1012 id="fields.E321",
1013 )
1014 ]
1015 else:
1016 return []
1018 def _check_unique(self, **kwargs):
1019 return (
1020 [
1021 checks.Warning(
1022 "Setting unique=True on a ForeignKey has the same effect as using "
1023 "a OneToOneField.",
1024 hint=(
1025 "ForeignKey(unique=True) is usually better served by a "
1026 "OneToOneField."
1027 ),
1028 obj=self,
1029 id="fields.W342",
1030 )
1031 ]
1032 if self.unique
1033 else []
1034 )
1036 def deconstruct(self):
1037 name, path, args, kwargs = super().deconstruct()
1038 del kwargs["to_fields"]
1039 del kwargs["from_fields"]
1040 # Handle the simpler arguments
1041 if self.db_index:
1042 del kwargs["db_index"]
1043 else:
1044 kwargs["db_index"] = False
1045 if self.db_constraint is not True:
1046 kwargs["db_constraint"] = self.db_constraint
1047 # Rel needs more work.
1048 to_meta = getattr(self.remote_field.model, "_meta", None)
1049 if self.remote_field.field_name and (
1050 not to_meta
1051 or (to_meta.pk and self.remote_field.field_name != to_meta.pk.name)
1052 ):
1053 kwargs["to_field"] = self.remote_field.field_name
1054 return name, path, args, kwargs
1056 def to_python(self, value):
1057 return self.target_field.to_python(value)
1059 @property
1060 def target_field(self):
1061 return self.foreign_related_fields[0]
1063 def validate(self, value, model_instance):
1064 if self.remote_field.parent_link:
1065 return
1066 super().validate(value, model_instance)
1067 if value is None:
1068 return
1070 using = router.db_for_read(self.remote_field.model, instance=model_instance)
1071 qs = self.remote_field.model._base_manager.using(using).filter(
1072 **{self.remote_field.field_name: value}
1073 )
1074 qs = qs.complex_filter(self.get_limit_choices_to())
1075 if not qs.exists():
1076 raise exceptions.ValidationError(
1077 self.error_messages["invalid"],
1078 code="invalid",
1079 params={
1080 "model": self.remote_field.model._meta.verbose_name,
1081 "pk": value,
1082 "field": self.remote_field.field_name,
1083 "value": value,
1084 }, # 'pk' is included for backwards compatibility
1085 )
1087 def resolve_related_fields(self):
1088 related_fields = super().resolve_related_fields()
1089 for from_field, to_field in related_fields:
1090 if (
1091 to_field
1092 and to_field.model != self.remote_field.model._meta.concrete_model
1093 ):
1094 raise exceptions.FieldError(
1095 "'%s.%s' refers to field '%s' which is not local to model "
1096 "'%s'."
1097 % (
1098 self.model._meta.label,
1099 self.name,
1100 to_field.name,
1101 self.remote_field.model._meta.concrete_model._meta.label,
1102 )
1103 )
1104 return related_fields
1106 def get_attname(self):
1107 return "%s_id" % self.name
1109 def get_attname_column(self):
1110 attname = self.get_attname()
1111 column = self.db_column or attname
1112 return attname, column
1114 def get_default(self):
1115 """Return the to_field if the default value is an object."""
1116 field_default = super().get_default()
1117 if isinstance(field_default, self.remote_field.model):
1118 return getattr(field_default, self.target_field.attname)
1119 return field_default
1121 def get_db_prep_save(self, value, connection):
1122 if value is None or (
1123 value == ""
1124 and (
1125 not self.target_field.empty_strings_allowed
1126 or connection.features.interprets_empty_strings_as_nulls
1127 )
1128 ):
1129 return None
1130 else:
1131 return self.target_field.get_db_prep_save(value, connection=connection)
1133 def get_db_prep_value(self, value, connection, prepared=False):
1134 return self.target_field.get_db_prep_value(value, connection, prepared)
1136 def get_prep_value(self, value):
1137 return self.target_field.get_prep_value(value)
1139 def contribute_to_related_class(self, cls, related):
1140 super().contribute_to_related_class(cls, related)
1141 if self.remote_field.field_name is None:
1142 self.remote_field.field_name = cls._meta.pk.name
1144 def formfield(self, *, using=None, **kwargs):
1145 if isinstance(self.remote_field.model, str):
1146 raise ValueError(
1147 "Cannot create form field for %r yet, because "
1148 "its related model %r has not been loaded yet"
1149 % (self.name, self.remote_field.model)
1150 )
1151 return super().formfield(
1152 **{
1153 "form_class": forms.ModelChoiceField,
1154 "queryset": self.remote_field.model._default_manager.using(using),
1155 "to_field_name": self.remote_field.field_name,
1156 **kwargs,
1157 "blank": self.blank,
1158 }
1159 )
1161 def db_check(self, connection):
1162 return None
1164 def db_type(self, connection):
1165 return self.target_field.rel_db_type(connection=connection)
1167 def db_parameters(self, connection):
1168 target_db_parameters = self.target_field.db_parameters(connection)
1169 return {
1170 "type": self.db_type(connection),
1171 "check": self.db_check(connection),
1172 "collation": target_db_parameters.get("collation"),
1173 }
1175 def convert_empty_strings(self, value, expression, connection):
1176 if (not value) and isinstance(value, str):
1177 return None
1178 return value
1180 def get_db_converters(self, connection):
1181 converters = super().get_db_converters(connection)
1182 if connection.features.interprets_empty_strings_as_nulls:
1183 converters += [self.convert_empty_strings]
1184 return converters
1186 def get_col(self, alias, output_field=None):
1187 if output_field is None:
1188 output_field = self.target_field
1189 while isinstance(output_field, ForeignKey):
1190 output_field = output_field.target_field
1191 if output_field is self:
1192 raise ValueError("Cannot resolve output_field.")
1193 return super().get_col(alias, output_field)
1196class OneToOneField(ForeignKey):
1197 """
1198 A OneToOneField is essentially the same as a ForeignKey, with the exception
1199 that it always carries a "unique" constraint with it and the reverse
1200 relation always returns the object pointed to (since there will only ever
1201 be one), rather than returning a list.
1202 """
1204 # Field flags
1205 many_to_many = False
1206 many_to_one = False
1207 one_to_many = False
1208 one_to_one = True
1210 related_accessor_class = ReverseOneToOneDescriptor
1211 forward_related_accessor_class = ForwardOneToOneDescriptor
1212 rel_class = OneToOneRel
1214 description = _("One-to-one relationship")
1216 def __init__(self, to, on_delete, to_field=None, **kwargs):
1217 kwargs["unique"] = True
1218 super().__init__(to, on_delete, to_field=to_field, **kwargs)
1220 def deconstruct(self):
1221 name, path, args, kwargs = super().deconstruct()
1222 if "unique" in kwargs:
1223 del kwargs["unique"]
1224 return name, path, args, kwargs
1226 def formfield(self, **kwargs):
1227 if self.remote_field.parent_link:
1228 return None
1229 return super().formfield(**kwargs)
1231 def save_form_data(self, instance, data):
1232 if isinstance(data, self.remote_field.model):
1233 setattr(instance, self.name, data)
1234 else:
1235 setattr(instance, self.attname, data)
1236 # Remote field object must be cleared otherwise Model.save()
1237 # will reassign attname using the related object pk.
1238 if data is None:
1239 setattr(instance, self.name, data)
1241 def _check_unique(self, **kwargs):
1242 # Override ForeignKey since check isn't applicable here.
1243 return []
1246def create_many_to_many_intermediary_model(field, klass):
1247 from django.db import models
1249 def set_managed(model, related, through):
1250 through._meta.managed = model._meta.managed or related._meta.managed
1252 to_model = resolve_relation(klass, field.remote_field.model)
1253 name = "%s_%s" % (klass._meta.object_name, field.name)
1254 lazy_related_operation(set_managed, klass, to_model, name)
1256 to = make_model_tuple(to_model)[1]
1257 from_ = klass._meta.model_name
1258 if to == from_:
1259 to = "to_%s" % to
1260 from_ = "from_%s" % from_
1262 meta = type(
1263 "Meta",
1264 (),
1265 {
1266 "db_table": field._get_m2m_db_table(klass._meta),
1267 "auto_created": klass,
1268 "app_label": klass._meta.app_label,
1269 "db_tablespace": klass._meta.db_tablespace,
1270 "unique_together": (from_, to),
1271 "verbose_name": _("%(from)s-%(to)s relationship")
1272 % {"from": from_, "to": to},
1273 "verbose_name_plural": _("%(from)s-%(to)s relationships")
1274 % {"from": from_, "to": to},
1275 "apps": field.model._meta.apps,
1276 },
1277 )
1278 # Construct and return the new class.
1279 return type(
1280 name,
1281 (models.Model,),
1282 {
1283 "Meta": meta,
1284 "__module__": klass.__module__,
1285 from_: models.ForeignKey(
1286 klass,
1287 related_name="%s+" % name,
1288 db_tablespace=field.db_tablespace,
1289 db_constraint=field.remote_field.db_constraint,
1290 on_delete=CASCADE,
1291 ),
1292 to: models.ForeignKey(
1293 to_model,
1294 related_name="%s+" % name,
1295 db_tablespace=field.db_tablespace,
1296 db_constraint=field.remote_field.db_constraint,
1297 on_delete=CASCADE,
1298 ),
1299 },
1300 )
1303class ManyToManyField(RelatedField):
1304 """
1305 Provide a many-to-many relation by using an intermediary model that
1306 holds two ForeignKey fields pointed at the two sides of the relation.
1308 Unless a ``through`` model was provided, ManyToManyField will use the
1309 create_many_to_many_intermediary_model factory to automatically generate
1310 the intermediary model.
1311 """
1313 # Field flags
1314 many_to_many = True
1315 many_to_one = False
1316 one_to_many = False
1317 one_to_one = False
1319 rel_class = ManyToManyRel
1321 description = _("Many-to-many relationship")
1323 def __init__(
1324 self,
1325 to,
1326 related_name=None,
1327 related_query_name=None,
1328 limit_choices_to=None,
1329 symmetrical=None,
1330 through=None,
1331 through_fields=None,
1332 db_constraint=True,
1333 db_table=None,
1334 swappable=True,
1335 **kwargs,
1336 ):
1337 try:
1338 to._meta
1339 except AttributeError:
1340 if not isinstance(to, str):
1341 raise TypeError(
1342 "%s(%r) is invalid. First parameter to ManyToManyField "
1343 "must be either a model, a model name, or the string %r"
1344 % (
1345 self.__class__.__name__,
1346 to,
1347 RECURSIVE_RELATIONSHIP_CONSTANT,
1348 )
1349 )
1351 if symmetrical is None:
1352 symmetrical = to == RECURSIVE_RELATIONSHIP_CONSTANT
1354 if through is not None and db_table is not None:
1355 raise ValueError(
1356 "Cannot specify a db_table if an intermediary model is used."
1357 )
1359 kwargs["rel"] = self.rel_class(
1360 self,
1361 to,
1362 related_name=related_name,
1363 related_query_name=related_query_name,
1364 limit_choices_to=limit_choices_to,
1365 symmetrical=symmetrical,
1366 through=through,
1367 through_fields=through_fields,
1368 db_constraint=db_constraint,
1369 )
1370 self.has_null_arg = "null" in kwargs
1372 super().__init__(
1373 related_name=related_name,
1374 related_query_name=related_query_name,
1375 limit_choices_to=limit_choices_to,
1376 **kwargs,
1377 )
1379 self.db_table = db_table
1380 self.swappable = swappable
1382 def check(self, **kwargs):
1383 return [
1384 *super().check(**kwargs),
1385 *self._check_unique(**kwargs),
1386 *self._check_relationship_model(**kwargs),
1387 *self._check_ignored_options(**kwargs),
1388 *self._check_table_uniqueness(**kwargs),
1389 ]
1391 def _check_unique(self, **kwargs):
1392 if self.unique:
1393 return [
1394 checks.Error(
1395 "ManyToManyFields cannot be unique.",
1396 obj=self,
1397 id="fields.E330",
1398 )
1399 ]
1400 return []
1402 def _check_ignored_options(self, **kwargs):
1403 warnings = []
1405 if self.has_null_arg:
1406 warnings.append(
1407 checks.Warning(
1408 "null has no effect on ManyToManyField.",
1409 obj=self,
1410 id="fields.W340",
1411 )
1412 )
1414 if self._validators:
1415 warnings.append(
1416 checks.Warning(
1417 "ManyToManyField does not support validators.",
1418 obj=self,
1419 id="fields.W341",
1420 )
1421 )
1422 if self.remote_field.symmetrical and self._related_name:
1423 warnings.append(
1424 checks.Warning(
1425 "related_name has no effect on ManyToManyField "
1426 'with a symmetrical relationship, e.g. to "self".',
1427 obj=self,
1428 id="fields.W345",
1429 )
1430 )
1431 if self.db_comment:
1432 warnings.append(
1433 checks.Warning(
1434 "db_comment has no effect on ManyToManyField.",
1435 obj=self,
1436 id="fields.W346",
1437 )
1438 )
1440 return warnings
1442 def _check_relationship_model(self, from_model=None, **kwargs):
1443 if hasattr(self.remote_field.through, "_meta"):
1444 qualified_model_name = "%s.%s" % (
1445 self.remote_field.through._meta.app_label,
1446 self.remote_field.through.__name__,
1447 )
1448 else:
1449 qualified_model_name = self.remote_field.through
1451 errors = []
1453 if self.remote_field.through not in self.opts.apps.get_models(
1454 include_auto_created=True
1455 ):
1456 # The relationship model is not installed.
1457 errors.append(
1458 checks.Error(
1459 "Field specifies a many-to-many relation through model "
1460 "'%s', which has not been installed." % qualified_model_name,
1461 obj=self,
1462 id="fields.E331",
1463 )
1464 )
1466 else:
1467 assert from_model is not None, (
1468 "ManyToManyField with intermediate "
1469 "tables cannot be checked if you don't pass the model "
1470 "where the field is attached to."
1471 )
1472 # Set some useful local variables
1473 to_model = resolve_relation(from_model, self.remote_field.model)
1474 from_model_name = from_model._meta.object_name
1475 if isinstance(to_model, str):
1476 to_model_name = to_model
1477 else:
1478 to_model_name = to_model._meta.object_name
1479 relationship_model_name = self.remote_field.through._meta.object_name
1480 self_referential = from_model == to_model
1481 # Count foreign keys in intermediate model
1482 if self_referential:
1483 seen_self = sum(
1484 from_model == getattr(field.remote_field, "model", None)
1485 for field in self.remote_field.through._meta.fields
1486 )
1488 if seen_self > 2 and not self.remote_field.through_fields:
1489 errors.append(
1490 checks.Error(
1491 "The model is used as an intermediate model by "
1492 "'%s', but it has more than two foreign keys "
1493 "to '%s', which is ambiguous. You must specify "
1494 "which two foreign keys Django should use via the "
1495 "through_fields keyword argument."
1496 % (self, from_model_name),
1497 hint=(
1498 "Use through_fields to specify which two foreign keys "
1499 "Django should use."
1500 ),
1501 obj=self.remote_field.through,
1502 id="fields.E333",
1503 )
1504 )
1506 else:
1507 # Count foreign keys in relationship model
1508 seen_from = sum(
1509 from_model == getattr(field.remote_field, "model", None)
1510 for field in self.remote_field.through._meta.fields
1511 )
1512 seen_to = sum(
1513 to_model == getattr(field.remote_field, "model", None)
1514 for field in self.remote_field.through._meta.fields
1515 )
1517 if seen_from > 1 and not self.remote_field.through_fields:
1518 errors.append(
1519 checks.Error(
1520 (
1521 "The model is used as an intermediate model by "
1522 "'%s', but it has more than one foreign key "
1523 "from '%s', which is ambiguous. You must specify "
1524 "which foreign key Django should use via the "
1525 "through_fields keyword argument."
1526 )
1527 % (self, from_model_name),
1528 hint=(
1529 "If you want to create a recursive relationship, "
1530 'use ManyToManyField("%s", through="%s").'
1531 )
1532 % (
1533 RECURSIVE_RELATIONSHIP_CONSTANT,
1534 relationship_model_name,
1535 ),
1536 obj=self,
1537 id="fields.E334",
1538 )
1539 )
1541 if seen_to > 1 and not self.remote_field.through_fields:
1542 errors.append(
1543 checks.Error(
1544 "The model is used as an intermediate model by "
1545 "'%s', but it has more than one foreign key "
1546 "to '%s', which is ambiguous. You must specify "
1547 "which foreign key Django should use via the "
1548 "through_fields keyword argument." % (self, to_model_name),
1549 hint=(
1550 "If you want to create a recursive relationship, "
1551 'use ManyToManyField("%s", through="%s").'
1552 )
1553 % (
1554 RECURSIVE_RELATIONSHIP_CONSTANT,
1555 relationship_model_name,
1556 ),
1557 obj=self,
1558 id="fields.E335",
1559 )
1560 )
1562 if seen_from == 0 or seen_to == 0:
1563 errors.append(
1564 checks.Error(
1565 "The model is used as an intermediate model by "
1566 "'%s', but it does not have a foreign key to '%s' or '%s'."
1567 % (self, from_model_name, to_model_name),
1568 obj=self.remote_field.through,
1569 id="fields.E336",
1570 )
1571 )
1573 # Validate `through_fields`.
1574 if self.remote_field.through_fields is not None:
1575 # Validate that we're given an iterable of at least two items
1576 # and that none of them is "falsy".
1577 if not (
1578 len(self.remote_field.through_fields) >= 2
1579 and self.remote_field.through_fields[0]
1580 and self.remote_field.through_fields[1]
1581 ):
1582 errors.append(
1583 checks.Error(
1584 "Field specifies 'through_fields' but does not provide "
1585 "the names of the two link fields that should be used "
1586 "for the relation through model '%s'." % qualified_model_name,
1587 hint=(
1588 "Make sure you specify 'through_fields' as "
1589 "through_fields=('field1', 'field2')"
1590 ),
1591 obj=self,
1592 id="fields.E337",
1593 )
1594 )
1596 # Validate the given through fields -- they should be actual
1597 # fields on the through model, and also be foreign keys to the
1598 # expected models.
1599 else:
1600 assert from_model is not None, (
1601 "ManyToManyField with intermediate "
1602 "tables cannot be checked if you don't pass the model "
1603 "where the field is attached to."
1604 )
1606 source, through, target = (
1607 from_model,
1608 self.remote_field.through,
1609 self.remote_field.model,
1610 )
1611 source_field_name, target_field_name = self.remote_field.through_fields[
1612 :2
1613 ]
1615 for field_name, related_model in (
1616 (source_field_name, source),
1617 (target_field_name, target),
1618 ):
1620 possible_field_names = []
1621 for f in through._meta.fields:
1622 if (
1623 hasattr(f, "remote_field")
1624 and getattr(f.remote_field, "model", None) == related_model
1625 ):
1626 possible_field_names.append(f.name)
1627 if possible_field_names:
1628 hint = (
1629 "Did you mean one of the following foreign keys to '%s': "
1630 "%s?"
1631 % (
1632 related_model._meta.object_name,
1633 ", ".join(possible_field_names),
1634 )
1635 )
1636 else:
1637 hint = None
1639 try:
1640 field = through._meta.get_field(field_name)
1641 except exceptions.FieldDoesNotExist:
1642 errors.append(
1643 checks.Error(
1644 "The intermediary model '%s' has no field '%s'."
1645 % (qualified_model_name, field_name),
1646 hint=hint,
1647 obj=self,
1648 id="fields.E338",
1649 )
1650 )
1651 else:
1652 if not (
1653 hasattr(field, "remote_field")
1654 and getattr(field.remote_field, "model", None)
1655 == related_model
1656 ):
1657 errors.append(
1658 checks.Error(
1659 "'%s.%s' is not a foreign key to '%s'."
1660 % (
1661 through._meta.object_name,
1662 field_name,
1663 related_model._meta.object_name,
1664 ),
1665 hint=hint,
1666 obj=self,
1667 id="fields.E339",
1668 )
1669 )
1671 return errors
1673 def _check_table_uniqueness(self, **kwargs):
1674 if (
1675 isinstance(self.remote_field.through, str)
1676 or not self.remote_field.through._meta.managed
1677 ):
1678 return []
1679 registered_tables = {
1680 model._meta.db_table: model
1681 for model in self.opts.apps.get_models(include_auto_created=True)
1682 if model != self.remote_field.through and model._meta.managed
1683 }
1684 m2m_db_table = self.m2m_db_table()
1685 model = registered_tables.get(m2m_db_table)
1686 # The second condition allows multiple m2m relations on a model if
1687 # some point to a through model that proxies another through model.
1688 if (
1689 model
1690 and model._meta.concrete_model
1691 != self.remote_field.through._meta.concrete_model
1692 ):
1693 if model._meta.auto_created:
1695 def _get_field_name(model):
1696 for field in model._meta.auto_created._meta.many_to_many:
1697 if field.remote_field.through is model:
1698 return field.name
1700 opts = model._meta.auto_created._meta
1701 clashing_obj = "%s.%s" % (opts.label, _get_field_name(model))
1702 else:
1703 clashing_obj = model._meta.label
1704 if settings.DATABASE_ROUTERS:
1705 error_class, error_id = checks.Warning, "fields.W344"
1706 error_hint = (
1707 "You have configured settings.DATABASE_ROUTERS. Verify "
1708 "that the table of %r is correctly routed to a separate "
1709 "database." % clashing_obj
1710 )
1711 else:
1712 error_class, error_id = checks.Error, "fields.E340"
1713 error_hint = None
1714 return [
1715 error_class(
1716 "The field's intermediary table '%s' clashes with the "
1717 "table name of '%s'." % (m2m_db_table, clashing_obj),
1718 obj=self,
1719 hint=error_hint,
1720 id=error_id,
1721 )
1722 ]
1723 return []
1725 def deconstruct(self):
1726 name, path, args, kwargs = super().deconstruct()
1727 # Handle the simpler arguments.
1728 if self.db_table is not None:
1729 kwargs["db_table"] = self.db_table
1730 if self.remote_field.db_constraint is not True:
1731 kwargs["db_constraint"] = self.remote_field.db_constraint
1732 # Lowercase model names as they should be treated as case-insensitive.
1733 if isinstance(self.remote_field.model, str):
1734 if "." in self.remote_field.model:
1735 app_label, model_name = self.remote_field.model.split(".")
1736 kwargs["to"] = "%s.%s" % (app_label, model_name.lower())
1737 else:
1738 kwargs["to"] = self.remote_field.model.lower()
1739 else:
1740 kwargs["to"] = self.remote_field.model._meta.label_lower
1741 if getattr(self.remote_field, "through", None) is not None:
1742 if isinstance(self.remote_field.through, str):
1743 kwargs["through"] = self.remote_field.through
1744 elif not self.remote_field.through._meta.auto_created:
1745 kwargs["through"] = self.remote_field.through._meta.label
1746 # If swappable is True, then see if we're actually pointing to the target
1747 # of a swap.
1748 swappable_setting = self.swappable_setting
1749 if swappable_setting is not None:
1750 # If it's already a settings reference, error.
1751 if hasattr(kwargs["to"], "setting_name"):
1752 if kwargs["to"].setting_name != swappable_setting:
1753 raise ValueError(
1754 "Cannot deconstruct a ManyToManyField pointing to a "
1755 "model that is swapped in place of more than one model "
1756 "(%s and %s)" % (kwargs["to"].setting_name, swappable_setting)
1757 )
1759 kwargs["to"] = SettingsReference(
1760 kwargs["to"],
1761 swappable_setting,
1762 )
1763 return name, path, args, kwargs
1765 def _get_path_info(self, direct=False, filtered_relation=None):
1766 """Called by both direct and indirect m2m traversal."""
1767 int_model = self.remote_field.through
1768 linkfield1 = int_model._meta.get_field(self.m2m_field_name())
1769 linkfield2 = int_model._meta.get_field(self.m2m_reverse_field_name())
1770 if direct:
1771 join1infos = linkfield1.reverse_path_infos
1772 if filtered_relation:
1773 join2infos = linkfield2.get_path_info(filtered_relation)
1774 else:
1775 join2infos = linkfield2.path_infos
1776 else:
1777 join1infos = linkfield2.reverse_path_infos
1778 if filtered_relation:
1779 join2infos = linkfield1.get_path_info(filtered_relation)
1780 else:
1781 join2infos = linkfield1.path_infos
1782 # Get join infos between the last model of join 1 and the first model
1783 # of join 2. Assume the only reason these may differ is due to model
1784 # inheritance.
1785 join1_final = join1infos[-1].to_opts
1786 join2_initial = join2infos[0].from_opts
1787 if join1_final is join2_initial:
1788 intermediate_infos = []
1789 elif issubclass(join1_final.model, join2_initial.model):
1790 intermediate_infos = join1_final.get_path_to_parent(join2_initial.model)
1791 else:
1792 intermediate_infos = join2_initial.get_path_from_parent(join1_final.model)
1794 return [*join1infos, *intermediate_infos, *join2infos]
1796 def get_path_info(self, filtered_relation=None):
1797 return self._get_path_info(direct=True, filtered_relation=filtered_relation)
1799 @cached_property
1800 def path_infos(self):
1801 return self.get_path_info()
1803 def get_reverse_path_info(self, filtered_relation=None):
1804 return self._get_path_info(direct=False, filtered_relation=filtered_relation)
1806 @cached_property
1807 def reverse_path_infos(self):
1808 return self.get_reverse_path_info()
1810 def _get_m2m_db_table(self, opts):
1811 """
1812 Function that can be curried to provide the m2m table name for this
1813 relation.
1814 """
1815 if self.remote_field.through is not None:
1816 return self.remote_field.through._meta.db_table
1817 elif self.db_table:
1818 return self.db_table
1819 else:
1820 m2m_table_name = "%s_%s" % (utils.strip_quotes(opts.db_table), self.name)
1821 return utils.truncate_name(m2m_table_name, connection.ops.max_name_length())
1823 def _get_m2m_attr(self, related, attr):
1824 """
1825 Function that can be curried to provide the source accessor or DB
1826 column name for the m2m table.
1827 """
1828 cache_attr = "_m2m_%s_cache" % attr
1829 if hasattr(self, cache_attr):
1830 return getattr(self, cache_attr)
1831 if self.remote_field.through_fields is not None:
1832 link_field_name = self.remote_field.through_fields[0]
1833 else:
1834 link_field_name = None
1835 for f in self.remote_field.through._meta.fields:
1836 if (
1837 f.is_relation
1838 and f.remote_field.model == related.related_model
1839 and (link_field_name is None or link_field_name == f.name)
1840 ):
1841 setattr(self, cache_attr, getattr(f, attr))
1842 return getattr(self, cache_attr)
1844 def _get_m2m_reverse_attr(self, related, attr):
1845 """
1846 Function that can be curried to provide the related accessor or DB
1847 column name for the m2m table.
1848 """
1849 cache_attr = "_m2m_reverse_%s_cache" % attr
1850 if hasattr(self, cache_attr):
1851 return getattr(self, cache_attr)
1852 found = False
1853 if self.remote_field.through_fields is not None:
1854 link_field_name = self.remote_field.through_fields[1]
1855 else:
1856 link_field_name = None
1857 for f in self.remote_field.through._meta.fields:
1858 if f.is_relation and f.remote_field.model == related.model:
1859 if link_field_name is None and related.related_model == related.model:
1860 # If this is an m2m-intermediate to self,
1861 # the first foreign key you find will be
1862 # the source column. Keep searching for
1863 # the second foreign key.
1864 if found:
1865 setattr(self, cache_attr, getattr(f, attr))
1866 break
1867 else:
1868 found = True
1869 elif link_field_name is None or link_field_name == f.name:
1870 setattr(self, cache_attr, getattr(f, attr))
1871 break
1872 return getattr(self, cache_attr)
1874 def contribute_to_class(self, cls, name, **kwargs):
1875 # To support multiple relations to self, it's useful to have a non-None
1876 # related name on symmetrical relations for internal reasons. The
1877 # concept doesn't make a lot of sense externally ("you want me to
1878 # specify *what* on my non-reversible relation?!"), so we set it up
1879 # automatically. The funky name reduces the chance of an accidental
1880 # clash.
1881 if self.remote_field.symmetrical and (
1882 self.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT
1883 or self.remote_field.model == cls._meta.object_name
1884 ):
1885 self.remote_field.related_name = "%s_rel_+" % name
1886 elif self.remote_field.is_hidden():
1887 # If the backwards relation is disabled, replace the original
1888 # related_name with one generated from the m2m field name. Django
1889 # still uses backwards relations internally and we need to avoid
1890 # clashes between multiple m2m fields with related_name == '+'.
1891 self.remote_field.related_name = "_%s_%s_%s_+" % (
1892 cls._meta.app_label,
1893 cls.__name__.lower(),
1894 name,
1895 )
1897 super().contribute_to_class(cls, name, **kwargs)
1899 # The intermediate m2m model is not auto created if:
1900 # 1) There is a manually specified intermediate, or
1901 # 2) The class owning the m2m field is abstract.
1902 # 3) The class owning the m2m field has been swapped out.
1903 if not cls._meta.abstract:
1904 if self.remote_field.through:
1906 def resolve_through_model(_, model, field):
1907 field.remote_field.through = model
1909 lazy_related_operation(
1910 resolve_through_model, cls, self.remote_field.through, field=self
1911 )
1912 elif not cls._meta.swapped:
1913 self.remote_field.through = create_many_to_many_intermediary_model(
1914 self, cls
1915 )
1917 # Add the descriptor for the m2m relation.
1918 setattr(cls, self.name, ManyToManyDescriptor(self.remote_field, reverse=False))
1920 # Set up the accessor for the m2m table name for the relation.
1921 self.m2m_db_table = partial(self._get_m2m_db_table, cls._meta)
1923 def contribute_to_related_class(self, cls, related):
1924 # Internal M2Ms (i.e., those with a related name ending with '+')
1925 # and swapped models don't get a related descriptor.
1926 if (
1927 not self.remote_field.is_hidden()
1928 and not related.related_model._meta.swapped
1929 ):
1930 setattr(
1931 cls,
1932 related.get_accessor_name(),
1933 ManyToManyDescriptor(self.remote_field, reverse=True),
1934 )
1936 # Set up the accessors for the column names on the m2m table.
1937 self.m2m_column_name = partial(self._get_m2m_attr, related, "column")
1938 self.m2m_reverse_name = partial(self._get_m2m_reverse_attr, related, "column")
1940 self.m2m_field_name = partial(self._get_m2m_attr, related, "name")
1941 self.m2m_reverse_field_name = partial(
1942 self._get_m2m_reverse_attr, related, "name"
1943 )
1945 get_m2m_rel = partial(self._get_m2m_attr, related, "remote_field")
1946 self.m2m_target_field_name = lambda: get_m2m_rel().field_name
1947 get_m2m_reverse_rel = partial(
1948 self._get_m2m_reverse_attr, related, "remote_field"
1949 )
1950 self.m2m_reverse_target_field_name = lambda: get_m2m_reverse_rel().field_name
1952 def set_attributes_from_rel(self):
1953 pass
1955 def value_from_object(self, obj):
1956 return [] if obj.pk is None else list(getattr(obj, self.attname).all())
1958 def save_form_data(self, instance, data):
1959 getattr(instance, self.attname).set(data)
1961 def formfield(self, *, using=None, **kwargs):
1962 defaults = {
1963 "form_class": forms.ModelMultipleChoiceField,
1964 "queryset": self.remote_field.model._default_manager.using(using),
1965 **kwargs,
1966 }
1967 # If initial is passed in, it's a list of related objects, but the
1968 # MultipleChoiceField takes a list of IDs.
1969 if defaults.get("initial") is not None:
1970 initial = defaults["initial"]
1971 if callable(initial):
1972 initial = initial()
1973 defaults["initial"] = [i.pk for i in initial]
1974 return super().formfield(**defaults)
1976 def db_check(self, connection):
1977 return None
1979 def db_type(self, connection):
1980 # A ManyToManyField is not represented by a single column,
1981 # so return None.
1982 return None
1984 def db_parameters(self, connection):
1985 return {"type": None, "check": None}