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:
Akis Kesoglou 2014-03-07 13:56:28 +02:00 committed by Ramiro Morales
parent f4d91638fc
commit aaad3e27ac
5 changed files with 154 additions and 49 deletions

View file

@ -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)