mirror of
https://github.com/django/django.git
synced 2025-09-09 04:00:48 +00:00
Fixed #22217 - ManyToManyField.through_fields fixes.
- Docs description of arguments mix up. - Keep it from erroneously masking E332 check. - Add checks E338 and E339, tweak message of E337.
This commit is contained in:
parent
f4d91638fc
commit
aaad3e27ac
5 changed files with 154 additions and 49 deletions
|
@ -240,6 +240,32 @@ class RelativeFieldTests(IsolatedModelsTestCase):
|
|||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_symmetric_self_reference_with_intermediate_table_and_through_fields(self):
|
||||
"""Using through_fields in a m2m with an intermediate model shouldn't mask its incompatibility with symmetry."""
|
||||
class Person(models.Model):
|
||||
# Explicit symmetrical=True.
|
||||
friends = models.ManyToManyField('self',
|
||||
symmetrical=True,
|
||||
through="Relationship",
|
||||
through_fields=('first', 'second'))
|
||||
|
||||
class Relationship(models.Model):
|
||||
first = models.ForeignKey(Person, related_name="rel_from_set")
|
||||
second = models.ForeignKey(Person, related_name="rel_to_set")
|
||||
referee = models.ForeignKey(Person, related_name="referred")
|
||||
|
||||
field = Person._meta.get_field('friends')
|
||||
errors = field.check(from_model=Person)
|
||||
expected = [
|
||||
Error(
|
||||
'Many-to-many fields with intermediate tables must not be symmetrical.',
|
||||
hint=None,
|
||||
obj=field,
|
||||
id='fields.E332',
|
||||
),
|
||||
]
|
||||
self.assertEqual(errors, expected)
|
||||
|
||||
def test_foreign_key_to_abstract_model(self):
|
||||
class Model(models.Model):
|
||||
foreign_key = models.ForeignKey('AbstractModel')
|
||||
|
@ -1071,43 +1097,69 @@ class M2mThroughFieldsTests(IsolatedModelsTestCase):
|
|||
ValueError, 'Cannot specify through_fields without a through model',
|
||||
models.ManyToManyField, Fan, through_fields=('f1', 'f2'))
|
||||
|
||||
def test_invalid_m2m_field(self):
|
||||
def test_invalid_order(self):
|
||||
"""
|
||||
Tests that providing invalid source field name to ManyToManyField.through_fields
|
||||
raises FieldDoesNotExist.
|
||||
Tests that mixing up the order of link fields to ManyToManyField.through_fields
|
||||
triggers validation errors.
|
||||
"""
|
||||
class Fan(models.Model):
|
||||
pass
|
||||
|
||||
class Event(models.Model):
|
||||
invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invalid_field', 'invitee'))
|
||||
invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invitee', 'event'))
|
||||
|
||||
class Invitation(models.Model):
|
||||
event = models.ForeignKey(Event)
|
||||
invitee = models.ForeignKey(Fan)
|
||||
inviter = models.ForeignKey(Fan, related_name='+')
|
||||
|
||||
with self.assertRaisesMessage(FieldDoesNotExist, 'invalid_field'):
|
||||
Event().invitees.all()
|
||||
field = Event._meta.get_field('invitees')
|
||||
errors = field.check(from_model=Event)
|
||||
expected = [
|
||||
Error(
|
||||
("'Invitation.invitee' is not a foreign key to 'Event'."),
|
||||
hint="Did you mean one of the following foreign keys to 'Event': event?",
|
||||
obj=field,
|
||||
id='fields.E339'),
|
||||
Error(
|
||||
("'Invitation.event' is not a foreign key to 'Fan'."),
|
||||
hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?",
|
||||
obj=field,
|
||||
id='fields.E339'),
|
||||
]
|
||||
self.assertEqual(expected, errors)
|
||||
|
||||
def test_invalid_m2m_reverse_field(self):
|
||||
def test_invalid_field(self):
|
||||
"""
|
||||
Tests that providing invalid reverse field name to ManyToManyField.through_fields
|
||||
raises FieldDoesNotExist.
|
||||
Tests that providing invalid field names to ManyToManyField.through_fields
|
||||
triggers validation errors.
|
||||
"""
|
||||
class Fan(models.Model):
|
||||
pass
|
||||
|
||||
class Event(models.Model):
|
||||
invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('event', 'invalid_field'))
|
||||
invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invalid_field_1', 'invalid_field_2'))
|
||||
|
||||
class Invitation(models.Model):
|
||||
event = models.ForeignKey(Event)
|
||||
invitee = models.ForeignKey(Fan)
|
||||
inviter = models.ForeignKey(Fan, related_name='+')
|
||||
|
||||
with self.assertRaisesMessage(FieldDoesNotExist, 'invalid_field'):
|
||||
Event().invitees.all()
|
||||
field = Event._meta.get_field('invitees')
|
||||
errors = field.check(from_model=Event)
|
||||
expected = [
|
||||
Error(
|
||||
("The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_1'."),
|
||||
hint="Did you mean one of the following foreign keys to 'Event': event?",
|
||||
obj=field,
|
||||
id='fields.E338'),
|
||||
Error(
|
||||
("The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_2'."),
|
||||
hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?",
|
||||
obj=field,
|
||||
id='fields.E338'),
|
||||
]
|
||||
self.assertEqual(expected, errors)
|
||||
|
||||
def test_explicit_field_names(self):
|
||||
"""
|
||||
|
@ -1129,11 +1181,11 @@ class M2mThroughFieldsTests(IsolatedModelsTestCase):
|
|||
errors = field.check(from_model=Event)
|
||||
expected = [
|
||||
Error(
|
||||
("The field is given an iterable for through_fields, "
|
||||
"which does not provide the names for both link fields "
|
||||
"that Django should use for the relation through model "
|
||||
"'invalid_models_tests.Invitation'."),
|
||||
hint=None,
|
||||
("Field specifies 'through_fields' but does not provide the names "
|
||||
"of the two link fields that should be used for the relation "
|
||||
"through model 'invalid_models_tests.Invitation'."),
|
||||
hint=("Make sure you specify 'through_fields' as "
|
||||
"through_fields=('field1', 'field2')"),
|
||||
obj=field,
|
||||
id='fields.E337')]
|
||||
self.assertEqual(expected, errors)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue