summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Willison <simonw@eventbrite.com>2018-07-18 15:21:40 -0700
committerTim Graham <timograham@gmail.com>2018-08-22 12:07:29 -0400
commit586a9dc4295357de1f5ad0590ad34bf2bc008f79 (patch)
treec416257a9176de603d79b12f1f99e28c72725735
parentf2d5dafec93e6b3100f004c559ebe21e2b783ae7 (diff)
Fixed #26352 -- Made system check allow ManyToManyField to target the same model if through_fields differs.
-rw-r--r--django/db/models/base.py7
-rw-r--r--docs/ref/checks.txt4
-rw-r--r--tests/invalid_models_tests/test_models.py24
3 files changed, 29 insertions, 6 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 9cc1af0171..11a2d2a264 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -1301,12 +1301,13 @@ class Model(metaclass=ModelBase):
fields = (f for f in fields if isinstance(f.remote_field.through, ModelBase))
for f in fields:
- signature = (f.remote_field.model, cls, f.remote_field.through)
+ signature = (f.remote_field.model, cls, f.remote_field.through, f.remote_field.through_fields)
if signature in seen_intermediary_signatures:
errors.append(
checks.Error(
- "The model has two many-to-many relations through "
- "the intermediate model '%s'." % f.remote_field.through._meta.label,
+ "The model has two identical many-to-many relations "
+ "through the intermediate model '%s'." %
+ f.remote_field.through._meta.label,
obj=cls,
id='models.E003',
)
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index a84d9b60b6..42296f65c9 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -252,8 +252,8 @@ Models
* **models.E001**: ``<swappable>`` is not of the form ``app_label.app_name``.
* **models.E002**: ``<SETTING>`` references ``<model>``, which has not been
installed, or is abstract.
-* **models.E003**: The model has two many-to-many relations through the
- intermediate model ``<app_label>.<model>``.
+* **models.E003**: The model has two identical many-to-many relations through
+ the intermediate model ``<app_label>.<model>``.
* **models.E004**: ``id`` can only be used as a field name if the field also
sets ``primary_key=True``.
* **models.E005**: The field ``<field name>`` from parent model ``<model>``
diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py
index 9dd2fd1f06..709d73d6c8 100644
--- a/tests/invalid_models_tests/test_models.py
+++ b/tests/invalid_models_tests/test_models.py
@@ -771,13 +771,35 @@ class OtherModelTests(SimpleTestCase):
self.assertEqual(Group.check(), [
Error(
- "The model has two many-to-many relations through "
+ "The model has two identical many-to-many relations through "
"the intermediate model 'invalid_models_tests.Membership'.",
obj=Group,
id='models.E003',
)
])
+ def test_two_m2m_through_same_model_with_different_through_fields(self):
+ class Country(models.Model):
+ pass
+
+ class ShippingMethod(models.Model):
+ to_countries = models.ManyToManyField(
+ Country, through='ShippingMethodPrice',
+ through_fields=('method', 'to_country'),
+ )
+ from_countries = models.ManyToManyField(
+ Country, through='ShippingMethodPrice',
+ through_fields=('method', 'from_country'),
+ related_name='+',
+ )
+
+ class ShippingMethodPrice(models.Model):
+ method = models.ForeignKey(ShippingMethod, models.CASCADE)
+ to_country = models.ForeignKey(Country, models.CASCADE)
+ from_country = models.ForeignKey(Country, models.CASCADE)
+
+ self.assertEqual(ShippingMethod.check(), [])
+
def test_missing_parent_link(self):
msg = 'Add parent_link=True to invalid_models_tests.ParkingLot.parent.'
with self.assertRaisesMessage(ImproperlyConfigured, msg):