mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Refs #19544 -- Ignored auto-created through additions conflicts if supported.
This prevents IntegrityError caused by race conditions between missing ids retrieval and bulk insertions.
This commit is contained in:
parent
dd32f9a3a2
commit
28712d8acf
@ -1110,14 +1110,20 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
|||||||
model=self.model, pk_set=missing_target_ids, using=db,
|
model=self.model, pk_set=missing_target_ids, using=db,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the ones that aren't there already
|
# Add the ones that aren't there already. Conflicts can be
|
||||||
|
# ignored when the intermediary model is auto-created as
|
||||||
|
# the only possible collision is on the (sid_id, tid_id)
|
||||||
|
# tuple. The same assertion doesn't hold for user-defined
|
||||||
|
# intermediary models as they could have other fields
|
||||||
|
# causing conflicts which must be surfaced.
|
||||||
|
ignore_conflicts = self.through._meta.auto_created is not False
|
||||||
self.through._default_manager.using(db).bulk_create([
|
self.through._default_manager.using(db).bulk_create([
|
||||||
self.through(**through_defaults, **{
|
self.through(**through_defaults, **{
|
||||||
'%s_id' % source_field_name: self.related_val[0],
|
'%s_id' % source_field_name: self.related_val[0],
|
||||||
'%s_id' % target_field_name: target_id,
|
'%s_id' % target_field_name: target_id,
|
||||||
})
|
})
|
||||||
for target_id in missing_target_ids
|
for target_id in missing_target_ids
|
||||||
])
|
], ignore_conflicts=ignore_conflicts)
|
||||||
|
|
||||||
if self.reverse or source_field_name == self.source_field_name:
|
if self.reverse or source_field_name == self.source_field_name:
|
||||||
# Don't send the signal when we are inserting the
|
# Don't send the signal when we are inserting the
|
||||||
|
@ -117,6 +117,16 @@ class ManyToManyTests(TestCase):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature('supports_ignore_conflicts')
|
||||||
|
def test_add_ignore_conflicts(self):
|
||||||
|
manager_cls = self.a1.publications.__class__
|
||||||
|
# Simulate a race condition between the missing ids retrieval and
|
||||||
|
# the bulk insertion attempt.
|
||||||
|
missing_target_ids = {self.p1.id}
|
||||||
|
with mock.patch.object(manager_cls, '_get_missing_target_ids', return_value=missing_target_ids) as mocked:
|
||||||
|
self.a1.publications.add(self.p1)
|
||||||
|
mocked.assert_called_once()
|
||||||
|
|
||||||
def test_related_sets(self):
|
def test_related_sets(self):
|
||||||
# Article objects have access to their related Publication objects.
|
# Article objects have access to their related Publication objects.
|
||||||
self.assertQuerysetEqual(self.a1.publications.all(), ['<Publication: The Python Journal>'])
|
self.assertQuerysetEqual(self.a1.publications.all(), ['<Publication: The Python Journal>'])
|
||||||
|
Loading…
Reference in New Issue
Block a user