Skip to content

Commit e6db084

Browse files
committed
Fixed #11226 -- Corrected an validation edge case with m2m relations between two models with the same class name. Thanks to pkoch for the report, and to Ramiro Morales for the patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12489 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent eb67e44 commit e6db084

File tree

4 files changed

+29
-19
lines changed

4 files changed

+29
-19
lines changed

django/core/management/validation.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -179,19 +179,20 @@ def get_validation_errors(outfile, app=None):
179179
)
180180
else:
181181
seen_intermediary_signatures.append(signature)
182-
seen_related_fk, seen_this_fk = False, False
183-
for field in f.rel.through._meta.fields:
184-
if field.rel:
185-
if not seen_related_fk and field.rel.to == f.rel.to:
186-
seen_related_fk = True
187-
elif field.rel.to == cls:
188-
seen_this_fk = True
189-
if not seen_related_fk or not seen_this_fk:
190-
e.add(opts, "'%s' has a manually-defined m2m relation "
191-
"through model %s, which does not have foreign keys "
192-
"to %s and %s" % (f.name, f.rel.through._meta.object_name,
193-
f.rel.to._meta.object_name, cls._meta.object_name)
194-
)
182+
if not f.rel.through._meta.auto_created:
183+
seen_related_fk, seen_this_fk = False, False
184+
for field in f.rel.through._meta.fields:
185+
if field.rel:
186+
if not seen_related_fk and field.rel.to == f.rel.to:
187+
seen_related_fk = True
188+
elif field.rel.to == cls:
189+
seen_this_fk = True
190+
if not seen_related_fk or not seen_this_fk:
191+
e.add(opts, "'%s' is a manually-defined m2m relation "
192+
"through model %s, which does not have foreign keys "
193+
"to %s and %s" % (f.name, f.rel.through._meta.object_name,
194+
f.rel.to._meta.object_name, cls._meta.object_name)
195+
)
195196
elif isinstance(f.rel.through, basestring):
196197
e.add(opts, "'%s' specifies an m2m relation through model %s, "
197198
"which has not been installed" % (f.name, f.rel.through)

django/db/models/fields/related.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ def get_or_create(self, **kwargs):
511511
def _add_items(self, source_field_name, target_field_name, *objs):
512512
# join_table: name of the m2m link table
513513
# source_field_name: the PK fieldname in join_table for the source object
514-
# target_col_name: the PK fieldname in join_table for the target object
514+
# target_field_name: the PK fieldname in join_table for the target object
515515
# *objs - objects to add. Either object instances, or primary keys of object instances.
516516

517517
# If there aren't any objects, there is nothing to do.
@@ -914,7 +914,7 @@ def set_managed(field, model, cls):
914914
to_model = field.rel.to
915915
managed = klass._meta.managed or to_model._meta.managed
916916
name = '%s_%s' % (klass._meta.object_name, field.name)
917-
if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or field.rel.to == klass._meta.object_name:
917+
if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name:
918918
from_ = 'from_%s' % to.lower()
919919
to = 'to_%s' % to.lower()
920920
else:
@@ -973,7 +973,7 @@ def _get_m2m_db_table(self, opts):
973973
connection.ops.max_name_length())
974974

975975
def _get_m2m_attr(self, related, attr):
976-
"Function that can be curried to provide the source column name for the m2m table"
976+
"Function that can be curried to provide the source accessor or DB column name for the m2m table"
977977
cache_attr = '_m2m_%s_cache' % attr
978978
if hasattr(self, cache_attr):
979979
return getattr(self, cache_attr)
@@ -983,7 +983,7 @@ def _get_m2m_attr(self, related, attr):
983983
return getattr(self, cache_attr)
984984

985985
def _get_m2m_reverse_attr(self, related, attr):
986-
"Function that can be curried to provide the related column name for the m2m table"
986+
"Function that can be curried to provide the related accessor or DB column name for the m2m table"
987987
cache_attr = '_m2m_reverse_%s_cache' % attr
988988
if hasattr(self, cache_attr):
989989
return getattr(self, cache_attr)

tests/modeltests/invalid_models/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,8 @@ class UniqueM2M(models.Model):
268268
invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_4' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_4'.
269269
invalid_models.missingrelations: 'rel1' has a relation with model Rel1, which has either not been installed or is abstract.
270270
invalid_models.missingrelations: 'rel2' has an m2m relation with model Rel2, which has either not been installed or is abstract.
271-
invalid_models.grouptwo: 'primary' has a manually-defined m2m relation through model Membership, which does not have foreign keys to Person and GroupTwo
272-
invalid_models.grouptwo: 'secondary' has a manually-defined m2m relation through model MembershipMissingFK, which does not have foreign keys to Group and GroupTwo
271+
invalid_models.grouptwo: 'primary' is a manually-defined m2m relation through model Membership, which does not have foreign keys to Person and GroupTwo
272+
invalid_models.grouptwo: 'secondary' is a manually-defined m2m relation through model MembershipMissingFK, which does not have foreign keys to Group and GroupTwo
273273
invalid_models.missingmanualm2mmodel: 'missing_m2m' specifies an m2m relation through model MissingM2MModel, which has not been installed
274274
invalid_models.group: The model Group has two manually-defined m2m relations through the model Membership, which is not permitted. Please consider using an extra field on your intermediary model instead.
275275
invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person, which is ambiguous and is not permitted.

tests/regressiontests/m2m_regress/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.db import models
2+
from django.contrib.auth import models as auth
23

34
# No related name is needed here, since symmetrical relations are not
45
# explicitly reversible.
@@ -41,6 +42,14 @@ class Worksheet(models.Model):
4142
id = models.CharField(primary_key=True, max_length=100)
4243
lines = models.ManyToManyField(Line, blank=True, null=True)
4344

45+
# Regression for #11226 -- A model with the same name that another one to
46+
# which it has a m2m relation. This shouldn't cause a name clash between
47+
# the automatically created m2m intermediary table FK field names when
48+
# running syncdb
49+
class User(models.Model):
50+
name = models.CharField(max_length=30)
51+
friends = models.ManyToManyField(auth.User)
52+
4453
__test__ = {"regressions": """
4554
# Multiple m2m references to the same model or a different model must be
4655
# distinguished when accessing the relations through an instance attribute.

0 commit comments

Comments
 (0)