Coverage for /Users/davegaeddert/Development/dropseed/plain/plain-models/plain/models/fields/related_descriptors.py: 13%

569 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-10-16 22:03 -0500

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