Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/db/models/fields/related_descriptors.py: 50%

540 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

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 Django models 

58 requires declaring them on one side but that's an implementation detail. 

59 They could be declared on the other side without any change in behavior. 

60 Therefore the forward and reverse descriptors can be the same. 

61 

62 If you're looking for ``ForwardManyToManyDescriptor`` or 

63 ``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead. 

64""" 

65 

66from django.core.exceptions import FieldError 

67from django.db import connections, router, transaction 

68from django.db.models import Q, signals 

69from django.db.models.query import QuerySet 

70from django.db.models.query_utils import DeferredAttribute 

71from django.db.models.utils import resolve_callables 

72from django.utils.functional import cached_property 

73 

74 

75class ForeignKeyDeferredAttribute(DeferredAttribute): 

76 def __set__(self, instance, value): 

77 if instance.__dict__.get(self.field.attname) != value and self.field.is_cached( 

78 instance 

79 ): 

80 self.field.delete_cached_value(instance) 

81 instance.__dict__[self.field.attname] = value 

82 

83 

84class ForwardManyToOneDescriptor: 

85 """ 

86 Accessor to the related object on the forward side of a many-to-one or 

87 one-to-one (via ForwardOneToOneDescriptor subclass) relation. 

88 

89 In the example:: 

90 

91 class Child(Model): 

92 parent = ForeignKey(Parent, related_name='children') 

93 

94 ``Child.parent`` is a ``ForwardManyToOneDescriptor`` instance. 

95 """ 

96 

97 def __init__(self, field_with_rel): 

98 self.field = field_with_rel 

99 

100 @cached_property 

101 def RelatedObjectDoesNotExist(self): 

102 # The exception can't be created at initialization time since the 

103 # related model might not be resolved yet; `self.field.model` might 

104 # still be a string model reference. 

105 return type( 

106 "RelatedObjectDoesNotExist", 

107 (self.field.remote_field.model.DoesNotExist, AttributeError), 

108 { 

109 "__module__": self.field.model.__module__, 

110 "__qualname__": "%s.%s.RelatedObjectDoesNotExist" 

111 % ( 

112 self.field.model.__qualname__, 

113 self.field.name, 

114 ), 

115 }, 

116 ) 

117 

118 def is_cached(self, instance): 

119 return self.field.is_cached(instance) 

120 

121 def get_queryset(self, **hints): 

122 return self.field.remote_field.model._base_manager.db_manager(hints=hints).all() 

123 

124 def get_prefetch_queryset(self, instances, queryset=None): 

125 if queryset is None: 

126 queryset = self.get_queryset() 

127 queryset._add_hints(instance=instances[0]) 

128 

129 rel_obj_attr = self.field.get_foreign_related_value 

130 instance_attr = self.field.get_local_related_value 

131 instances_dict = {instance_attr(inst): inst for inst in instances} 

132 related_field = self.field.foreign_related_fields[0] 

133 remote_field = self.field.remote_field 

134 

135 # FIXME: This will need to be revisited when we introduce support for 

136 # composite fields. In the meantime we take this practical approach to 

137 # solve a regression on 1.6 when the reverse manager in hidden 

138 # (related_name ends with a '+'). Refs #21410. 

139 # The check for len(...) == 1 is a special case that allows the query 

140 # to be join-less and smaller. Refs #21760. 

141 if remote_field.is_hidden() or len(self.field.foreign_related_fields) == 1: 

142 query = { 

143 "%s__in" 

144 % related_field.name: {instance_attr(inst)[0] for inst in instances} 

145 } 

146 else: 

147 query = {"%s__in" % self.field.related_query_name(): instances} 

148 queryset = queryset.filter(**query) 

149 

150 # Since we're going to assign directly in the cache, 

151 # we must manage the reverse relation cache manually. 

152 if not remote_field.multiple: 

153 for rel_obj in queryset: 

154 instance = instances_dict[rel_obj_attr(rel_obj)] 

155 remote_field.set_cached_value(rel_obj, instance) 

156 return ( 

157 queryset, 

158 rel_obj_attr, 

159 instance_attr, 

160 True, 

161 self.field.get_cache_name(), 

162 False, 

163 ) 

164 

165 def get_object(self, instance): 

166 qs = self.get_queryset(instance=instance) 

167 # Assuming the database enforces foreign keys, this won't fail. 

168 return qs.get(self.field.get_reverse_related_filter(instance)) 

169 

170 def __get__(self, instance, cls=None): 

171 """ 

172 Get the related instance through the forward relation. 

173 

174 With the example above, when getting ``child.parent``: 

175 

176 - ``self`` is the descriptor managing the ``parent`` attribute 

177 - ``instance`` is the ``child`` instance 

178 - ``cls`` is the ``Child`` class (we don't need it) 

179 """ 

180 if instance is None: 

181 return self 

182 

183 # The related instance is loaded from the database and then cached 

184 # by the field on the model instance state. It can also be pre-cached 

185 # by the reverse accessor (ReverseOneToOneDescriptor). 

186 try: 

187 rel_obj = self.field.get_cached_value(instance) 

188 except KeyError: 

189 has_value = None not in self.field.get_local_related_value(instance) 

190 ancestor_link = ( 

191 instance._meta.get_ancestor_link(self.field.model) 

192 if has_value 

193 else None 

194 ) 

195 if ancestor_link and ancestor_link.is_cached(instance): 195 ↛ 198line 195 didn't jump to line 198, because the condition on line 195 was never true

196 # An ancestor link will exist if this field is defined on a 

197 # multi-table inheritance parent of the instance's class. 

198 ancestor = ancestor_link.get_cached_value(instance) 

199 # The value might be cached on an ancestor if the instance 

200 # originated from walking down the inheritance chain. 

201 rel_obj = self.field.get_cached_value(ancestor, default=None) 

202 else: 

203 rel_obj = None 

204 if rel_obj is None and has_value: 

205 rel_obj = self.get_object(instance) 

206 remote_field = self.field.remote_field 

207 # If this is a one-to-one relation, set the reverse accessor 

208 # cache on the related object to the current instance to avoid 

209 # an extra SQL query if it's accessed later on. 

210 if not remote_field.multiple: 

211 remote_field.set_cached_value(rel_obj, instance) 

212 self.field.set_cached_value(instance, rel_obj) 

213 

214 if rel_obj is None and not self.field.null: 214 ↛ 215line 214 didn't jump to line 215, because the condition on line 214 was never true

215 raise self.RelatedObjectDoesNotExist( 

216 "%s has no %s." % (self.field.model.__name__, self.field.name) 

217 ) 

218 else: 

219 return rel_obj 

220 

221 def __set__(self, instance, value): 

222 """ 

223 Set the related instance through the forward relation. 

224 

225 With the example above, when setting ``child.parent = parent``: 

226 

227 - ``self`` is the descriptor managing the ``parent`` attribute 

228 - ``instance`` is the ``child`` instance 

229 - ``value`` is the ``parent`` instance on the right of the equal sign 

230 """ 

231 # An object must be an instance of the related class. 

232 if value is not None and not isinstance( 232 ↛ 235line 232 didn't jump to line 235, because the condition on line 232 was never true

233 value, self.field.remote_field.model._meta.concrete_model 

234 ): 

235 raise ValueError( 

236 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' 

237 % ( 

238 value, 

239 instance._meta.object_name, 

240 self.field.name, 

241 self.field.remote_field.model._meta.object_name, 

242 ) 

243 ) 

244 elif value is not None: 

245 if instance._state.db is None: 

246 instance._state.db = router.db_for_write( 

247 instance.__class__, instance=value 

248 ) 

249 if value._state.db is None: 249 ↛ 250line 249 didn't jump to line 250, because the condition on line 249 was never true

250 value._state.db = router.db_for_write( 

251 value.__class__, instance=instance 

252 ) 

253 if not router.allow_relation(value, instance): 253 ↛ 254line 253 didn't jump to line 254, because the condition on line 253 was never true

254 raise ValueError( 

255 'Cannot assign "%r": the current database router prevents this ' 

256 "relation." % value 

257 ) 

258 

259 remote_field = self.field.remote_field 

260 # If we're setting the value of a OneToOneField to None, we need to clear 

261 # out the cache on any old related object. Otherwise, deleting the 

262 # previously-related object will also cause this object to be deleted, 

263 # which is wrong. 

264 if value is None: 

265 # Look up the previously-related object, which may still be available 

266 # since we've not yet cleared out the related field. 

267 # Use the cache directly, instead of the accessor; if we haven't 

268 # populated the cache, then we don't care - we're only accessing 

269 # the object to invalidate the accessor cache, so there's no 

270 # need to populate the cache just to expire it again. 

271 related = self.field.get_cached_value(instance, default=None) 

272 

273 # If we've got an old related object, we need to clear out its 

274 # cache. This cache also might not exist if the related object 

275 # hasn't been accessed yet. 

276 if related is not None: 276 ↛ 277line 276 didn't jump to line 277, because the condition on line 276 was never true

277 remote_field.set_cached_value(related, None) 

278 

279 for lh_field, rh_field in self.field.related_fields: 

280 setattr(instance, lh_field.attname, None) 

281 

282 # Set the values of the related field. 

283 else: 

284 for lh_field, rh_field in self.field.related_fields: 

285 setattr(instance, lh_field.attname, getattr(value, rh_field.attname)) 

286 

287 # Set the related instance cache used by __get__ to avoid an SQL query 

288 # when accessing the attribute we just set. 

289 self.field.set_cached_value(instance, value) 

290 

291 # If this is a one-to-one relation, set the reverse accessor cache on 

292 # the related object to the current instance to avoid an extra SQL 

293 # query if it's accessed later on. 

294 if value is not None and not remote_field.multiple: 

295 remote_field.set_cached_value(value, instance) 

296 

297 def __reduce__(self): 

298 """ 

299 Pickling should return the instance attached by self.field on the 

300 model, not a new copy of that descriptor. Use getattr() to retrieve 

301 the instance directly from the model. 

302 """ 

303 return getattr, (self.field.model, self.field.name) 

304 

305 

306class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor): 

307 """ 

308 Accessor to the related object on the forward side of a one-to-one relation. 

309 

310 In the example:: 

311 

312 class Restaurant(Model): 

313 place = OneToOneField(Place, related_name='restaurant') 

314 

315 ``Restaurant.place`` is a ``ForwardOneToOneDescriptor`` instance. 

316 """ 

317 

318 def get_object(self, instance): 

319 if self.field.remote_field.parent_link: 319 ↛ 320line 319 didn't jump to line 320, because the condition on line 319 was never true

320 deferred = instance.get_deferred_fields() 

321 # Because it's a parent link, all the data is available in the 

322 # instance, so populate the parent model with this data. 

323 rel_model = self.field.remote_field.model 

324 fields = [field.attname for field in rel_model._meta.concrete_fields] 

325 

326 # If any of the related model's fields are deferred, fallback to 

327 # fetching all fields from the related model. This avoids a query 

328 # on the related model for every deferred field. 

329 if not any(field in fields for field in deferred): 

330 kwargs = {field: getattr(instance, field) for field in fields} 

331 obj = rel_model(**kwargs) 

332 obj._state.adding = instance._state.adding 

333 obj._state.db = instance._state.db 

334 return obj 

335 return super().get_object(instance) 

336 

337 def __set__(self, instance, value): 

338 super().__set__(instance, value) 

339 # If the primary key is a link to a parent model and a parent instance 

340 # is being set, update the value of the inherited pk(s). 

341 if self.field.primary_key and self.field.remote_field.parent_link: 341 ↛ 342line 341 didn't jump to line 342, because the condition on line 341 was never true

342 opts = instance._meta 

343 # Inherited primary key fields from this object's base classes. 

344 inherited_pk_fields = [ 

345 field 

346 for field in opts.concrete_fields 

347 if field.primary_key and field.remote_field 

348 ] 

349 for field in inherited_pk_fields: 

350 rel_model_pk_name = field.remote_field.model._meta.pk.attname 

351 raw_value = ( 

352 getattr(value, rel_model_pk_name) if value is not None else None 

353 ) 

354 setattr(instance, rel_model_pk_name, raw_value) 

355 

356 

357class ReverseOneToOneDescriptor: 

358 """ 

359 Accessor to the related object on the reverse side of a one-to-one 

360 relation. 

361 

362 In the example:: 

363 

364 class Restaurant(Model): 

365 place = OneToOneField(Place, related_name='restaurant') 

366 

367 ``Place.restaurant`` is a ``ReverseOneToOneDescriptor`` instance. 

368 """ 

369 

370 def __init__(self, related): 

371 # Following the example above, `related` is an instance of OneToOneRel 

372 # which represents the reverse restaurant field (place.restaurant). 

373 self.related = related 

374 

375 @cached_property 

376 def RelatedObjectDoesNotExist(self): 

377 # The exception isn't created at initialization time for the sake of 

378 # consistency with `ForwardManyToOneDescriptor`. 

379 return type( 

380 "RelatedObjectDoesNotExist", 

381 (self.related.related_model.DoesNotExist, AttributeError), 

382 { 

383 "__module__": self.related.model.__module__, 

384 "__qualname__": "%s.%s.RelatedObjectDoesNotExist" 

385 % ( 

386 self.related.model.__qualname__, 

387 self.related.name, 

388 ), 

389 }, 

390 ) 

391 

392 def is_cached(self, instance): 

393 return self.related.is_cached(instance) 

394 

395 def get_queryset(self, **hints): 

396 return self.related.related_model._base_manager.db_manager(hints=hints).all() 

397 

398 def get_prefetch_queryset(self, instances, queryset=None): 

399 if queryset is None: 

400 queryset = self.get_queryset() 

401 queryset._add_hints(instance=instances[0]) 

402 

403 rel_obj_attr = self.related.field.get_local_related_value 

404 instance_attr = self.related.field.get_foreign_related_value 

405 instances_dict = {instance_attr(inst): inst for inst in instances} 

406 query = {"%s__in" % self.related.field.name: instances} 

407 queryset = queryset.filter(**query) 

408 

409 # Since we're going to assign directly in the cache, 

410 # we must manage the reverse relation cache manually. 

411 for rel_obj in queryset: 

412 instance = instances_dict[rel_obj_attr(rel_obj)] 

413 self.related.field.set_cached_value(rel_obj, instance) 

414 return ( 

415 queryset, 

416 rel_obj_attr, 

417 instance_attr, 

418 True, 

419 self.related.get_cache_name(), 

420 False, 

421 ) 

422 

423 def __get__(self, instance, cls=None): 

424 """ 

425 Get the related instance through the reverse relation. 

426 

427 With the example above, when getting ``place.restaurant``: 

428 

429 - ``self`` is the descriptor managing the ``restaurant`` attribute 

430 - ``instance`` is the ``place`` instance 

431 - ``cls`` is the ``Place`` class (unused) 

432 

433 Keep in mind that ``Restaurant`` holds the foreign key to ``Place``. 

434 """ 

435 if instance is None: 

436 return self 

437 

438 # The related instance is loaded from the database and then cached 

439 # by the field on the model instance state. It can also be pre-cached 

440 # by the forward accessor (ForwardManyToOneDescriptor). 

441 try: 

442 rel_obj = self.related.get_cached_value(instance) 

443 except KeyError: 

444 related_pk = instance.pk 

445 if related_pk is None: 445 ↛ 446line 445 didn't jump to line 446, because the condition on line 445 was never true

446 rel_obj = None 

447 else: 

448 filter_args = self.related.field.get_forward_related_filter(instance) 

449 try: 

450 rel_obj = self.get_queryset(instance=instance).get(**filter_args) 

451 except self.related.related_model.DoesNotExist: 

452 rel_obj = None 

453 else: 

454 # Set the forward accessor cache on the related object to 

455 # the current instance to avoid an extra SQL query if it's 

456 # accessed later on. 

457 self.related.field.set_cached_value(rel_obj, instance) 

458 self.related.set_cached_value(instance, rel_obj) 

459 

460 if rel_obj is None: 

461 raise self.RelatedObjectDoesNotExist( 

462 "%s has no %s." 

463 % (instance.__class__.__name__, self.related.get_accessor_name()) 

464 ) 

465 else: 

466 return rel_obj 

467 

468 def __set__(self, instance, value): 

469 """ 

470 Set the related instance through the reverse relation. 

471 

472 With the example above, when setting ``place.restaurant = restaurant``: 

473 

474 - ``self`` is the descriptor managing the ``restaurant`` attribute 

475 - ``instance`` is the ``place`` instance 

476 - ``value`` is the ``restaurant`` instance on the right of the equal sign 

477 

478 Keep in mind that ``Restaurant`` holds the foreign key to ``Place``. 

479 """ 

480 # The similarity of the code below to the code in 

481 # ForwardManyToOneDescriptor is annoying, but there's a bunch 

482 # of small differences that would make a common base class convoluted. 

483 

484 if value is None: 

485 # Update the cached related instance (if any) & clear the cache. 

486 # Following the example above, this would be the cached 

487 # ``restaurant`` instance (if any). 

488 rel_obj = self.related.get_cached_value(instance, default=None) 

489 if rel_obj is not None: 

490 # Remove the ``restaurant`` instance from the ``place`` 

491 # instance cache. 

492 self.related.delete_cached_value(instance) 

493 # Set the ``place`` field on the ``restaurant`` 

494 # instance to None. 

495 setattr(rel_obj, self.related.field.name, None) 

496 elif not isinstance(value, self.related.related_model): 

497 # An object must be an instance of the related class. 

498 raise ValueError( 

499 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' 

500 % ( 

501 value, 

502 instance._meta.object_name, 

503 self.related.get_accessor_name(), 

504 self.related.related_model._meta.object_name, 

505 ) 

506 ) 

507 else: 

508 if instance._state.db is None: 

509 instance._state.db = router.db_for_write( 

510 instance.__class__, instance=value 

511 ) 

512 if value._state.db is None: 

513 value._state.db = router.db_for_write( 

514 value.__class__, instance=instance 

515 ) 

516 if not router.allow_relation(value, instance): 

517 raise ValueError( 

518 'Cannot assign "%r": the current database router prevents this ' 

519 "relation." % value 

520 ) 

521 

522 related_pk = tuple( 

523 getattr(instance, field.attname) 

524 for field in self.related.field.foreign_related_fields 

525 ) 

526 # Set the value of the related field to the value of the related 

527 # object's related field. 

528 for index, field in enumerate(self.related.field.local_related_fields): 

529 setattr(value, field.attname, related_pk[index]) 

530 

531 # Set the related instance cache used by __get__ to avoid an SQL query 

532 # when accessing the attribute we just set. 

533 self.related.set_cached_value(instance, value) 

534 

535 # Set the forward accessor cache on the related object to the current 

536 # instance to avoid an extra SQL query if it's accessed later on. 

537 self.related.field.set_cached_value(value, instance) 

538 

539 def __reduce__(self): 

540 # Same purpose as ForwardManyToOneDescriptor.__reduce__(). 

541 return getattr, (self.related.model, self.related.name) 

542 

543 

544class ReverseManyToOneDescriptor: 

545 """ 

546 Accessor to the related objects manager on the reverse side of a 

547 many-to-one relation. 

548 

549 In the example:: 

550 

551 class Child(Model): 

552 parent = ForeignKey(Parent, related_name='children') 

553 

554 ``Parent.children`` is a ``ReverseManyToOneDescriptor`` instance. 

555 

556 Most of the implementation is delegated to a dynamically defined manager 

557 class built by ``create_forward_many_to_many_manager()`` defined below. 

558 """ 

559 

560 def __init__(self, rel): 

561 self.rel = rel 

562 self.field = rel.field 

563 

564 @cached_property 

565 def related_manager_cls(self): 

566 related_model = self.rel.related_model 

567 

568 return create_reverse_many_to_one_manager( 

569 related_model._default_manager.__class__, 

570 self.rel, 

571 ) 

572 

573 def __get__(self, instance, cls=None): 

574 """ 

575 Get the related objects through the reverse relation. 

576 

577 With the example above, when getting ``parent.children``: 

578 

579 - ``self`` is the descriptor managing the ``children`` attribute 

580 - ``instance`` is the ``parent`` instance 

581 - ``cls`` is the ``Parent`` class (unused) 

582 """ 

583 if instance is None: 583 ↛ 584line 583 didn't jump to line 584, because the condition on line 583 was never true

584 return self 

585 

586 return self.related_manager_cls(instance) 

587 

588 def _get_set_deprecation_msg_params(self): 

589 return ( 

590 "reverse side of a related set", 

591 self.rel.get_accessor_name(), 

592 ) 

593 

594 def __set__(self, instance, value): 

595 raise TypeError( 

596 "Direct assignment to the %s is prohibited. Use %s.set() instead." 

597 % self._get_set_deprecation_msg_params(), 

598 ) 

599 

600 

601def create_reverse_many_to_one_manager(superclass, rel): 

602 """ 

603 Create a manager for the reverse side of a many-to-one relation. 

604 

605 This manager subclasses another manager, generally the default manager of 

606 the related model, and adds behaviors specific to many-to-one relations. 

607 """ 

608 

609 class RelatedManager(superclass): 

610 def __init__(self, instance): 

611 super().__init__() 

612 

613 self.instance = instance 

614 self.model = rel.related_model 

615 self.field = rel.field 

616 

617 self.core_filters = {self.field.name: instance} 

618 

619 def __call__(self, *, manager): 

620 manager = getattr(self.model, manager) 

621 manager_class = create_reverse_many_to_one_manager(manager.__class__, rel) 

622 return manager_class(self.instance) 

623 

624 do_not_call_in_templates = True 

625 

626 def _apply_rel_filters(self, queryset): 

627 """ 

628 Filter the queryset for the instance this manager is bound to. 

629 """ 

630 db = self._db or router.db_for_read(self.model, instance=self.instance) 

631 empty_strings_as_null = connections[ 

632 db 

633 ].features.interprets_empty_strings_as_nulls 

634 queryset._add_hints(instance=self.instance) 

635 if self._db: 635 ↛ 636line 635 didn't jump to line 636, because the condition on line 635 was never true

636 queryset = queryset.using(self._db) 

637 queryset._defer_next_filter = True 

638 queryset = queryset.filter(**self.core_filters) 

639 for field in self.field.foreign_related_fields: 

640 val = getattr(self.instance, field.attname) 

641 if val is None or (val == "" and empty_strings_as_null): 

642 return queryset.none() 

643 if self.field.many_to_one: 643 ↛ 666line 643 didn't jump to line 666, because the condition on line 643 was never false

644 # Guard against field-like objects such as GenericRelation 

645 # that abuse create_reverse_many_to_one_manager() with reverse 

646 # one-to-many relationships instead and break known related 

647 # objects assignment. 

648 try: 

649 target_field = self.field.target_field 

650 except FieldError: 

651 # The relationship has multiple target fields. Use a tuple 

652 # for related object id. 

653 rel_obj_id = tuple( 

654 [ 

655 getattr(self.instance, target_field.attname) 

656 for target_field in self.field.get_path_info()[ 

657 -1 

658 ].target_fields 

659 ] 

660 ) 

661 else: 

662 rel_obj_id = getattr(self.instance, target_field.attname) 

663 queryset._known_related_objects = { 

664 self.field: {rel_obj_id: self.instance} 

665 } 

666 return queryset 

667 

668 def _remove_prefetched_objects(self): 

669 try: 

670 self.instance._prefetched_objects_cache.pop( 

671 self.field.remote_field.get_cache_name() 

672 ) 

673 except (AttributeError, KeyError): 

674 pass # nothing to clear from cache 

675 

676 def get_queryset(self): 

677 try: 

678 return self.instance._prefetched_objects_cache[ 

679 self.field.remote_field.get_cache_name() 

680 ] 

681 except (AttributeError, KeyError): 

682 queryset = super().get_queryset() 

683 return self._apply_rel_filters(queryset) 

684 

685 def get_prefetch_queryset(self, instances, queryset=None): 

686 if queryset is None: 

687 queryset = super().get_queryset() 

688 

689 queryset._add_hints(instance=instances[0]) 

690 queryset = queryset.using(queryset._db or self._db) 

691 

692 rel_obj_attr = self.field.get_local_related_value 

693 instance_attr = self.field.get_foreign_related_value 

694 instances_dict = {instance_attr(inst): inst for inst in instances} 

695 query = {"%s__in" % self.field.name: instances} 

696 queryset = queryset.filter(**query) 

697 

698 # Since we just bypassed this class' get_queryset(), we must manage 

699 # the reverse relation manually. 

700 for rel_obj in queryset: 

701 instance = instances_dict[rel_obj_attr(rel_obj)] 

702 setattr(rel_obj, self.field.name, instance) 

703 cache_name = self.field.remote_field.get_cache_name() 

704 return queryset, rel_obj_attr, instance_attr, False, cache_name, False 

705 

706 def add(self, *objs, bulk=True): 

707 self._remove_prefetched_objects() 

708 db = router.db_for_write(self.model, instance=self.instance) 

709 

710 def check_and_update_obj(obj): 

711 if not isinstance(obj, self.model): 

712 raise TypeError( 

713 "'%s' instance expected, got %r" 

714 % ( 

715 self.model._meta.object_name, 

716 obj, 

717 ) 

718 ) 

719 setattr(obj, self.field.name, self.instance) 

720 

721 if bulk: 

722 pks = [] 

723 for obj in objs: 

724 check_and_update_obj(obj) 

725 if obj._state.adding or obj._state.db != db: 

726 raise ValueError( 

727 "%r instance isn't saved. Use bulk=False or save " 

728 "the object first." % obj 

729 ) 

730 pks.append(obj.pk) 

731 self.model._base_manager.using(db).filter(pk__in=pks).update( 

732 **{ 

733 self.field.name: self.instance, 

734 } 

735 ) 

736 else: 

737 with transaction.atomic(using=db, savepoint=False): 

738 for obj in objs: 

739 check_and_update_obj(obj) 

740 obj.save() 

741 

742 add.alters_data = True 

743 

744 def create(self, **kwargs): 

745 kwargs[self.field.name] = self.instance 

746 db = router.db_for_write(self.model, instance=self.instance) 

747 return super(RelatedManager, self.db_manager(db)).create(**kwargs) 

748 

749 create.alters_data = True 

750 

751 def get_or_create(self, **kwargs): 

752 kwargs[self.field.name] = self.instance 

753 db = router.db_for_write(self.model, instance=self.instance) 

754 return super(RelatedManager, self.db_manager(db)).get_or_create(**kwargs) 

755 

756 get_or_create.alters_data = True 

757 

758 def update_or_create(self, **kwargs): 

759 kwargs[self.field.name] = self.instance 

760 db = router.db_for_write(self.model, instance=self.instance) 

761 return super(RelatedManager, self.db_manager(db)).update_or_create(**kwargs) 

762 

763 update_or_create.alters_data = True 

764 

765 # remove() and clear() are only provided if the ForeignKey can have a 

766 # value of null. 

767 if rel.field.null: 

768 

769 def remove(self, *objs, bulk=True): 

770 if not objs: 

771 return 

772 val = self.field.get_foreign_related_value(self.instance) 

773 old_ids = set() 

774 for obj in objs: 

775 if not isinstance(obj, self.model): 

776 raise TypeError( 

777 "'%s' instance expected, got %r" 

778 % ( 

779 self.model._meta.object_name, 

780 obj, 

781 ) 

782 ) 

783 # Is obj actually part of this descriptor set? 

784 if self.field.get_local_related_value(obj) == val: 

785 old_ids.add(obj.pk) 

786 else: 

787 raise self.field.remote_field.model.DoesNotExist( 

788 "%r is not related to %r." % (obj, self.instance) 

789 ) 

790 self._clear(self.filter(pk__in=old_ids), bulk) 

791 

792 remove.alters_data = True 

793 

794 def clear(self, *, bulk=True): 

795 self._clear(self, bulk) 

796 

797 clear.alters_data = True 

798 

799 def _clear(self, queryset, bulk): 

800 self._remove_prefetched_objects() 

801 db = router.db_for_write(self.model, instance=self.instance) 

802 queryset = queryset.using(db) 

803 if bulk: 

804 # `QuerySet.update()` is intrinsically atomic. 

805 queryset.update(**{self.field.name: None}) 

806 else: 

807 with transaction.atomic(using=db, savepoint=False): 

808 for obj in queryset: 

809 setattr(obj, self.field.name, None) 

810 obj.save(update_fields=[self.field.name]) 

811 

812 _clear.alters_data = True 

813 

814 def set(self, objs, *, bulk=True, clear=False): 

815 # Force evaluation of `objs` in case it's a queryset whose value 

816 # could be affected by `manager.clear()`. Refs #19816. 

817 objs = tuple(objs) 

818 

819 if self.field.null: 

820 db = router.db_for_write(self.model, instance=self.instance) 

821 with transaction.atomic(using=db, savepoint=False): 

822 if clear: 

823 self.clear(bulk=bulk) 

824 self.add(*objs, bulk=bulk) 

825 else: 

826 old_objs = set(self.using(db).all()) 

827 new_objs = [] 

828 for obj in objs: 

829 if obj in old_objs: 

830 old_objs.remove(obj) 

831 else: 

832 new_objs.append(obj) 

833 

834 self.remove(*old_objs, bulk=bulk) 

835 self.add(*new_objs, bulk=bulk) 

836 else: 

837 self.add(*objs, bulk=bulk) 

838 

839 set.alters_data = True 

840 

841 return RelatedManager 

842 

843 

844class ManyToManyDescriptor(ReverseManyToOneDescriptor): 

845 """ 

846 Accessor to the related objects manager on the forward and reverse sides of 

847 a many-to-many relation. 

848 

849 In the example:: 

850 

851 class Pizza(Model): 

852 toppings = ManyToManyField(Topping, related_name='pizzas') 

853 

854 ``Pizza.toppings`` and ``Topping.pizzas`` are ``ManyToManyDescriptor`` 

855 instances. 

856 

857 Most of the implementation is delegated to a dynamically defined manager 

858 class built by ``create_forward_many_to_many_manager()`` defined below. 

859 """ 

860 

861 def __init__(self, rel, reverse=False): 

862 super().__init__(rel) 

863 

864 self.reverse = reverse 

865 

866 @property 

867 def through(self): 

868 # through is provided so that you have easy access to the through 

869 # model (Book.authors.through) for inlines, etc. This is done as 

870 # a property to ensure that the fully resolved value is returned. 

871 return self.rel.through 

872 

873 @cached_property 

874 def related_manager_cls(self): 

875 related_model = self.rel.related_model if self.reverse else self.rel.model 

876 

877 return create_forward_many_to_many_manager( 

878 related_model._default_manager.__class__, 

879 self.rel, 

880 reverse=self.reverse, 

881 ) 

882 

883 def _get_set_deprecation_msg_params(self): 

884 return ( 

885 "%s side of a many-to-many set" 

886 % ("reverse" if self.reverse else "forward"), 

887 self.rel.get_accessor_name() if self.reverse else self.field.name, 

888 ) 

889 

890 

891def create_forward_many_to_many_manager(superclass, rel, reverse): 

892 """ 

893 Create a manager for the either side of a many-to-many relation. 

894 

895 This manager subclasses another manager, generally the default manager of 

896 the related model, and adds behaviors specific to many-to-many relations. 

897 """ 

898 

899 class ManyRelatedManager(superclass): 

900 def __init__(self, instance=None): 

901 super().__init__() 

902 

903 self.instance = instance 

904 

905 if not reverse: 

906 self.model = rel.model 

907 self.query_field_name = rel.field.related_query_name() 

908 self.prefetch_cache_name = rel.field.name 

909 self.source_field_name = rel.field.m2m_field_name() 

910 self.target_field_name = rel.field.m2m_reverse_field_name() 

911 self.symmetrical = rel.symmetrical 

912 else: 

913 self.model = rel.related_model 

914 self.query_field_name = rel.field.name 

915 self.prefetch_cache_name = rel.field.related_query_name() 

916 self.source_field_name = rel.field.m2m_reverse_field_name() 

917 self.target_field_name = rel.field.m2m_field_name() 

918 self.symmetrical = False 

919 

920 self.through = rel.through 

921 self.reverse = reverse 

922 

923 self.source_field = self.through._meta.get_field(self.source_field_name) 

924 self.target_field = self.through._meta.get_field(self.target_field_name) 

925 

926 self.core_filters = {} 

927 self.pk_field_names = {} 

928 for lh_field, rh_field in self.source_field.related_fields: 

929 core_filter_key = "%s__%s" % (self.query_field_name, rh_field.name) 

930 self.core_filters[core_filter_key] = getattr(instance, rh_field.attname) 

931 self.pk_field_names[lh_field.name] = rh_field.name 

932 

933 self.related_val = self.source_field.get_foreign_related_value(instance) 

934 if None in self.related_val: 934 ↛ 935line 934 didn't jump to line 935, because the condition on line 934 was never true

935 raise ValueError( 

936 '"%r" needs to have a value for field "%s" before ' 

937 "this many-to-many relationship can be used." 

938 % (instance, self.pk_field_names[self.source_field_name]) 

939 ) 

940 # Even if this relation is not to pk, we require still pk value. 

941 # The wish is that the instance has been already saved to DB, 

942 # although having a pk value isn't a guarantee of that. 

943 if instance.pk is None: 943 ↛ 944line 943 didn't jump to line 944, because the condition on line 943 was never true

944 raise ValueError( 

945 "%r instance needs to have a primary key value before " 

946 "a many-to-many relationship can be used." 

947 % instance.__class__.__name__ 

948 ) 

949 

950 def __call__(self, *, manager): 

951 manager = getattr(self.model, manager) 

952 manager_class = create_forward_many_to_many_manager( 

953 manager.__class__, rel, reverse 

954 ) 

955 return manager_class(instance=self.instance) 

956 

957 do_not_call_in_templates = True 

958 

959 def _build_remove_filters(self, removed_vals): 

960 filters = Q((self.source_field_name, self.related_val)) 

961 # No need to add a subquery condition if removed_vals is a QuerySet without 

962 # filters. 

963 removed_vals_filters = ( 

964 not isinstance(removed_vals, QuerySet) or removed_vals._has_filters() 

965 ) 

966 if removed_vals_filters: 

967 filters &= Q((f"{self.target_field_name}__in", removed_vals)) 

968 if self.symmetrical: 

969 symmetrical_filters = Q((self.target_field_name, self.related_val)) 

970 if removed_vals_filters: 

971 symmetrical_filters &= Q( 

972 (f"{self.source_field_name}__in", removed_vals) 

973 ) 

974 filters |= symmetrical_filters 

975 return filters 

976 

977 def _apply_rel_filters(self, queryset): 

978 """ 

979 Filter the queryset for the instance this manager is bound to. 

980 """ 

981 queryset._add_hints(instance=self.instance) 

982 if self._db: 982 ↛ 983line 982 didn't jump to line 983, because the condition on line 982 was never true

983 queryset = queryset.using(self._db) 

984 queryset._defer_next_filter = True 

985 return queryset._next_is_sticky().filter(**self.core_filters) 

986 

987 def _remove_prefetched_objects(self): 

988 try: 

989 self.instance._prefetched_objects_cache.pop(self.prefetch_cache_name) 

990 except (AttributeError, KeyError): 

991 pass # nothing to clear from cache 

992 

993 def get_queryset(self): 

994 try: 

995 return self.instance._prefetched_objects_cache[self.prefetch_cache_name] 

996 except (AttributeError, KeyError): 

997 queryset = super().get_queryset() 

998 return self._apply_rel_filters(queryset) 

999 

1000 def get_prefetch_queryset(self, instances, queryset=None): 

1001 if queryset is None: 

1002 queryset = super().get_queryset() 

1003 

1004 queryset._add_hints(instance=instances[0]) 

1005 queryset = queryset.using(queryset._db or self._db) 

1006 

1007 query = {"%s__in" % self.query_field_name: instances} 

1008 queryset = queryset._next_is_sticky().filter(**query) 

1009 

1010 # M2M: need to annotate the query in order to get the primary model 

1011 # that the secondary model was actually related to. We know that 

1012 # there will already be a join on the join table, so we can just add 

1013 # the select. 

1014 

1015 # For non-autocreated 'through' models, can't assume we are 

1016 # dealing with PK values. 

1017 fk = self.through._meta.get_field(self.source_field_name) 

1018 join_table = fk.model._meta.db_table 

1019 connection = connections[queryset.db] 

1020 qn = connection.ops.quote_name 

1021 queryset = queryset.extra( 

1022 select={ 

1023 "_prefetch_related_val_%s" 

1024 % f.attname: "%s.%s" 

1025 % (qn(join_table), qn(f.column)) 

1026 for f in fk.local_related_fields 

1027 } 

1028 ) 

1029 return ( 

1030 queryset, 

1031 lambda result: tuple( 

1032 getattr(result, "_prefetch_related_val_%s" % f.attname) 

1033 for f in fk.local_related_fields 

1034 ), 

1035 lambda inst: tuple( 

1036 f.get_db_prep_value(getattr(inst, f.attname), connection) 

1037 for f in fk.foreign_related_fields 

1038 ), 

1039 False, 

1040 self.prefetch_cache_name, 

1041 False, 

1042 ) 

1043 

1044 def add(self, *objs, through_defaults=None): 

1045 self._remove_prefetched_objects() 

1046 db = router.db_for_write(self.through, instance=self.instance) 

1047 with transaction.atomic(using=db, savepoint=False): 

1048 self._add_items( 

1049 self.source_field_name, 

1050 self.target_field_name, 

1051 *objs, 

1052 through_defaults=through_defaults, 

1053 ) 

1054 # If this is a symmetrical m2m relation to self, add the mirror 

1055 # entry in the m2m table. 

1056 if self.symmetrical: 1056 ↛ 1057line 1056 didn't jump to line 1057, because the condition on line 1056 was never true

1057 self._add_items( 

1058 self.target_field_name, 

1059 self.source_field_name, 

1060 *objs, 

1061 through_defaults=through_defaults, 

1062 ) 

1063 

1064 add.alters_data = True 

1065 

1066 def remove(self, *objs): 

1067 self._remove_prefetched_objects() 

1068 self._remove_items(self.source_field_name, self.target_field_name, *objs) 

1069 

1070 remove.alters_data = True 

1071 

1072 def clear(self): 

1073 db = router.db_for_write(self.through, instance=self.instance) 

1074 with transaction.atomic(using=db, savepoint=False): 

1075 signals.m2m_changed.send( 

1076 sender=self.through, 

1077 action="pre_clear", 

1078 instance=self.instance, 

1079 reverse=self.reverse, 

1080 model=self.model, 

1081 pk_set=None, 

1082 using=db, 

1083 ) 

1084 self._remove_prefetched_objects() 

1085 filters = self._build_remove_filters(super().get_queryset().using(db)) 

1086 self.through._default_manager.using(db).filter(filters).delete() 

1087 

1088 signals.m2m_changed.send( 

1089 sender=self.through, 

1090 action="post_clear", 

1091 instance=self.instance, 

1092 reverse=self.reverse, 

1093 model=self.model, 

1094 pk_set=None, 

1095 using=db, 

1096 ) 

1097 

1098 clear.alters_data = True 

1099 

1100 def set(self, objs, *, clear=False, through_defaults=None): 

1101 # Force evaluation of `objs` in case it's a queryset whose value 

1102 # could be affected by `manager.clear()`. Refs #19816. 

1103 objs = tuple(objs) 

1104 

1105 db = router.db_for_write(self.through, instance=self.instance) 

1106 with transaction.atomic(using=db, savepoint=False): 

1107 if clear: 1107 ↛ 1108line 1107 didn't jump to line 1108, because the condition on line 1107 was never true

1108 self.clear() 

1109 self.add(*objs, through_defaults=through_defaults) 

1110 else: 

1111 old_ids = set( 

1112 self.using(db).values_list( 

1113 self.target_field.target_field.attname, flat=True 

1114 ) 

1115 ) 

1116 

1117 new_objs = [] 

1118 for obj in objs: 

1119 fk_val = ( 

1120 self.target_field.get_foreign_related_value(obj)[0] 

1121 if isinstance(obj, self.model) 

1122 else self.target_field.get_prep_value(obj) 

1123 ) 

1124 if fk_val in old_ids: 1124 ↛ 1125line 1124 didn't jump to line 1125, because the condition on line 1124 was never true

1125 old_ids.remove(fk_val) 

1126 else: 

1127 new_objs.append(obj) 

1128 

1129 self.remove(*old_ids) 

1130 self.add(*new_objs, through_defaults=through_defaults) 

1131 

1132 set.alters_data = True 

1133 

1134 def create(self, *, through_defaults=None, **kwargs): 

1135 db = router.db_for_write(self.instance.__class__, instance=self.instance) 

1136 new_obj = super(ManyRelatedManager, self.db_manager(db)).create(**kwargs) 

1137 self.add(new_obj, through_defaults=through_defaults) 

1138 return new_obj 

1139 

1140 create.alters_data = True 

1141 

1142 def get_or_create(self, *, through_defaults=None, **kwargs): 

1143 db = router.db_for_write(self.instance.__class__, instance=self.instance) 

1144 obj, created = super(ManyRelatedManager, self.db_manager(db)).get_or_create( 

1145 **kwargs 

1146 ) 

1147 # We only need to add() if created because if we got an object back 

1148 # from get() then the relationship already exists. 

1149 if created: 

1150 self.add(obj, through_defaults=through_defaults) 

1151 return obj, created 

1152 

1153 get_or_create.alters_data = True 

1154 

1155 def update_or_create(self, *, through_defaults=None, **kwargs): 

1156 db = router.db_for_write(self.instance.__class__, instance=self.instance) 

1157 obj, created = super( 

1158 ManyRelatedManager, self.db_manager(db) 

1159 ).update_or_create(**kwargs) 

1160 # We only need to add() if created because if we got an object back 

1161 # from get() then the relationship already exists. 

1162 if created: 

1163 self.add(obj, through_defaults=through_defaults) 

1164 return obj, created 

1165 

1166 update_or_create.alters_data = True 

1167 

1168 def _get_target_ids(self, target_field_name, objs): 

1169 """ 

1170 Return the set of ids of `objs` that the target field references. 

1171 """ 

1172 from django.db.models import Model 

1173 

1174 target_ids = set() 

1175 target_field = self.through._meta.get_field(target_field_name) 

1176 for obj in objs: 

1177 if isinstance(obj, self.model): 1177 ↛ 1191line 1177 didn't jump to line 1191, because the condition on line 1177 was never false

1178 if not router.allow_relation(obj, self.instance): 1178 ↛ 1179line 1178 didn't jump to line 1179, because the condition on line 1178 was never true

1179 raise ValueError( 

1180 'Cannot add "%r": instance is on database "%s", ' 

1181 'value is on database "%s"' 

1182 % (obj, self.instance._state.db, obj._state.db) 

1183 ) 

1184 target_id = target_field.get_foreign_related_value(obj)[0] 

1185 if target_id is None: 1185 ↛ 1186line 1185 didn't jump to line 1186, because the condition on line 1185 was never true

1186 raise ValueError( 

1187 'Cannot add "%r": the value for field "%s" is None' 

1188 % (obj, target_field_name) 

1189 ) 

1190 target_ids.add(target_id) 

1191 elif isinstance(obj, Model): 

1192 raise TypeError( 

1193 "'%s' instance expected, got %r" 

1194 % (self.model._meta.object_name, obj) 

1195 ) 

1196 else: 

1197 target_ids.add(target_field.get_prep_value(obj)) 

1198 return target_ids 

1199 

1200 def _get_missing_target_ids( 

1201 self, source_field_name, target_field_name, db, target_ids 

1202 ): 

1203 """ 

1204 Return the subset of ids of `objs` that aren't already assigned to 

1205 this relationship. 

1206 """ 

1207 vals = ( 

1208 self.through._default_manager.using(db) 

1209 .values_list(target_field_name, flat=True) 

1210 .filter( 

1211 **{ 

1212 source_field_name: self.related_val[0], 

1213 "%s__in" % target_field_name: target_ids, 

1214 } 

1215 ) 

1216 ) 

1217 return target_ids.difference(vals) 

1218 

1219 def _get_add_plan(self, db, source_field_name): 

1220 """ 

1221 Return a boolean triple of the way the add should be performed. 

1222 

1223 The first element is whether or not bulk_create(ignore_conflicts) 

1224 can be used, the second whether or not signals must be sent, and 

1225 the third element is whether or not the immediate bulk insertion 

1226 with conflicts ignored can be performed. 

1227 """ 

1228 # Conflicts can be ignored when the intermediary model is 

1229 # auto-created as the only possible collision is on the 

1230 # (source_id, target_id) tuple. The same assertion doesn't hold for 

1231 # user-defined intermediary models as they could have other fields 

1232 # causing conflicts which must be surfaced. 

1233 can_ignore_conflicts = ( 

1234 connections[db].features.supports_ignore_conflicts 

1235 and self.through._meta.auto_created is not False 

1236 ) 

1237 # Don't send the signal when inserting duplicate data row 

1238 # for symmetrical reverse entries. 

1239 must_send_signals = ( 

1240 self.reverse or source_field_name == self.source_field_name 

1241 ) and (signals.m2m_changed.has_listeners(self.through)) 

1242 # Fast addition through bulk insertion can only be performed 

1243 # if no m2m_changed listeners are connected for self.through 

1244 # as they require the added set of ids to be provided via 

1245 # pk_set. 

1246 return ( 

1247 can_ignore_conflicts, 

1248 must_send_signals, 

1249 (can_ignore_conflicts and not must_send_signals), 

1250 ) 

1251 

1252 def _add_items( 

1253 self, source_field_name, target_field_name, *objs, through_defaults=None 

1254 ): 

1255 # source_field_name: the PK fieldname in join table for the source object 

1256 # target_field_name: the PK fieldname in join table for the target object 

1257 # *objs - objects to add. Either object instances, or primary keys 

1258 # of object instances. 

1259 if not objs: 

1260 return 

1261 

1262 through_defaults = dict(resolve_callables(through_defaults or {})) 

1263 target_ids = self._get_target_ids(target_field_name, objs) 

1264 db = router.db_for_write(self.through, instance=self.instance) 

1265 can_ignore_conflicts, must_send_signals, can_fast_add = self._get_add_plan( 

1266 db, source_field_name 

1267 ) 

1268 if can_fast_add: 

1269 self.through._default_manager.using(db).bulk_create( 

1270 [ 

1271 self.through( 

1272 **{ 

1273 "%s_id" % source_field_name: self.related_val[0], 

1274 "%s_id" % target_field_name: target_id, 

1275 } 

1276 ) 

1277 for target_id in target_ids 

1278 ], 

1279 ignore_conflicts=True, 

1280 ) 

1281 return 

1282 

1283 missing_target_ids = self._get_missing_target_ids( 

1284 source_field_name, target_field_name, db, target_ids 

1285 ) 

1286 with transaction.atomic(using=db, savepoint=False): 

1287 if must_send_signals: 1287 ↛ 1288line 1287 didn't jump to line 1288, because the condition on line 1287 was never true

1288 signals.m2m_changed.send( 

1289 sender=self.through, 

1290 action="pre_add", 

1291 instance=self.instance, 

1292 reverse=self.reverse, 

1293 model=self.model, 

1294 pk_set=missing_target_ids, 

1295 using=db, 

1296 ) 

1297 # Add the ones that aren't there already. 

1298 self.through._default_manager.using(db).bulk_create( 

1299 [ 

1300 self.through( 

1301 **through_defaults, 

1302 **{ 

1303 "%s_id" % source_field_name: self.related_val[0], 

1304 "%s_id" % target_field_name: target_id, 

1305 }, 

1306 ) 

1307 for target_id in missing_target_ids 

1308 ], 

1309 ignore_conflicts=can_ignore_conflicts, 

1310 ) 

1311 

1312 if must_send_signals: 1312 ↛ 1313line 1312 didn't jump to line 1313, because the condition on line 1312 was never true

1313 signals.m2m_changed.send( 

1314 sender=self.through, 

1315 action="post_add", 

1316 instance=self.instance, 

1317 reverse=self.reverse, 

1318 model=self.model, 

1319 pk_set=missing_target_ids, 

1320 using=db, 

1321 ) 

1322 

1323 def _remove_items(self, source_field_name, target_field_name, *objs): 

1324 # source_field_name: the PK colname in join table for the source object 

1325 # target_field_name: the PK colname in join table for the target object 

1326 # *objs - objects to remove. Either object instances, or primary 

1327 # keys of object instances. 

1328 if not objs: 1328 ↛ 1332line 1328 didn't jump to line 1332, because the condition on line 1328 was never false

1329 return 

1330 

1331 # Check that all the objects are of the right type 

1332 old_ids = set() 

1333 for obj in objs: 

1334 if isinstance(obj, self.model): 

1335 fk_val = self.target_field.get_foreign_related_value(obj)[0] 

1336 old_ids.add(fk_val) 

1337 else: 

1338 old_ids.add(obj) 

1339 

1340 db = router.db_for_write(self.through, instance=self.instance) 

1341 with transaction.atomic(using=db, savepoint=False): 

1342 # Send a signal to the other end if need be. 

1343 signals.m2m_changed.send( 

1344 sender=self.through, 

1345 action="pre_remove", 

1346 instance=self.instance, 

1347 reverse=self.reverse, 

1348 model=self.model, 

1349 pk_set=old_ids, 

1350 using=db, 

1351 ) 

1352 target_model_qs = super().get_queryset() 

1353 if target_model_qs._has_filters(): 

1354 old_vals = target_model_qs.using(db).filter( 

1355 **{"%s__in" % self.target_field.target_field.attname: old_ids} 

1356 ) 

1357 else: 

1358 old_vals = old_ids 

1359 filters = self._build_remove_filters(old_vals) 

1360 self.through._default_manager.using(db).filter(filters).delete() 

1361 

1362 signals.m2m_changed.send( 

1363 sender=self.through, 

1364 action="post_remove", 

1365 instance=self.instance, 

1366 reverse=self.reverse, 

1367 model=self.model, 

1368 pk_set=old_ids, 

1369 using=db, 

1370 ) 

1371 

1372 return ManyRelatedManager