Plain is headed towards 1.0! Subscribe for development updates →

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