summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-04-16 08:09:46 +0000
committerMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-04-16 08:09:46 +0000
commit2b426635aaed0edf921a726dd95ef50a8dab9beb (patch)
tree96e640f483885820ca0fdfb30a2b4295b5021120
parentb114fecfe4ac817d780205a21424610ea600907b (diff)
queryset-refactor: Added a way to specify the related_name attribute on
abstract base classes. git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7432 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/db/models/fields/related.py11
-rw-r--r--docs/model-api.txt39
2 files changed, 47 insertions, 3 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 6629289b05..5a173b6fa5 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -1,3 +1,5 @@
+import copy
+
from django.db import connection, transaction
from django.db.models import signals, get_model
from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class
@@ -108,6 +110,8 @@ class RelatedField(object):
add_lazy_relation(cls, self, other)
else:
self.do_related_class(other, cls)
+ if not cls._meta.abstract and self.rel.related_name:
+ self.rel.related_name = self.rel.related_name % {'class': cls.__name__.lower()}
def set_attributes_from_rel(self):
self.name = self.name or (self.rel.to._meta.object_name.lower() + '_' + self.rel.to._meta.pk.name)
@@ -149,9 +153,10 @@ class RelatedField(object):
raise TypeError, "Related Field has invalid lookup: %s" % lookup_type
def _get_related_query_name(self, opts):
- # This method defines the name that can be used to identify this related object
- # in a table-spanning query. It uses the lower-cased object_name by default,
- # but this can be overridden with the "related_name" option.
+ # This method defines the name that can be used to identify this
+ # related object in a table-spanning query. It uses the lower-cased
+ # object_name by default, but this can be overridden with the
+ # "related_name" option.
return self.rel.related_name or opts.object_name.lower()
class SingleRelatedObjectDescriptor(object):
diff --git a/docs/model-api.txt b/docs/model-api.txt
index 5502bd51eb..dd9fe8bdf2 100644
--- a/docs/model-api.txt
+++ b/docs/model-api.txt
@@ -886,6 +886,10 @@ relationship should work. All are optional:
`related objects documentation`_ for a full
explanation and example.
+ If using this in an `abstract base class`_, be
+ sure to read the `extra notes`_ in that section
+ about ``related_name``.
+
``to_field`` The field on the related object that the relation
is to. By default, Django uses the primary key of
the related object.
@@ -893,6 +897,8 @@ relationship should work. All are optional:
.. _`Database API reference`: ../db-api/
.. _related objects documentation: ../db-api/#related-objects
+.. _abstract base class: `Abstract base classes`_
+.. _extra notes: `Be careful with related_name`_
Many-to-many relationships
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2146,6 +2152,39 @@ abstract base class. For example, including ``db_table`` would mean that all
the child classes (the ones that don't specify their own ``Meta``) would use
the same database table, which is almost certainly not what you want.
+Be careful with ``related_name``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are using the ``related_name`` attribute on a ``ForeignKey`` or
+``ManyToManyField``, you must always specify a *unique* reverse name for the
+field. This would normally cause a problem in abstract base classes, since the
+fields on this class are included into each of the child classes, with exactly
+the same values for the attributes (including ``related_name``) each time.
+
+To work around this problem, when you are using ``related_name`` in an
+abstract base class (only), part of the name should be the string
+``'%(class)s'``. This is replaced by the lower-cased name of the child class
+that the field is used in. Since each class has a different name, each related
+name will end up being different. For example::
+
+ class Base(models.Model):
+ m2m = models.ManyToMany(OtherModel, related_name="%(class)s_related")
+
+ class Meta:
+ abstract = True
+
+ class ChildA(Base):
+ pass
+
+ class ChildB(Base):
+ pass
+
+The reverse name of the ``ChildA.m2m`` field will be ``childa_related``,
+whilst the reverse name of the ``ChildB.m2m`` field will be
+``childb_related``. It is up to you how you use the ``'%(class)s'`` portion to
+construct your related name, but if you forget to use it, Django will raise
+errors when you validate your models (or run ``syncdb``).
+
Multi-table inheritance
-----------------------