diff --git a/AUTHORS b/AUTHORS index 06e80e113a..94dc8891a4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -376,6 +376,7 @@ answer newbie questions, and generally made Django that much better: Wang Chun Filip Wasilewski Dan Watson + Joel Watts Chris Wesseling James Wheare charly.wilhelm@gmail.com diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 41d625186c..9678ba4154 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -103,13 +103,15 @@ class RelatedField(object): if hasattr(sup, 'contribute_to_class'): sup.contribute_to_class(cls, name) + + if not cls._meta.abstract and self.rel.related_name: + self.rel.related_name = self.rel.related_name % {'class': cls.__name__.lower()} + other = self.rel.to if isinstance(other, basestring): 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) @@ -119,7 +121,8 @@ class RelatedField(object): def do_related_class(self, other, cls): self.set_attributes_from_rel() related = RelatedObject(other, cls, self) - self.contribute_to_related_class(other, related) + if not cls._meta.abstract: + self.contribute_to_related_class(other, related) def get_db_prep_lookup(self, lookup_type, value): # If we are doing a lookup on a Related Field, we must be diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py index 7c737b6bd1..9842cb166b 100644 --- a/tests/modeltests/model_inheritance/models.py +++ b/tests/modeltests/model_inheritance/models.py @@ -38,6 +38,29 @@ class Student(CommonInfo): class Meta: pass +# +# Abstract base classes with related models +# + +class Post(models.Model): + title = models.CharField(max_length=50) + +class Attachment(models.Model): + post = models.ForeignKey(Post, related_name='attached_%(class)s_set') + content = models.TextField() + + class Meta: + abstract = True + + def __unicode__(self): + return self.content + +class Comment(Attachment): + is_spam = models.BooleanField() + +class Link(Attachment): + url = models.URLField() + # # Multi-table inheritance # @@ -128,9 +151,25 @@ Traceback (most recent call last): ... AttributeError: type object 'CommonInfo' has no attribute 'objects' -# The Place/Restaurant/ItalianRestaurant models, on the other hand, all exist -# as independent models. However, the subclasses also have transparent access -# to the fields of their ancestors. +# Create a Post +>>> post = Post(title='Lorem Ipsum') +>>> post.save() + +# The Post model has distinct accessors for the Comment and Link models. +>>> post.attached_comment_set.create(content='Save $ on V1agr@', is_spam=True) + +>>> post.attached_link_set.create(content='The Web framework for perfectionists with deadlines.', url='http://www.djangoproject.com/') + + +# The Post model doesn't have an attribute called 'attached_%(class)s_set'. +>>> getattr(post, 'attached_%(class)s_set') +Traceback (most recent call last): + ... +AttributeError: 'Post' object has no attribute 'attached_%(class)s_set' + +# The Place/Restaurant/ItalianRestaurant models all exist as independent +# models. However, the subclasses also have transparent access to the fields of +# their ancestors. # Create a couple of Places. >>> p1 = Place(name='Master Shakes', address='666 W. Jersey')