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
This commit is contained in:
parent
eb67e449dd
commit
e6db084ac8
|
@ -179,19 +179,20 @@ def get_validation_errors(outfile, app=None):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
seen_intermediary_signatures.append(signature)
|
seen_intermediary_signatures.append(signature)
|
||||||
seen_related_fk, seen_this_fk = False, False
|
if not f.rel.through._meta.auto_created:
|
||||||
for field in f.rel.through._meta.fields:
|
seen_related_fk, seen_this_fk = False, False
|
||||||
if field.rel:
|
for field in f.rel.through._meta.fields:
|
||||||
if not seen_related_fk and field.rel.to == f.rel.to:
|
if field.rel:
|
||||||
seen_related_fk = True
|
if not seen_related_fk and field.rel.to == f.rel.to:
|
||||||
elif field.rel.to == cls:
|
seen_related_fk = True
|
||||||
seen_this_fk = True
|
elif field.rel.to == cls:
|
||||||
if not seen_related_fk or not seen_this_fk:
|
seen_this_fk = True
|
||||||
e.add(opts, "'%s' has a manually-defined m2m relation "
|
if not seen_related_fk or not seen_this_fk:
|
||||||
"through model %s, which does not have foreign keys "
|
e.add(opts, "'%s' is a manually-defined m2m relation "
|
||||||
"to %s and %s" % (f.name, f.rel.through._meta.object_name,
|
"through model %s, which does not have foreign keys "
|
||||||
f.rel.to._meta.object_name, cls._meta.object_name)
|
"to %s and %s" % (f.name, f.rel.through._meta.object_name,
|
||||||
)
|
f.rel.to._meta.object_name, cls._meta.object_name)
|
||||||
|
)
|
||||||
elif isinstance(f.rel.through, basestring):
|
elif isinstance(f.rel.through, basestring):
|
||||||
e.add(opts, "'%s' specifies an m2m relation through model %s, "
|
e.add(opts, "'%s' specifies an m2m relation through model %s, "
|
||||||
"which has not been installed" % (f.name, f.rel.through)
|
"which has not been installed" % (f.name, f.rel.through)
|
||||||
|
|
|
@ -511,7 +511,7 @@ def create_many_related_manager(superclass, rel=False):
|
||||||
def _add_items(self, source_field_name, target_field_name, *objs):
|
def _add_items(self, source_field_name, target_field_name, *objs):
|
||||||
# join_table: name of the m2m link table
|
# join_table: name of the m2m link table
|
||||||
# source_field_name: the PK fieldname in join_table for the source object
|
# source_field_name: the PK fieldname in join_table for the source object
|
||||||
# target_col_name: the PK fieldname in join_table for the target object
|
# target_field_name: the PK fieldname in join_table for the target object
|
||||||
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
||||||
|
|
||||||
# If there aren't any objects, there is nothing to do.
|
# If there aren't any objects, there is nothing to do.
|
||||||
|
@ -914,7 +914,7 @@ def create_many_to_many_intermediary_model(field, klass):
|
||||||
to_model = field.rel.to
|
to_model = field.rel.to
|
||||||
managed = klass._meta.managed or to_model._meta.managed
|
managed = klass._meta.managed or to_model._meta.managed
|
||||||
name = '%s_%s' % (klass._meta.object_name, field.name)
|
name = '%s_%s' % (klass._meta.object_name, field.name)
|
||||||
if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or field.rel.to == klass._meta.object_name:
|
if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name:
|
||||||
from_ = 'from_%s' % to.lower()
|
from_ = 'from_%s' % to.lower()
|
||||||
to = 'to_%s' % to.lower()
|
to = 'to_%s' % to.lower()
|
||||||
else:
|
else:
|
||||||
|
@ -973,7 +973,7 @@ class ManyToManyField(RelatedField, Field):
|
||||||
connection.ops.max_name_length())
|
connection.ops.max_name_length())
|
||||||
|
|
||||||
def _get_m2m_attr(self, related, attr):
|
def _get_m2m_attr(self, related, attr):
|
||||||
"Function that can be curried to provide the source column name for the m2m table"
|
"Function that can be curried to provide the source accessor or DB column name for the m2m table"
|
||||||
cache_attr = '_m2m_%s_cache' % attr
|
cache_attr = '_m2m_%s_cache' % attr
|
||||||
if hasattr(self, cache_attr):
|
if hasattr(self, cache_attr):
|
||||||
return getattr(self, cache_attr)
|
return getattr(self, cache_attr)
|
||||||
|
@ -983,7 +983,7 @@ class ManyToManyField(RelatedField, Field):
|
||||||
return getattr(self, cache_attr)
|
return getattr(self, cache_attr)
|
||||||
|
|
||||||
def _get_m2m_reverse_attr(self, related, attr):
|
def _get_m2m_reverse_attr(self, related, attr):
|
||||||
"Function that can be curried to provide the related column name for the m2m table"
|
"Function that can be curried to provide the related accessor or DB column name for the m2m table"
|
||||||
cache_attr = '_m2m_reverse_%s_cache' % attr
|
cache_attr = '_m2m_reverse_%s_cache' % attr
|
||||||
if hasattr(self, cache_attr):
|
if hasattr(self, cache_attr):
|
||||||
return getattr(self, cache_attr)
|
return getattr(self, cache_attr)
|
||||||
|
|
|
@ -268,8 +268,8 @@ invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_3' clashes wi
|
||||||
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'.
|
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'.
|
||||||
invalid_models.missingrelations: 'rel1' has a relation with model Rel1, which has either not been installed or is abstract.
|
invalid_models.missingrelations: 'rel1' has a relation with model Rel1, which has either not been installed or is abstract.
|
||||||
invalid_models.missingrelations: 'rel2' has an m2m relation with model Rel2, which has either not been installed or is abstract.
|
invalid_models.missingrelations: 'rel2' has an m2m relation with model Rel2, which has either not been installed or is abstract.
|
||||||
invalid_models.grouptwo: 'primary' has a manually-defined m2m relation through model Membership, which does not have foreign keys to Person and GroupTwo
|
invalid_models.grouptwo: 'primary' is a manually-defined m2m relation through model Membership, which does not have foreign keys to Person and GroupTwo
|
||||||
invalid_models.grouptwo: 'secondary' has a manually-defined m2m relation through model MembershipMissingFK, which does not have foreign keys to Group and GroupTwo
|
invalid_models.grouptwo: 'secondary' is a manually-defined m2m relation through model MembershipMissingFK, which does not have foreign keys to Group and GroupTwo
|
||||||
invalid_models.missingmanualm2mmodel: 'missing_m2m' specifies an m2m relation through model MissingM2MModel, which has not been installed
|
invalid_models.missingmanualm2mmodel: 'missing_m2m' specifies an m2m relation through model MissingM2MModel, which has not been installed
|
||||||
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.
|
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.
|
||||||
invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person, which is ambiguous and is not permitted.
|
invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person, which is ambiguous and is not permitted.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.auth import models as auth
|
||||||
|
|
||||||
# No related name is needed here, since symmetrical relations are not
|
# No related name is needed here, since symmetrical relations are not
|
||||||
# explicitly reversible.
|
# explicitly reversible.
|
||||||
|
@ -41,6 +42,14 @@ class Worksheet(models.Model):
|
||||||
id = models.CharField(primary_key=True, max_length=100)
|
id = models.CharField(primary_key=True, max_length=100)
|
||||||
lines = models.ManyToManyField(Line, blank=True, null=True)
|
lines = models.ManyToManyField(Line, blank=True, null=True)
|
||||||
|
|
||||||
|
# Regression for #11226 -- A model with the same name that another one to
|
||||||
|
# which it has a m2m relation. This shouldn't cause a name clash between
|
||||||
|
# the automatically created m2m intermediary table FK field names when
|
||||||
|
# running syncdb
|
||||||
|
class User(models.Model):
|
||||||
|
name = models.CharField(max_length=30)
|
||||||
|
friends = models.ManyToManyField(auth.User)
|
||||||
|
|
||||||
__test__ = {"regressions": """
|
__test__ = {"regressions": """
|
||||||
# Multiple m2m references to the same model or a different model must be
|
# Multiple m2m references to the same model or a different model must be
|
||||||
# distinguished when accessing the relations through an instance attribute.
|
# distinguished when accessing the relations through an instance attribute.
|
||||||
|
|
Loading…
Reference in New Issue