Author: russellm
Date: 2006-09-07 08:29:56 -0500 (Thu, 07 Sep 2006)
New Revision: 3734
Modified:
django/trunk/django/core/management.py
django/trunk/django/db/models/related.py
django/trunk/tests/modeltests/invalid_models/models.py
Log:
Fixes #2653 -- Modified related field utility methods to return None as the
related name for symmetrical m2m fields on self. Updated validators and unit
tests to account for the new behavior.
Modified: django/trunk/django/core/management.py
===================================================================
--- django/trunk/django/core/management.py 2006-09-07 04:21:11 UTC (rev
3733)
+++ django/trunk/django/core/management.py 2006-09-07 13:29:56 UTC (rev
3734)
@@ -903,27 +903,32 @@
rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
rel_query_name = f.related_query_name()
- for r in rel_opts.fields:
- if r.name == rel_name:
- e.add(opts, "Accessor for m2m field '%s' clashes with
field '%s.%s'. Add a related_name argument to the definition for '%s'." %
(f.name, rel_opts.object_name, r.name, f.name))
- if r.name == rel_query_name:
- e.add(opts, "Reverse query name for m2m field '%s' clashes
with field '%s.%s'. Add a related_name argument to the definition for '%s'." %
(f.name, rel_opts.object_name, r.name, f.name))
- for r in rel_opts.many_to_many:
- if r.name == rel_name:
- e.add(opts, "Accessor for m2m field '%s' clashes with m2m
field '%s.%s'. Add a related_name argument to the definition for '%s'." %
(f.name, rel_opts.object_name, r.name, f.name))
- if r.name == rel_query_name:
- e.add(opts, "Reverse query name for m2m field '%s' clashes
with m2m field '%s.%s'. Add a related_name argument to the definition for
'%s'." % (f.name, rel_opts.object_name, r.name, f.name))
- for r in rel_opts.get_all_related_many_to_many_objects():
- if r.field is not f:
+ # If rel_name is none, there is no reverse accessor.
+ # (This only occurs for symmetrical m2m relations to self).
+ # If this is the case, there are no clashes to check for this
field, as
+ # there are no reverse descriptors for this field.
+ if rel_name is not None:
+ for r in rel_opts.fields:
+ if r.name == rel_name:
+ e.add(opts, "Accessor for m2m field '%s' clashes with
field '%s.%s'. Add a related_name argument to the definition for '%s'." %
(f.name, rel_opts.object_name, r.name, f.name))
+ if r.name == rel_query_name:
+ e.add(opts, "Reverse query name for m2m field '%s'
clashes with field '%s.%s'. Add a related_name argument to the definition for
'%s'." % (f.name, rel_opts.object_name, r.name, f.name))
+ for r in rel_opts.many_to_many:
+ if r.name == rel_name:
+ e.add(opts, "Accessor for m2m field '%s' clashes with
m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." %
(f.name, rel_opts.object_name, r.name, f.name))
+ if r.name == rel_query_name:
+ e.add(opts, "Reverse query name for m2m field '%s'
clashes with m2m field '%s.%s'. Add a related_name argument to the definition
for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
+ for r in rel_opts.get_all_related_many_to_many_objects():
+ if r.field is not f:
+ if r.get_accessor_name() == rel_name:
+ e.add(opts, "Accessor for m2m field '%s' clashes
with related m2m field '%s.%s'. Add a related_name argument to the definition
for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
+ if r.get_accessor_name() == rel_query_name:
+ e.add(opts, "Reverse query name for m2m field '%s'
clashes with related m2m field '%s.%s'. Add a related_name argument to the
definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(),
f.name))
+ for r in rel_opts.get_all_related_objects():
if r.get_accessor_name() == rel_name:
- e.add(opts, "Accessor for m2m field '%s' clashes with
related m2m field '%s.%s'. Add a related_name argument to the definition for
'%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
+ e.add(opts, "Accessor for m2m field '%s' clashes with
related field '%s.%s'. Add a related_name argument to the definition for '%s'."
% (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
if r.get_accessor_name() == rel_query_name:
- e.add(opts, "Reverse query name for m2m field '%s'
clashes with related m2m field '%s.%s'. Add a related_name argument to the
definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(),
f.name))
- for r in rel_opts.get_all_related_objects():
- if r.get_accessor_name() == rel_name:
- e.add(opts, "Accessor for m2m field '%s' clashes with
related field '%s.%s'. Add a related_name argument to the definition for '%s'."
% (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
- if r.get_accessor_name() == rel_query_name:
- e.add(opts, "Reverse query name for m2m field '%s' clashes
with related field '%s.%s'. Add a related_name argument to the definition for
'%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
+ e.add(opts, "Reverse query name for m2m field '%s'
clashes with related field '%s.%s'. Add a related_name argument to the
definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(),
f.name))
# Check admin attribute.
if opts.admin is not None:
Modified: django/trunk/django/db/models/related.py
===================================================================
--- django/trunk/django/db/models/related.py 2006-09-07 04:21:11 UTC (rev
3733)
+++ django/trunk/django/db/models/related.py 2006-09-07 13:29:56 UTC (rev
3734)
@@ -131,6 +131,9 @@
# many-to-many objects. It uses the lower-cased object_name + "_set",
# but this can be overridden with the "related_name" option.
if self.field.rel.multiple:
+ # If this is a symmetrical m2m relation on self, there is no
reverse accessor.
+ if getattr(self.field.rel, 'symmetrical', False) and self.model ==
self.parent_model:
+ return None
return self.field.rel.related_name or
(self.opts.object_name.lower() + '_set')
else:
return self.field.rel.related_name or
(self.opts.object_name.lower())
Modified: django/trunk/tests/modeltests/invalid_models/models.py
===================================================================
--- django/trunk/tests/modeltests/invalid_models/models.py 2006-09-07
04:21:11 UTC (rev 3733)
+++ django/trunk/tests/modeltests/invalid_models/models.py 2006-09-07
13:29:56 UTC (rev 3734)
@@ -68,16 +68,35 @@
foreign_1 = models.ForeignKey("SelfClashForeign", related_name='id')
foreign_2 = models.ForeignKey("SelfClashForeign", related_name='src_safe')
+class ValidM2M(models.Model):
+ src_safe = models.CharField(maxlength=10)
+ validm2m = models.CharField(maxlength=10)
+
+ # M2M fields are symmetrical by default. Symmetrical M2M fields
+ # on self don't require a related accessor, so many potential
+ # clashes are avoided.
+ validm2m_set = models.ManyToManyField("ValidM2M")
+
+ m2m_1 = models.ManyToManyField("ValidM2M", related_name='id')
+ m2m_2 = models.ManyToManyField("ValidM2M", related_name='src_safe')
+
+ m2m_3 = models.ManyToManyField('self')
+ m2m_4 = models.ManyToManyField('self')
+
class SelfClashM2M(models.Model):
src_safe = models.CharField(maxlength=10)
selfclashm2m = models.CharField(maxlength=10)
- selfclashm2m_set = models.ManyToManyField("SelfClashM2M")
- m2m_1 = models.ManyToManyField("SelfClashM2M", related_name='id')
- m2m_2 = models.ManyToManyField("SelfClashM2M", related_name='src_safe')
+ # Non-symmetrical M2M fields _do_ have related accessors, so
+ # there is potential for clashes.
+ selfclashm2m_set = models.ManyToManyField("SelfClashM2M",
symmetrical=False)
+
+ m2m_1 = models.ManyToManyField("SelfClashM2M", related_name='id',
symmetrical=False)
+ m2m_2 = models.ManyToManyField("SelfClashM2M", related_name='src_safe',
symmetrical=False)
+ m2m_3 = models.ManyToManyField('self', symmetrical=False)
+ m2m_4 = models.ManyToManyField('self', symmetrical=False)
-
model_errors = """invalid_models.fielderrors: "charfield": CharFields require
a "maxlength" attribute.
invalid_models.fielderrors: "floatfield": FloatFields require a
"decimal_places" attribute.
invalid_models.fielderrors: "floatfield": FloatFields require a "max_digits"
attribute.
@@ -147,9 +166,17 @@
invalid_models.selfclashforeign: Reverse query name for field 'foreign_2'
clashes with field 'SelfClashForeign.src_safe'. Add a related_name argument to
the definition for 'foreign_2'.
invalid_models.selfclashm2m: Accessor for m2m field 'selfclashm2m_set' clashes
with m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to
the definition for 'selfclashm2m_set'.
invalid_models.selfclashm2m: Reverse query name for m2m field
'selfclashm2m_set' clashes with field 'SelfClashM2M.selfclashm2m'. Add a
related_name argument to the definition for 'selfclashm2m_set'.
+invalid_models.selfclashm2m: Accessor for m2m field 'selfclashm2m_set' clashes
with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name
argument to the definition for 'selfclashm2m_set'.
invalid_models.selfclashm2m: Accessor for m2m field 'm2m_1' clashes with field
'SelfClashM2M.id'. Add a related_name argument to the definition for 'm2m_1'.
invalid_models.selfclashm2m: Accessor for m2m field 'm2m_2' clashes with field
'SelfClashM2M.src_safe'. Add a related_name argument to the definition for
'm2m_2'.
invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_1' clashes
with field 'SelfClashM2M.id'. Add a related_name argument to the definition for
'm2m_1'.
invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_2' clashes
with field 'SelfClashM2M.src_safe'. Add a related_name argument to the
definition for 'm2m_2'.
+invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with m2m
field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the
definition for 'm2m_3'.
+invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with
related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument
to the definition for 'm2m_3'.
+invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with
related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument
to the definition for 'm2m_3'.
+invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with m2m
field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the
definition for 'm2m_4'.
+invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with
related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument
to the definition for 'm2m_4'.
+invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with
related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument
to the definition for 'm2m_4'.
+invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_3' clashes
with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the
definition for 'm2m_3'.
+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'.
"""
-
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/django-updates
-~----------~----~----~----~------~----~------~--~---