Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py: 9%
611 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
1"""
2Accessors for related objects.
4When a field defines a relation between two models, each model class provides
5an attribute to access related instances of the other model class (unless the
6reverse accessor has been disabled with related_name='+').
8Accessors are implemented as descriptors in order to customize access and
9assignment. This module defines the descriptor classes.
11Forward accessors follow foreign keys. Reverse accessors trace them back. For
12example, with the following models::
14 class Parent(Model):
15 pass
17 class Child(Model):
18 parent = ForeignKey(Parent, related_name='children')
20 ``child.parent`` is a forward many-to-one relation. ``parent.children`` is a
21reverse many-to-one relation.
23There are three types of relations (many-to-one, one-to-one, and many-to-many)
24and two directions (forward and reverse) for a total of six combinations.
261. Related instance on the forward side of a many-to-one relation:
27 ``ForwardManyToOneDescriptor``.
29 Uniqueness of foreign key values is irrelevant to accessing the related
30 instance, making the many-to-one and one-to-one cases identical as far as
31 the descriptor is concerned. The constraint is checked upstream (unicity
32 validation in forms) or downstream (unique indexes in the database).
342. Related instance on the forward side of a one-to-one
35 relation: ``ForwardOneToOneDescriptor``.
37 It avoids querying the database when accessing the parent link field in
38 a multi-table inheritance scenario.
403. Related instance on the reverse side of a one-to-one relation:
41 ``ReverseOneToOneDescriptor``.
43 One-to-one relations are asymmetrical, despite the apparent symmetry of the
44 name, because they're implemented in the database with a foreign key from
45 one table to another. As a consequence ``ReverseOneToOneDescriptor`` is
46 slightly different from ``ForwardManyToOneDescriptor``.
484. Related objects manager for related instances on the reverse side of a
49 many-to-one relation: ``ReverseManyToOneDescriptor``.
51 Unlike the previous two classes, this one provides access to a collection
52 of objects. It returns a manager rather than an instance.
545. Related objects manager for related instances on the forward or reverse
55 sides of a many-to-many relation: ``ManyToManyDescriptor``.
57 Many-to-many relations are symmetrical. The syntax of Django models
58 requires declaring them on one side but that's an implementation detail.
59 They could be declared on the other side without any change in behavior.
60 Therefore the forward and reverse descriptors can be the same.
62 If you're looking for ``ForwardManyToManyDescriptor`` or
63 ``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead.
64"""
66from asgiref.sync import sync_to_async
68from django.core.exceptions import FieldError
69from django.db import (
70 DEFAULT_DB_ALIAS,
71 NotSupportedError,
72 connections,
73 router,
74 transaction,
75)
76from django.db.models import Q, Window, signals
77from django.db.models.functions import RowNumber
78from django.db.models.lookups import GreaterThan, LessThanOrEqual
79from django.db.models.query import QuerySet
80from django.db.models.query_utils import DeferredAttribute
81from django.db.models.utils import AltersData, resolve_callables
82from django.utils.functional import cached_property
85class ForeignKeyDeferredAttribute(DeferredAttribute):
86 def __set__(self, instance, value):
87 if instance.__dict__.get(self.field.attname) != value and self.field.is_cached(
88 instance
89 ):
90 self.field.delete_cached_value(instance)
91 instance.__dict__[self.field.attname] = value
94def _filter_prefetch_queryset(queryset, field_name, instances):
95 predicate = Q(**{f"{field_name}__in": instances})
96 db = queryset._db or DEFAULT_DB_ALIAS
97 if queryset.query.is_sliced:
98 if not connections[db].features.supports_over_clause:
99 raise NotSupportedError(
100 "Prefetching from a limited queryset is only supported on backends "
101 "that support window functions."
102 )
103 low_mark, high_mark = queryset.query.low_mark, queryset.query.high_mark
104 order_by = [
105 expr for expr, _ in queryset.query.get_compiler(using=db).get_order_by()
106 ]
107 window = Window(RowNumber(), partition_by=field_name, order_by=order_by)
108 predicate &= GreaterThan(window, low_mark)
109 if high_mark is not None:
110 predicate &= LessThanOrEqual(window, high_mark)
111 queryset.query.clear_limits()
112 return queryset.filter(predicate)
115class ForwardManyToOneDescriptor:
116 """
117 Accessor to the related object on the forward side of a many-to-one or
118 one-to-one (via ForwardOneToOneDescriptor subclass) relation.
120 In the example::
122 class Child(Model):
123 parent = ForeignKey(Parent, related_name='children')
125 ``Child.parent`` is a ``ForwardManyToOneDescriptor`` instance.
126 """
128 def __init__(self, field_with_rel):
129 self.field = field_with_rel
131 @cached_property
132 def RelatedObjectDoesNotExist(self):
133 # The exception can't be created at initialization time since the
134 # related model might not be resolved yet; `self.field.model` might
135 # still be a string model reference.
136 return type(
137 "RelatedObjectDoesNotExist",
138 (self.field.remote_field.model.DoesNotExist, AttributeError),
139 {
140 "__module__": self.field.model.__module__,
141 "__qualname__": "%s.%s.RelatedObjectDoesNotExist"
142 % (
143 self.field.model.__qualname__,
144 self.field.name,
145 ),
146 },
147 )
149 def is_cached(self, instance):
150 return self.field.is_cached(instance)
152 def get_queryset(self, **hints):
153 return self.field.remote_field.model._base_manager.db_manager(hints=hints).all()
155 def get_prefetch_queryset(self, instances, queryset=None):
156 if queryset is None:
157 queryset = self.get_queryset()
158 queryset._add_hints(instance=instances[0])
160 rel_obj_attr = self.field.get_foreign_related_value
161 instance_attr = self.field.get_local_related_value
162 instances_dict = {instance_attr(inst): inst for inst in instances}
163 related_field = self.field.foreign_related_fields[0]
164 remote_field = self.field.remote_field
166 # FIXME: This will need to be revisited when we introduce support for
167 # composite fields. In the meantime we take this practical approach to
168 # solve a regression on 1.6 when the reverse manager in hidden
169 # (related_name ends with a '+'). Refs #21410.
170 # The check for len(...) == 1 is a special case that allows the query
171 # to be join-less and smaller. Refs #21760.
172 if remote_field.is_hidden() or len(self.field.foreign_related_fields) == 1:
173 query = {
174 "%s__in"
175 % related_field.name: {instance_attr(inst)[0] for inst in instances}
176 }
177 else:
178 query = {"%s__in" % self.field.related_query_name(): instances}
179 queryset = queryset.filter(**query)
181 # Since we're going to assign directly in the cache,
182 # we must manage the reverse relation cache manually.
183 if not remote_field.multiple:
184 for rel_obj in queryset:
185 instance = instances_dict[rel_obj_attr(rel_obj)]
186 remote_field.set_cached_value(rel_obj, instance)
187 return (
188 queryset,
189 rel_obj_attr,
190 instance_attr,
191 True,
192 self.field.get_cache_name(),
193 False,
194 )
196 def get_object(self, instance):
197 qs = self.get_queryset(instance=instance)
198 # Assuming the database enforces foreign keys, this won't fail.
199 return qs.get(self.field.get_reverse_related_filter(instance))
201 def __get__(self, instance, cls=None):
202 """
203 Get the related instance through the forward relation.
205 With the example above, when getting ``child.parent``:
207 - ``self`` is the descriptor managing the ``parent`` attribute
208 - ``instance`` is the ``child`` instance
209 - ``cls`` is the ``Child`` class (we don't need it)
210 """
211 if instance is None:
212 return self
214 # The related instance is loaded from the database and then cached
215 # by the field on the model instance state. It can also be pre-cached
216 # by the reverse accessor (ReverseOneToOneDescriptor).
217 try:
218 rel_obj = self.field.get_cached_value(instance)
219 except KeyError:
220 has_value = None not in self.field.get_local_related_value(instance)
221 ancestor_link = (
222 instance._meta.get_ancestor_link(self.field.model)
223 if has_value
224 else None
225 )
226 if ancestor_link and ancestor_link.is_cached(instance):
227 # An ancestor link will exist if this field is defined on a
228 # multi-table inheritance parent of the instance's class.
229 ancestor = ancestor_link.get_cached_value(instance)
230 # The value might be cached on an ancestor if the instance
231 # originated from walking down the inheritance chain.
232 rel_obj = self.field.get_cached_value(ancestor, default=None)
233 else:
234 rel_obj = None
235 if rel_obj is None and has_value:
236 rel_obj = self.get_object(instance)
237 remote_field = self.field.remote_field
238 # If this is a one-to-one relation, set the reverse accessor
239 # cache on the related object to the current instance to avoid
240 # an extra SQL query if it's accessed later on.
241 if not remote_field.multiple:
242 remote_field.set_cached_value(rel_obj, instance)
243 self.field.set_cached_value(instance, rel_obj)
245 if rel_obj is None and not self.field.null:
246 raise self.RelatedObjectDoesNotExist(
247 "%s has no %s." % (self.field.model.__name__, self.field.name)
248 )
249 else:
250 return rel_obj
252 def __set__(self, instance, value):
253 """
254 Set the related instance through the forward relation.
256 With the example above, when setting ``child.parent = parent``:
258 - ``self`` is the descriptor managing the ``parent`` attribute
259 - ``instance`` is the ``child`` instance
260 - ``value`` is the ``parent`` instance on the right of the equal sign
261 """
262 # An object must be an instance of the related class.
263 if value is not None and not isinstance(
264 value, self.field.remote_field.model._meta.concrete_model
265 ):
266 raise ValueError(
267 'Cannot assign "%r": "%s.%s" must be a "%s" instance.'
268 % (
269 value,
270 instance._meta.object_name,
271 self.field.name,
272 self.field.remote_field.model._meta.object_name,
273 )
274 )
275 elif value is not None:
276 if instance._state.db is None:
277 instance._state.db = router.db_for_write(
278 instance.__class__, instance=value
279 )
280 if value._state.db is None:
281 value._state.db = router.db_for_write(
282 value.__class__, instance=instance
283 )
284 if not router.allow_relation(value, instance):
285 raise ValueError(
286 'Cannot assign "%r": the current database router prevents this '
287 "relation." % value
288 )
290 remote_field = self.field.remote_field
291 # If we're setting the value of a OneToOneField to None, we need to clear
292 # out the cache on any old related object. Otherwise, deleting the
293 # previously-related object will also cause this object to be deleted,
294 # which is wrong.
295 if value is None:
296 # Look up the previously-related object, which may still be available
297 # since we've not yet cleared out the related field.
298 # Use the cache directly, instead of the accessor; if we haven't
299 # populated the cache, then we don't care - we're only accessing
300 # the object to invalidate the accessor cache, so there's no
301 # need to populate the cache just to expire it again.
302 related = self.field.get_cached_value(instance, default=None)
304 # If we've got an old related object, we need to clear out its
305 # cache. This cache also might not exist if the related object
306 # hasn't been accessed yet.
307 if related is not None:
308 remote_field.set_cached_value(related, None)
310 for lh_field, rh_field in self.field.related_fields:
311 setattr(instance, lh_field.attname, None)
313 # Set the values of the related field.
314 else:
315 for lh_field, rh_field in self.field.related_fields:
316 setattr(instance, lh_field.attname, getattr(value, rh_field.attname))
318 # Set the related instance cache used by __get__ to avoid an SQL query
319 # when accessing the attribute we just set.
320 self.field.set_cached_value(instance, value)
322 # If this is a one-to-one relation, set the reverse accessor cache on
323 # the related object to the current instance to avoid an extra SQL
324 # query if it's accessed later on.
325 if value is not None and not remote_field.multiple:
326 remote_field.set_cached_value(value, instance)
328 def __reduce__(self):
329 """
330 Pickling should return the instance attached by self.field on the
331 model, not a new copy of that descriptor. Use getattr() to retrieve
332 the instance directly from the model.
333 """
334 return getattr, (self.field.model, self.field.name)
337class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor):
338 """
339 Accessor to the related object on the forward side of a one-to-one relation.
341 In the example::
343 class Restaurant(Model):
344 place = OneToOneField(Place, related_name='restaurant')
346 ``Restaurant.place`` is a ``ForwardOneToOneDescriptor`` instance.
347 """
349 def get_object(self, instance):
350 if self.field.remote_field.parent_link:
351 deferred = instance.get_deferred_fields()
352 # Because it's a parent link, all the data is available in the
353 # instance, so populate the parent model with this data.
354 rel_model = self.field.remote_field.model
355 fields = [field.attname for field in rel_model._meta.concrete_fields]
357 # If any of the related model's fields are deferred, fallback to
358 # fetching all fields from the related model. This avoids a query
359 # on the related model for every deferred field.
360 if not any(field in fields for field in deferred):
361 kwargs = {field: getattr(instance, field) for field in fields}
362 obj = rel_model(**kwargs)
363 obj._state.adding = instance._state.adding
364 obj._state.db = instance._state.db
365 return obj
366 return super().get_object(instance)
368 def __set__(self, instance, value):
369 super().__set__(instance, value)
370 # If the primary key is a link to a parent model and a parent instance
371 # is being set, update the value of the inherited pk(s).
372 if self.field.primary_key and self.field.remote_field.parent_link:
373 opts = instance._meta
374 # Inherited primary key fields from this object's base classes.
375 inherited_pk_fields = [
376 field
377 for field in opts.concrete_fields
378 if field.primary_key and field.remote_field
379 ]
380 for field in inherited_pk_fields:
381 rel_model_pk_name = field.remote_field.model._meta.pk.attname
382 raw_value = (
383 getattr(value, rel_model_pk_name) if value is not None else None
384 )
385 setattr(instance, rel_model_pk_name, raw_value)
388class ReverseOneToOneDescriptor:
389 """
390 Accessor to the related object on the reverse side of a one-to-one
391 relation.
393 In the example::
395 class Restaurant(Model):
396 place = OneToOneField(Place, related_name='restaurant')
398 ``Place.restaurant`` is a ``ReverseOneToOneDescriptor`` instance.
399 """
401 def __init__(self, related):
402 # Following the example above, `related` is an instance of OneToOneRel
403 # which represents the reverse restaurant field (place.restaurant).
404 self.related = related
406 @cached_property
407 def RelatedObjectDoesNotExist(self):
408 # The exception isn't created at initialization time for the sake of
409 # consistency with `ForwardManyToOneDescriptor`.
410 return type(
411 "RelatedObjectDoesNotExist",
412 (self.related.related_model.DoesNotExist, AttributeError),
413 {
414 "__module__": self.related.model.__module__,
415 "__qualname__": "%s.%s.RelatedObjectDoesNotExist"
416 % (
417 self.related.model.__qualname__,
418 self.related.name,
419 ),
420 },
421 )
423 def is_cached(self, instance):
424 return self.related.is_cached(instance)
426 def get_queryset(self, **hints):
427 return self.related.related_model._base_manager.db_manager(hints=hints).all()
429 def get_prefetch_queryset(self, instances, queryset=None):
430 if queryset is None:
431 queryset = self.get_queryset()
432 queryset._add_hints(instance=instances[0])
434 rel_obj_attr = self.related.field.get_local_related_value
435 instance_attr = self.related.field.get_foreign_related_value
436 instances_dict = {instance_attr(inst): inst for inst in instances}
437 query = {"%s__in" % self.related.field.name: instances}
438 queryset = queryset.filter(**query)
440 # Since we're going to assign directly in the cache,
441 # we must manage the reverse relation cache manually.
442 for rel_obj in queryset:
443 instance = instances_dict[rel_obj_attr(rel_obj)]
444 self.related.field.set_cached_value(rel_obj, instance)
445 return (
446 queryset,
447 rel_obj_attr,
448 instance_attr,
449 True,
450 self.related.get_cache_name(),
451 False,
452 )
454 def __get__(self, instance, cls=None):
455 """
456 Get the related instance through the reverse relation.
458 With the example above, when getting ``place.restaurant``:
460 - ``self`` is the descriptor managing the ``restaurant`` attribute
461 - ``instance`` is the ``place`` instance
462 - ``cls`` is the ``Place`` class (unused)
464 Keep in mind that ``Restaurant`` holds the foreign key to ``Place``.
465 """
466 if instance is None:
467 return self
469 # The related instance is loaded from the database and then cached
470 # by the field on the model instance state. It can also be pre-cached
471 # by the forward accessor (ForwardManyToOneDescriptor).
472 try:
473 rel_obj = self.related.get_cached_value(instance)
474 except KeyError:
475 related_pk = instance.pk
476 if related_pk is None:
477 rel_obj = None
478 else:
479 filter_args = self.related.field.get_forward_related_filter(instance)
480 try:
481 rel_obj = self.get_queryset(instance=instance).get(**filter_args)
482 except self.related.related_model.DoesNotExist:
483 rel_obj = None
484 else:
485 # Set the forward accessor cache on the related object to
486 # the current instance to avoid an extra SQL query if it's
487 # accessed later on.
488 self.related.field.set_cached_value(rel_obj, instance)
489 self.related.set_cached_value(instance, rel_obj)
491 if rel_obj is None:
492 raise self.RelatedObjectDoesNotExist(
493 "%s has no %s."
494 % (instance.__class__.__name__, self.related.get_accessor_name())
495 )
496 else:
497 return rel_obj
499 def __set__(self, instance, value):
500 """
501 Set the related instance through the reverse relation.
503 With the example above, when setting ``place.restaurant = restaurant``:
505 - ``self`` is the descriptor managing the ``restaurant`` attribute
506 - ``instance`` is the ``place`` instance
507 - ``value`` is the ``restaurant`` instance on the right of the equal sign
509 Keep in mind that ``Restaurant`` holds the foreign key to ``Place``.
510 """
511 # The similarity of the code below to the code in
512 # ForwardManyToOneDescriptor is annoying, but there's a bunch
513 # of small differences that would make a common base class convoluted.
515 if value is None:
516 # Update the cached related instance (if any) & clear the cache.
517 # Following the example above, this would be the cached
518 # ``restaurant`` instance (if any).
519 rel_obj = self.related.get_cached_value(instance, default=None)
520 if rel_obj is not None:
521 # Remove the ``restaurant`` instance from the ``place``
522 # instance cache.
523 self.related.delete_cached_value(instance)
524 # Set the ``place`` field on the ``restaurant``
525 # instance to None.
526 setattr(rel_obj, self.related.field.name, None)
527 elif not isinstance(value, self.related.related_model):
528 # An object must be an instance of the related class.
529 raise ValueError(
530 'Cannot assign "%r": "%s.%s" must be a "%s" instance.'
531 % (
532 value,
533 instance._meta.object_name,
534 self.related.get_accessor_name(),
535 self.related.related_model._meta.object_name,
536 )
537 )
538 else:
539 if instance._state.db is None:
540 instance._state.db = router.db_for_write(
541 instance.__class__, instance=value
542 )
543 if value._state.db is None:
544 value._state.db = router.db_for_write(
545 value.__class__, instance=instance
546 )
547 if not router.allow_relation(value, instance):
548 raise ValueError(
549 'Cannot assign "%r": the current database router prevents this '
550 "relation." % value
551 )
553 related_pk = tuple(
554 getattr(instance, field.attname)
555 for field in self.related.field.foreign_related_fields
556 )
557 # Set the value of the related field to the value of the related
558 # object's related field.
559 for index, field in enumerate(self.related.field.local_related_fields):
560 setattr(value, field.attname, related_pk[index])
562 # Set the related instance cache used by __get__ to avoid an SQL query
563 # when accessing the attribute we just set.
564 self.related.set_cached_value(instance, value)
566 # Set the forward accessor cache on the related object to the current
567 # instance to avoid an extra SQL query if it's accessed later on.
568 self.related.field.set_cached_value(value, instance)
570 def __reduce__(self):
571 # Same purpose as ForwardManyToOneDescriptor.__reduce__().
572 return getattr, (self.related.model, self.related.name)
575class ReverseManyToOneDescriptor:
576 """
577 Accessor to the related objects manager on the reverse side of a
578 many-to-one relation.
580 In the example::
582 class Child(Model):
583 parent = ForeignKey(Parent, related_name='children')
585 ``Parent.children`` is a ``ReverseManyToOneDescriptor`` instance.
587 Most of the implementation is delegated to a dynamically defined manager
588 class built by ``create_forward_many_to_many_manager()`` defined below.
589 """
591 def __init__(self, rel):
592 self.rel = rel
593 self.field = rel.field
595 @cached_property
596 def related_manager_cls(self):
597 related_model = self.rel.related_model
599 return create_reverse_many_to_one_manager(
600 related_model._default_manager.__class__,
601 self.rel,
602 )
604 def __get__(self, instance, cls=None):
605 """
606 Get the related objects through the reverse relation.
608 With the example above, when getting ``parent.children``:
610 - ``self`` is the descriptor managing the ``children`` attribute
611 - ``instance`` is the ``parent`` instance
612 - ``cls`` is the ``Parent`` class (unused)
613 """
614 if instance is None:
615 return self
617 return self.related_manager_cls(instance)
619 def _get_set_deprecation_msg_params(self):
620 return (
621 "reverse side of a related set",
622 self.rel.get_accessor_name(),
623 )
625 def __set__(self, instance, value):
626 raise TypeError(
627 "Direct assignment to the %s is prohibited. Use %s.set() instead."
628 % self._get_set_deprecation_msg_params(),
629 )
632def create_reverse_many_to_one_manager(superclass, rel):
633 """
634 Create a manager for the reverse side of a many-to-one relation.
636 This manager subclasses another manager, generally the default manager of
637 the related model, and adds behaviors specific to many-to-one relations.
638 """
640 class RelatedManager(superclass, AltersData):
641 def __init__(self, instance):
642 super().__init__()
644 self.instance = instance
645 self.model = rel.related_model
646 self.field = rel.field
648 self.core_filters = {self.field.name: instance}
650 def __call__(self, *, manager):
651 manager = getattr(self.model, manager)
652 manager_class = create_reverse_many_to_one_manager(manager.__class__, rel)
653 return manager_class(self.instance)
655 do_not_call_in_templates = True
657 def _check_fk_val(self):
658 for field in self.field.foreign_related_fields:
659 if getattr(self.instance, field.attname) is None:
660 raise ValueError(
661 f'"{self.instance!r}" needs to have a value for field '
662 f'"{field.attname}" before this relationship can be used.'
663 )
665 def _apply_rel_filters(self, queryset):
666 """
667 Filter the queryset for the instance this manager is bound to.
668 """
669 db = self._db or router.db_for_read(self.model, instance=self.instance)
670 empty_strings_as_null = connections[
671 db
672 ].features.interprets_empty_strings_as_nulls
673 queryset._add_hints(instance=self.instance)
674 if self._db:
675 queryset = queryset.using(self._db)
676 queryset._defer_next_filter = True
677 queryset = queryset.filter(**self.core_filters)
678 for field in self.field.foreign_related_fields:
679 val = getattr(self.instance, field.attname)
680 if val is None or (val == "" and empty_strings_as_null):
681 return queryset.none()
682 if self.field.many_to_one:
683 # Guard against field-like objects such as GenericRelation
684 # that abuse create_reverse_many_to_one_manager() with reverse
685 # one-to-many relationships instead and break known related
686 # objects assignment.
687 try:
688 target_field = self.field.target_field
689 except FieldError:
690 # The relationship has multiple target fields. Use a tuple
691 # for related object id.
692 rel_obj_id = tuple(
693 [
694 getattr(self.instance, target_field.attname)
695 for target_field in self.field.path_infos[-1].target_fields
696 ]
697 )
698 else:
699 rel_obj_id = getattr(self.instance, target_field.attname)
700 queryset._known_related_objects = {
701 self.field: {rel_obj_id: self.instance}
702 }
703 return queryset
705 def _remove_prefetched_objects(self):
706 try:
707 self.instance._prefetched_objects_cache.pop(
708 self.field.remote_field.get_cache_name()
709 )
710 except (AttributeError, KeyError):
711 pass # nothing to clear from cache
713 def get_queryset(self):
714 # Even if this relation is not to pk, we require still pk value.
715 # The wish is that the instance has been already saved to DB,
716 # although having a pk value isn't a guarantee of that.
717 if self.instance.pk is None:
718 raise ValueError(
719 f"{self.instance.__class__.__name__!r} instance needs to have a "
720 f"primary key value before this relationship can be used."
721 )
722 try:
723 return self.instance._prefetched_objects_cache[
724 self.field.remote_field.get_cache_name()
725 ]
726 except (AttributeError, KeyError):
727 queryset = super().get_queryset()
728 return self._apply_rel_filters(queryset)
730 def get_prefetch_queryset(self, instances, queryset=None):
731 if queryset is None:
732 queryset = super().get_queryset()
734 queryset._add_hints(instance=instances[0])
735 queryset = queryset.using(queryset._db or self._db)
737 rel_obj_attr = self.field.get_local_related_value
738 instance_attr = self.field.get_foreign_related_value
739 instances_dict = {instance_attr(inst): inst for inst in instances}
740 queryset = _filter_prefetch_queryset(queryset, self.field.name, instances)
742 # Since we just bypassed this class' get_queryset(), we must manage
743 # the reverse relation manually.
744 for rel_obj in queryset:
745 if not self.field.is_cached(rel_obj):
746 instance = instances_dict[rel_obj_attr(rel_obj)]
747 setattr(rel_obj, self.field.name, instance)
748 cache_name = self.field.remote_field.get_cache_name()
749 return queryset, rel_obj_attr, instance_attr, False, cache_name, False
751 def add(self, *objs, bulk=True):
752 self._check_fk_val()
753 self._remove_prefetched_objects()
754 db = router.db_for_write(self.model, instance=self.instance)
756 def check_and_update_obj(obj):
757 if not isinstance(obj, self.model):
758 raise TypeError(
759 "'%s' instance expected, got %r"
760 % (
761 self.model._meta.object_name,
762 obj,
763 )
764 )
765 setattr(obj, self.field.name, self.instance)
767 if bulk:
768 pks = []
769 for obj in objs:
770 check_and_update_obj(obj)
771 if obj._state.adding or obj._state.db != db:
772 raise ValueError(
773 "%r instance isn't saved. Use bulk=False or save "
774 "the object first." % obj
775 )
776 pks.append(obj.pk)
777 self.model._base_manager.using(db).filter(pk__in=pks).update(
778 **{
779 self.field.name: self.instance,
780 }
781 )
782 else:
783 with transaction.atomic(using=db, savepoint=False):
784 for obj in objs:
785 check_and_update_obj(obj)
786 obj.save()
788 add.alters_data = True
790 async def aadd(self, *objs, bulk=True):
791 return await sync_to_async(self.add)(*objs, bulk=bulk)
793 aadd.alters_data = True
795 def create(self, **kwargs):
796 self._check_fk_val()
797 kwargs[self.field.name] = self.instance
798 db = router.db_for_write(self.model, instance=self.instance)
799 return super(RelatedManager, self.db_manager(db)).create(**kwargs)
801 create.alters_data = True
803 async def acreate(self, **kwargs):
804 return await sync_to_async(self.create)(**kwargs)
806 acreate.alters_data = True
808 def get_or_create(self, **kwargs):
809 self._check_fk_val()
810 kwargs[self.field.name] = self.instance
811 db = router.db_for_write(self.model, instance=self.instance)
812 return super(RelatedManager, self.db_manager(db)).get_or_create(**kwargs)
814 get_or_create.alters_data = True
816 async def aget_or_create(self, **kwargs):
817 return await sync_to_async(self.get_or_create)(**kwargs)
819 aget_or_create.alters_data = True
821 def update_or_create(self, **kwargs):
822 self._check_fk_val()
823 kwargs[self.field.name] = self.instance
824 db = router.db_for_write(self.model, instance=self.instance)
825 return super(RelatedManager, self.db_manager(db)).update_or_create(**kwargs)
827 update_or_create.alters_data = True
829 async def aupdate_or_create(self, **kwargs):
830 return await sync_to_async(self.update_or_create)(**kwargs)
832 aupdate_or_create.alters_data = True
834 # remove() and clear() are only provided if the ForeignKey can have a
835 # value of null.
836 if rel.field.null:
838 def remove(self, *objs, bulk=True):
839 if not objs:
840 return
841 self._check_fk_val()
842 val = self.field.get_foreign_related_value(self.instance)
843 old_ids = set()
844 for obj in objs:
845 if not isinstance(obj, self.model):
846 raise TypeError(
847 "'%s' instance expected, got %r"
848 % (
849 self.model._meta.object_name,
850 obj,
851 )
852 )
853 # Is obj actually part of this descriptor set?
854 if self.field.get_local_related_value(obj) == val:
855 old_ids.add(obj.pk)
856 else:
857 raise self.field.remote_field.model.DoesNotExist(
858 "%r is not related to %r." % (obj, self.instance)
859 )
860 self._clear(self.filter(pk__in=old_ids), bulk)
862 remove.alters_data = True
864 async def aremove(self, *objs, bulk=True):
865 return await sync_to_async(self.remove)(*objs, bulk=bulk)
867 aremove.alters_data = True
869 def clear(self, *, bulk=True):
870 self._check_fk_val()
871 self._clear(self, bulk)
873 clear.alters_data = True
875 async def aclear(self, *, bulk=True):
876 return await sync_to_async(self.clear)(bulk=bulk)
878 aclear.alters_data = True
880 def _clear(self, queryset, bulk):
881 self._remove_prefetched_objects()
882 db = router.db_for_write(self.model, instance=self.instance)
883 queryset = queryset.using(db)
884 if bulk:
885 # `QuerySet.update()` is intrinsically atomic.
886 queryset.update(**{self.field.name: None})
887 else:
888 with transaction.atomic(using=db, savepoint=False):
889 for obj in queryset:
890 setattr(obj, self.field.name, None)
891 obj.save(update_fields=[self.field.name])
893 _clear.alters_data = True
895 def set(self, objs, *, bulk=True, clear=False):
896 self._check_fk_val()
897 # Force evaluation of `objs` in case it's a queryset whose value
898 # could be affected by `manager.clear()`. Refs #19816.
899 objs = tuple(objs)
901 if self.field.null:
902 db = router.db_for_write(self.model, instance=self.instance)
903 with transaction.atomic(using=db, savepoint=False):
904 if clear:
905 self.clear(bulk=bulk)
906 self.add(*objs, bulk=bulk)
907 else:
908 old_objs = set(self.using(db).all())
909 new_objs = []
910 for obj in objs:
911 if obj in old_objs:
912 old_objs.remove(obj)
913 else:
914 new_objs.append(obj)
916 self.remove(*old_objs, bulk=bulk)
917 self.add(*new_objs, bulk=bulk)
918 else:
919 self.add(*objs, bulk=bulk)
921 set.alters_data = True
923 async def aset(self, objs, *, bulk=True, clear=False):
924 return await sync_to_async(self.set)(objs=objs, bulk=bulk, clear=clear)
926 aset.alters_data = True
928 return RelatedManager
931class ManyToManyDescriptor(ReverseManyToOneDescriptor):
932 """
933 Accessor to the related objects manager on the forward and reverse sides of
934 a many-to-many relation.
936 In the example::
938 class Pizza(Model):
939 toppings = ManyToManyField(Topping, related_name='pizzas')
941 ``Pizza.toppings`` and ``Topping.pizzas`` are ``ManyToManyDescriptor``
942 instances.
944 Most of the implementation is delegated to a dynamically defined manager
945 class built by ``create_forward_many_to_many_manager()`` defined below.
946 """
948 def __init__(self, rel, reverse=False):
949 super().__init__(rel)
951 self.reverse = reverse
953 @property
954 def through(self):
955 # through is provided so that you have easy access to the through
956 # model (Book.authors.through) for inlines, etc. This is done as
957 # a property to ensure that the fully resolved value is returned.
958 return self.rel.through
960 @cached_property
961 def related_manager_cls(self):
962 related_model = self.rel.related_model if self.reverse else self.rel.model
964 return create_forward_many_to_many_manager(
965 related_model._default_manager.__class__,
966 self.rel,
967 reverse=self.reverse,
968 )
970 def _get_set_deprecation_msg_params(self):
971 return (
972 "%s side of a many-to-many set"
973 % ("reverse" if self.reverse else "forward"),
974 self.rel.get_accessor_name() if self.reverse else self.field.name,
975 )
978def create_forward_many_to_many_manager(superclass, rel, reverse):
979 """
980 Create a manager for the either side of a many-to-many relation.
982 This manager subclasses another manager, generally the default manager of
983 the related model, and adds behaviors specific to many-to-many relations.
984 """
986 class ManyRelatedManager(superclass, AltersData):
987 def __init__(self, instance=None):
988 super().__init__()
990 self.instance = instance
992 if not reverse:
993 self.model = rel.model
994 self.query_field_name = rel.field.related_query_name()
995 self.prefetch_cache_name = rel.field.name
996 self.source_field_name = rel.field.m2m_field_name()
997 self.target_field_name = rel.field.m2m_reverse_field_name()
998 self.symmetrical = rel.symmetrical
999 else:
1000 self.model = rel.related_model
1001 self.query_field_name = rel.field.name
1002 self.prefetch_cache_name = rel.field.related_query_name()
1003 self.source_field_name = rel.field.m2m_reverse_field_name()
1004 self.target_field_name = rel.field.m2m_field_name()
1005 self.symmetrical = False
1007 self.through = rel.through
1008 self.reverse = reverse
1010 self.source_field = self.through._meta.get_field(self.source_field_name)
1011 self.target_field = self.through._meta.get_field(self.target_field_name)
1013 self.core_filters = {}
1014 self.pk_field_names = {}
1015 for lh_field, rh_field in self.source_field.related_fields:
1016 core_filter_key = "%s__%s" % (self.query_field_name, rh_field.name)
1017 self.core_filters[core_filter_key] = getattr(instance, rh_field.attname)
1018 self.pk_field_names[lh_field.name] = rh_field.name
1020 self.related_val = self.source_field.get_foreign_related_value(instance)
1021 if None in self.related_val:
1022 raise ValueError(
1023 '"%r" needs to have a value for field "%s" before '
1024 "this many-to-many relationship can be used."
1025 % (instance, self.pk_field_names[self.source_field_name])
1026 )
1027 # Even if this relation is not to pk, we require still pk value.
1028 # The wish is that the instance has been already saved to DB,
1029 # although having a pk value isn't a guarantee of that.
1030 if instance.pk is None:
1031 raise ValueError(
1032 "%r instance needs to have a primary key value before "
1033 "a many-to-many relationship can be used."
1034 % instance.__class__.__name__
1035 )
1037 def __call__(self, *, manager):
1038 manager = getattr(self.model, manager)
1039 manager_class = create_forward_many_to_many_manager(
1040 manager.__class__, rel, reverse
1041 )
1042 return manager_class(instance=self.instance)
1044 do_not_call_in_templates = True
1046 def _build_remove_filters(self, removed_vals):
1047 filters = Q.create([(self.source_field_name, self.related_val)])
1048 # No need to add a subquery condition if removed_vals is a QuerySet without
1049 # filters.
1050 removed_vals_filters = (
1051 not isinstance(removed_vals, QuerySet) or removed_vals._has_filters()
1052 )
1053 if removed_vals_filters:
1054 filters &= Q.create([(f"{self.target_field_name}__in", removed_vals)])
1055 if self.symmetrical:
1056 symmetrical_filters = Q.create(
1057 [(self.target_field_name, self.related_val)]
1058 )
1059 if removed_vals_filters:
1060 symmetrical_filters &= Q.create(
1061 [(f"{self.source_field_name}__in", removed_vals)]
1062 )
1063 filters |= symmetrical_filters
1064 return filters
1066 def _apply_rel_filters(self, queryset):
1067 """
1068 Filter the queryset for the instance this manager is bound to.
1069 """
1070 queryset._add_hints(instance=self.instance)
1071 if self._db:
1072 queryset = queryset.using(self._db)
1073 queryset._defer_next_filter = True
1074 return queryset._next_is_sticky().filter(**self.core_filters)
1076 def _remove_prefetched_objects(self):
1077 try:
1078 self.instance._prefetched_objects_cache.pop(self.prefetch_cache_name)
1079 except (AttributeError, KeyError):
1080 pass # nothing to clear from cache
1082 def get_queryset(self):
1083 try:
1084 return self.instance._prefetched_objects_cache[self.prefetch_cache_name]
1085 except (AttributeError, KeyError):
1086 queryset = super().get_queryset()
1087 return self._apply_rel_filters(queryset)
1089 def get_prefetch_queryset(self, instances, queryset=None):
1090 if queryset is None:
1091 queryset = super().get_queryset()
1093 queryset._add_hints(instance=instances[0])
1094 queryset = queryset.using(queryset._db or self._db)
1095 queryset = _filter_prefetch_queryset(
1096 queryset._next_is_sticky(), self.query_field_name, instances
1097 )
1099 # M2M: need to annotate the query in order to get the primary model
1100 # that the secondary model was actually related to. We know that
1101 # there will already be a join on the join table, so we can just add
1102 # the select.
1104 # For non-autocreated 'through' models, can't assume we are
1105 # dealing with PK values.
1106 fk = self.through._meta.get_field(self.source_field_name)
1107 join_table = fk.model._meta.db_table
1108 connection = connections[queryset.db]
1109 qn = connection.ops.quote_name
1110 queryset = queryset.extra(
1111 select={
1112 "_prefetch_related_val_%s"
1113 % f.attname: "%s.%s"
1114 % (qn(join_table), qn(f.column))
1115 for f in fk.local_related_fields
1116 }
1117 )
1118 return (
1119 queryset,
1120 lambda result: tuple(
1121 getattr(result, "_prefetch_related_val_%s" % f.attname)
1122 for f in fk.local_related_fields
1123 ),
1124 lambda inst: tuple(
1125 f.get_db_prep_value(getattr(inst, f.attname), connection)
1126 for f in fk.foreign_related_fields
1127 ),
1128 False,
1129 self.prefetch_cache_name,
1130 False,
1131 )
1133 def add(self, *objs, through_defaults=None):
1134 self._remove_prefetched_objects()
1135 db = router.db_for_write(self.through, instance=self.instance)
1136 with transaction.atomic(using=db, savepoint=False):
1137 self._add_items(
1138 self.source_field_name,
1139 self.target_field_name,
1140 *objs,
1141 through_defaults=through_defaults,
1142 )
1143 # If this is a symmetrical m2m relation to self, add the mirror
1144 # entry in the m2m table.
1145 if self.symmetrical:
1146 self._add_items(
1147 self.target_field_name,
1148 self.source_field_name,
1149 *objs,
1150 through_defaults=through_defaults,
1151 )
1153 add.alters_data = True
1155 async def aadd(self, *objs, through_defaults=None):
1156 return await sync_to_async(self.add)(
1157 *objs, through_defaults=through_defaults
1158 )
1160 aadd.alters_data = True
1162 def remove(self, *objs):
1163 self._remove_prefetched_objects()
1164 self._remove_items(self.source_field_name, self.target_field_name, *objs)
1166 remove.alters_data = True
1168 async def aremove(self, *objs):
1169 return await sync_to_async(self.remove)(*objs)
1171 aremove.alters_data = True
1173 def clear(self):
1174 db = router.db_for_write(self.through, instance=self.instance)
1175 with transaction.atomic(using=db, savepoint=False):
1176 signals.m2m_changed.send(
1177 sender=self.through,
1178 action="pre_clear",
1179 instance=self.instance,
1180 reverse=self.reverse,
1181 model=self.model,
1182 pk_set=None,
1183 using=db,
1184 )
1185 self._remove_prefetched_objects()
1186 filters = self._build_remove_filters(super().get_queryset().using(db))
1187 self.through._default_manager.using(db).filter(filters).delete()
1189 signals.m2m_changed.send(
1190 sender=self.through,
1191 action="post_clear",
1192 instance=self.instance,
1193 reverse=self.reverse,
1194 model=self.model,
1195 pk_set=None,
1196 using=db,
1197 )
1199 clear.alters_data = True
1201 async def aclear(self):
1202 return await sync_to_async(self.clear)()
1204 aclear.alters_data = True
1206 def set(self, objs, *, clear=False, through_defaults=None):
1207 # Force evaluation of `objs` in case it's a queryset whose value
1208 # could be affected by `manager.clear()`. Refs #19816.
1209 objs = tuple(objs)
1211 db = router.db_for_write(self.through, instance=self.instance)
1212 with transaction.atomic(using=db, savepoint=False):
1213 if clear:
1214 self.clear()
1215 self.add(*objs, through_defaults=through_defaults)
1216 else:
1217 old_ids = set(
1218 self.using(db).values_list(
1219 self.target_field.target_field.attname, flat=True
1220 )
1221 )
1223 new_objs = []
1224 for obj in objs:
1225 fk_val = (
1226 self.target_field.get_foreign_related_value(obj)[0]
1227 if isinstance(obj, self.model)
1228 else self.target_field.get_prep_value(obj)
1229 )
1230 if fk_val in old_ids:
1231 old_ids.remove(fk_val)
1232 else:
1233 new_objs.append(obj)
1235 self.remove(*old_ids)
1236 self.add(*new_objs, through_defaults=through_defaults)
1238 set.alters_data = True
1240 async def aset(self, objs, *, clear=False, through_defaults=None):
1241 return await sync_to_async(self.set)(
1242 objs=objs, clear=clear, through_defaults=through_defaults
1243 )
1245 aset.alters_data = True
1247 def create(self, *, through_defaults=None, **kwargs):
1248 db = router.db_for_write(self.instance.__class__, instance=self.instance)
1249 new_obj = super(ManyRelatedManager, self.db_manager(db)).create(**kwargs)
1250 self.add(new_obj, through_defaults=through_defaults)
1251 return new_obj
1253 create.alters_data = True
1255 async def acreate(self, *, through_defaults=None, **kwargs):
1256 return await sync_to_async(self.create)(
1257 through_defaults=through_defaults, **kwargs
1258 )
1260 acreate.alters_data = True
1262 def get_or_create(self, *, through_defaults=None, **kwargs):
1263 db = router.db_for_write(self.instance.__class__, instance=self.instance)
1264 obj, created = super(ManyRelatedManager, self.db_manager(db)).get_or_create(
1265 **kwargs
1266 )
1267 # We only need to add() if created because if we got an object back
1268 # from get() then the relationship already exists.
1269 if created:
1270 self.add(obj, through_defaults=through_defaults)
1271 return obj, created
1273 get_or_create.alters_data = True
1275 async def aget_or_create(self, *, through_defaults=None, **kwargs):
1276 return await sync_to_async(self.get_or_create)(
1277 through_defaults=through_defaults, **kwargs
1278 )
1280 aget_or_create.alters_data = True
1282 def update_or_create(self, *, through_defaults=None, **kwargs):
1283 db = router.db_for_write(self.instance.__class__, instance=self.instance)
1284 obj, created = super(
1285 ManyRelatedManager, self.db_manager(db)
1286 ).update_or_create(**kwargs)
1287 # We only need to add() if created because if we got an object back
1288 # from get() then the relationship already exists.
1289 if created:
1290 self.add(obj, through_defaults=through_defaults)
1291 return obj, created
1293 update_or_create.alters_data = True
1295 async def aupdate_or_create(self, *, through_defaults=None, **kwargs):
1296 return await sync_to_async(self.update_or_create)(
1297 through_defaults=through_defaults, **kwargs
1298 )
1300 aupdate_or_create.alters_data = True
1302 def _get_target_ids(self, target_field_name, objs):
1303 """
1304 Return the set of ids of `objs` that the target field references.
1305 """
1306 from django.db.models import Model
1308 target_ids = set()
1309 target_field = self.through._meta.get_field(target_field_name)
1310 for obj in objs:
1311 if isinstance(obj, self.model):
1312 if not router.allow_relation(obj, self.instance):
1313 raise ValueError(
1314 'Cannot add "%r": instance is on database "%s", '
1315 'value is on database "%s"'
1316 % (obj, self.instance._state.db, obj._state.db)
1317 )
1318 target_id = target_field.get_foreign_related_value(obj)[0]
1319 if target_id is None:
1320 raise ValueError(
1321 'Cannot add "%r": the value for field "%s" is None'
1322 % (obj, target_field_name)
1323 )
1324 target_ids.add(target_id)
1325 elif isinstance(obj, Model):
1326 raise TypeError(
1327 "'%s' instance expected, got %r"
1328 % (self.model._meta.object_name, obj)
1329 )
1330 else:
1331 target_ids.add(target_field.get_prep_value(obj))
1332 return target_ids
1334 def _get_missing_target_ids(
1335 self, source_field_name, target_field_name, db, target_ids
1336 ):
1337 """
1338 Return the subset of ids of `objs` that aren't already assigned to
1339 this relationship.
1340 """
1341 vals = (
1342 self.through._default_manager.using(db)
1343 .values_list(target_field_name, flat=True)
1344 .filter(
1345 **{
1346 source_field_name: self.related_val[0],
1347 "%s__in" % target_field_name: target_ids,
1348 }
1349 )
1350 )
1351 return target_ids.difference(vals)
1353 def _get_add_plan(self, db, source_field_name):
1354 """
1355 Return a boolean triple of the way the add should be performed.
1357 The first element is whether or not bulk_create(ignore_conflicts)
1358 can be used, the second whether or not signals must be sent, and
1359 the third element is whether or not the immediate bulk insertion
1360 with conflicts ignored can be performed.
1361 """
1362 # Conflicts can be ignored when the intermediary model is
1363 # auto-created as the only possible collision is on the
1364 # (source_id, target_id) tuple. The same assertion doesn't hold for
1365 # user-defined intermediary models as they could have other fields
1366 # causing conflicts which must be surfaced.
1367 can_ignore_conflicts = (
1368 self.through._meta.auto_created is not False
1369 and connections[db].features.supports_ignore_conflicts
1370 )
1371 # Don't send the signal when inserting duplicate data row
1372 # for symmetrical reverse entries.
1373 must_send_signals = (
1374 self.reverse or source_field_name == self.source_field_name
1375 ) and (signals.m2m_changed.has_listeners(self.through))
1376 # Fast addition through bulk insertion can only be performed
1377 # if no m2m_changed listeners are connected for self.through
1378 # as they require the added set of ids to be provided via
1379 # pk_set.
1380 return (
1381 can_ignore_conflicts,
1382 must_send_signals,
1383 (can_ignore_conflicts and not must_send_signals),
1384 )
1386 def _add_items(
1387 self, source_field_name, target_field_name, *objs, through_defaults=None
1388 ):
1389 # source_field_name: the PK fieldname in join table for the source object
1390 # target_field_name: the PK fieldname in join table for the target object
1391 # *objs - objects to add. Either object instances, or primary keys
1392 # of object instances.
1393 if not objs:
1394 return
1396 through_defaults = dict(resolve_callables(through_defaults or {}))
1397 target_ids = self._get_target_ids(target_field_name, objs)
1398 db = router.db_for_write(self.through, instance=self.instance)
1399 can_ignore_conflicts, must_send_signals, can_fast_add = self._get_add_plan(
1400 db, source_field_name
1401 )
1402 if can_fast_add:
1403 self.through._default_manager.using(db).bulk_create(
1404 [
1405 self.through(
1406 **{
1407 "%s_id" % source_field_name: self.related_val[0],
1408 "%s_id" % target_field_name: target_id,
1409 }
1410 )
1411 for target_id in target_ids
1412 ],
1413 ignore_conflicts=True,
1414 )
1415 return
1417 missing_target_ids = self._get_missing_target_ids(
1418 source_field_name, target_field_name, db, target_ids
1419 )
1420 with transaction.atomic(using=db, savepoint=False):
1421 if must_send_signals:
1422 signals.m2m_changed.send(
1423 sender=self.through,
1424 action="pre_add",
1425 instance=self.instance,
1426 reverse=self.reverse,
1427 model=self.model,
1428 pk_set=missing_target_ids,
1429 using=db,
1430 )
1431 # Add the ones that aren't there already.
1432 self.through._default_manager.using(db).bulk_create(
1433 [
1434 self.through(
1435 **through_defaults,
1436 **{
1437 "%s_id" % source_field_name: self.related_val[0],
1438 "%s_id" % target_field_name: target_id,
1439 },
1440 )
1441 for target_id in missing_target_ids
1442 ],
1443 ignore_conflicts=can_ignore_conflicts,
1444 )
1446 if must_send_signals:
1447 signals.m2m_changed.send(
1448 sender=self.through,
1449 action="post_add",
1450 instance=self.instance,
1451 reverse=self.reverse,
1452 model=self.model,
1453 pk_set=missing_target_ids,
1454 using=db,
1455 )
1457 def _remove_items(self, source_field_name, target_field_name, *objs):
1458 # source_field_name: the PK colname in join table for the source object
1459 # target_field_name: the PK colname in join table for the target object
1460 # *objs - objects to remove. Either object instances, or primary
1461 # keys of object instances.
1462 if not objs:
1463 return
1465 # Check that all the objects are of the right type
1466 old_ids = set()
1467 for obj in objs:
1468 if isinstance(obj, self.model):
1469 fk_val = self.target_field.get_foreign_related_value(obj)[0]
1470 old_ids.add(fk_val)
1471 else:
1472 old_ids.add(obj)
1474 db = router.db_for_write(self.through, instance=self.instance)
1475 with transaction.atomic(using=db, savepoint=False):
1476 # Send a signal to the other end if need be.
1477 signals.m2m_changed.send(
1478 sender=self.through,
1479 action="pre_remove",
1480 instance=self.instance,
1481 reverse=self.reverse,
1482 model=self.model,
1483 pk_set=old_ids,
1484 using=db,
1485 )
1486 target_model_qs = super().get_queryset()
1487 if target_model_qs._has_filters():
1488 old_vals = target_model_qs.using(db).filter(
1489 **{"%s__in" % self.target_field.target_field.attname: old_ids}
1490 )
1491 else:
1492 old_vals = old_ids
1493 filters = self._build_remove_filters(old_vals)
1494 self.through._default_manager.using(db).filter(filters).delete()
1496 signals.m2m_changed.send(
1497 sender=self.through,
1498 action="post_remove",
1499 instance=self.instance,
1500 reverse=self.reverse,
1501 model=self.model,
1502 pk_set=old_ids,
1503 using=db,
1504 )
1506 return ManyRelatedManager