Fixed #8467 -- Prevented crash when adding existent m2m relation with an invalid type.

This was an issue anymore on backends that allows conflicts to be
ignored (Refs #19544) as long the provided values were coercible to the
expected type. However on the remaining backends that don't support
this feature, namely Oracle, this could still result in an
IntegrityError.

By attempting to coerce the provided values to the expected types in
Python beforehand we allow the existing value set intersection in
ManyRelatedManager._get_missing_target_ids to prevent the problematic
insertion attempts.

Thanks Baptiste Mispelon for triaging this old ticket against the
current state of the master branch.
This commit is contained in:
Simon Charette 2019-11-21 20:02:57 -05:00 committed by Mariusz Felisiak
parent 8cc711999a
commit 379bf1a2d4
2 changed files with 10 additions and 2 deletions

View File

@ -1064,7 +1064,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
(self.model._meta.object_name, obj)
)
else:
target_ids.add(obj)
target_ids.add(target_field.get_prep_value(obj))
return target_ids
def _get_missing_target_ids(self, source_field_name, target_field_name, db, target_ids):

View File

@ -1,7 +1,7 @@
from unittest import mock
from django.db import transaction
from django.test import TestCase, skipUnlessDBFeature
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from .models import (
Article, InheritedArticleA, InheritedArticleB, Publication, User,
@ -158,6 +158,14 @@ class ManyToManyTests(TestCase):
with self.assertNumQueries(1):
self.a1.publications.add(self.p1, self.p2)
@skipIfDBFeature('supports_ignore_conflicts')
def test_add_existing_different_type(self):
# A single SELECT query is necessary to compare existing values to the
# provided one; no INSERT should be attempted.
with self.assertNumQueries(1):
self.a1.publications.add(str(self.p1.pk))
self.assertEqual(self.a1.publications.get(), self.p1)
@skipUnlessDBFeature('supports_ignore_conflicts')
def test_slow_add_ignore_conflicts(self):
manager_cls = self.a1.publications.__class__