Loading django/db/models/fields/related.py +12 −7 Original line number Diff line number Diff line Loading @@ -139,7 +139,7 @@ class RelatedField(Field): # 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 self.opts.model_name return self.rel.related_query_name or self.rel.related_name or self.opts.model_name class RenameRelatedObjectDescriptorMethods(RenameMethodsBase): Loading Loading @@ -826,7 +826,7 @@ class ReverseManyRelatedObjectsDescriptor(object): class ForeignObjectRel(object): def __init__(self, field, to, related_name=None, limit_choices_to=None, parent_link=False, on_delete=None): parent_link=False, on_delete=None, related_query_name=None): try: to._meta except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT Loading @@ -835,6 +835,7 @@ class ForeignObjectRel(object): self.field = field self.to = to self.related_name = related_name self.related_query_name = related_query_name self.limit_choices_to = {} if limit_choices_to is None else limit_choices_to self.multiple = True self.parent_link = parent_link Loading Loading @@ -862,10 +863,10 @@ class ForeignObjectRel(object): class ManyToOneRel(ForeignObjectRel): def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None, parent_link=False, on_delete=None): parent_link=False, on_delete=None, related_query_name=None): super(ManyToOneRel, self).__init__( field, to, related_name=related_name, limit_choices_to=limit_choices_to, parent_link=parent_link, on_delete=on_delete) parent_link=parent_link, on_delete=on_delete, related_query_name=related_query_name) self.field_name = field_name def get_related_field(self): Loading @@ -885,21 +886,22 @@ class ManyToOneRel(ForeignObjectRel): class OneToOneRel(ManyToOneRel): def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None, parent_link=False, on_delete=None): parent_link=False, on_delete=None, related_query_name=None): super(OneToOneRel, self).__init__(field, to, field_name, related_name=related_name, limit_choices_to=limit_choices_to, parent_link=parent_link, on_delete=on_delete parent_link=parent_link, on_delete=on_delete, related_query_name=related_query_name, ) self.multiple = False class ManyToManyRel(object): def __init__(self, to, related_name=None, limit_choices_to=None, symmetrical=True, through=None, db_constraint=True): symmetrical=True, through=None, db_constraint=True, related_query_name=None): if through and not db_constraint: raise ValueError("Can't supply a through model and db_constraint=False") self.to = to self.related_name = related_name self.related_query_name = related_query_name if limit_choices_to is None: limit_choices_to = {} self.limit_choices_to = limit_choices_to Loading Loading @@ -933,6 +935,7 @@ class ForeignObject(RelatedField): kwargs['rel'] = ForeignObjectRel( self, to, related_name=kwargs.pop('related_name', None), related_query_name=kwargs.pop('related_query_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), parent_link=kwargs.pop('parent_link', False), on_delete=kwargs.pop('on_delete', CASCADE), Loading Loading @@ -1141,6 +1144,7 @@ class ForeignKey(ForeignObject): kwargs['rel'] = rel_class( self, to, to_field, related_name=kwargs.pop('related_name', None), related_query_name=kwargs.pop('related_query_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), parent_link=kwargs.pop('parent_link', False), on_delete=kwargs.pop('on_delete', CASCADE), Loading Loading @@ -1340,6 +1344,7 @@ class ManyToManyField(RelatedField): kwargs['verbose_name'] = kwargs.get('verbose_name', None) kwargs['rel'] = ManyToManyRel(to, related_name=kwargs.pop('related_name', None), related_query_name=kwargs.pop('related_query_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), symmetrical=kwargs.pop('symmetrical', to == RECURSIVE_RELATIONSHIP_CONSTANT), through=kwargs.pop('through', None), Loading tests/foreign_object/models.py +8 −0 Original line number Diff line number Diff line Loading @@ -150,3 +150,11 @@ class ArticleTranslation(models.Model): class Meta: unique_together = ('article', 'lang') ordering = ('active_translation__title',) class ArticleTag(models.Model): article = models.ForeignKey(Article, related_name="tags", related_query_name="tag") name = models.CharField(max_length=255) class ArticleIdea(models.Model): articles = models.ManyToManyField(Article, related_name="ideas", related_query_name="idea_things") name = models.CharField(max_length=255) tests/foreign_object/tests.py +20 −1 Original line number Diff line number Diff line import datetime from operator import attrgetter from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation, ArticleTag, ArticleIdea from django.test import TestCase from django.utils.translation import activate from django.core.exceptions import FieldError from django import forms class MultiColumnFKTests(TestCase): Loading Loading @@ -321,6 +322,24 @@ class MultiColumnFKTests(TestCase): with self.assertRaisesMessage(Article.DoesNotExist, 'ArticleTranslation has no article'): referrer.article def test_foreign_key_related_query_name(self): a1 = Article.objects.create(pub_date=datetime.date.today()) ArticleTag.objects.create(article=a1, name="foo") self.assertEqual(Article.objects.filter(tag__name="foo").count(), 1) self.assertEqual(Article.objects.filter(tag__name="bar").count(), 0) with self.assertRaises(FieldError): Article.objects.filter(tags__name="foo") def test_many_to_many_related_query_name(self): a1 = Article.objects.create(pub_date=datetime.date.today()) i1 = ArticleIdea.objects.create(name="idea1") a1.ideas.add(i1) self.assertEqual(Article.objects.filter(idea_things__name="idea1").count(), 1) self.assertEqual(Article.objects.filter(idea_things__name="idea2").count(), 0) with self.assertRaises(FieldError): Article.objects.filter(ideas__name="idea1") class FormsTests(TestCase): # ForeignObjects should not have any form fields, currently the user needs # to manually deal with the foreignobject relation. Loading Loading
django/db/models/fields/related.py +12 −7 Original line number Diff line number Diff line Loading @@ -139,7 +139,7 @@ class RelatedField(Field): # 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 self.opts.model_name return self.rel.related_query_name or self.rel.related_name or self.opts.model_name class RenameRelatedObjectDescriptorMethods(RenameMethodsBase): Loading Loading @@ -826,7 +826,7 @@ class ReverseManyRelatedObjectsDescriptor(object): class ForeignObjectRel(object): def __init__(self, field, to, related_name=None, limit_choices_to=None, parent_link=False, on_delete=None): parent_link=False, on_delete=None, related_query_name=None): try: to._meta except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT Loading @@ -835,6 +835,7 @@ class ForeignObjectRel(object): self.field = field self.to = to self.related_name = related_name self.related_query_name = related_query_name self.limit_choices_to = {} if limit_choices_to is None else limit_choices_to self.multiple = True self.parent_link = parent_link Loading Loading @@ -862,10 +863,10 @@ class ForeignObjectRel(object): class ManyToOneRel(ForeignObjectRel): def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None, parent_link=False, on_delete=None): parent_link=False, on_delete=None, related_query_name=None): super(ManyToOneRel, self).__init__( field, to, related_name=related_name, limit_choices_to=limit_choices_to, parent_link=parent_link, on_delete=on_delete) parent_link=parent_link, on_delete=on_delete, related_query_name=related_query_name) self.field_name = field_name def get_related_field(self): Loading @@ -885,21 +886,22 @@ class ManyToOneRel(ForeignObjectRel): class OneToOneRel(ManyToOneRel): def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None, parent_link=False, on_delete=None): parent_link=False, on_delete=None, related_query_name=None): super(OneToOneRel, self).__init__(field, to, field_name, related_name=related_name, limit_choices_to=limit_choices_to, parent_link=parent_link, on_delete=on_delete parent_link=parent_link, on_delete=on_delete, related_query_name=related_query_name, ) self.multiple = False class ManyToManyRel(object): def __init__(self, to, related_name=None, limit_choices_to=None, symmetrical=True, through=None, db_constraint=True): symmetrical=True, through=None, db_constraint=True, related_query_name=None): if through and not db_constraint: raise ValueError("Can't supply a through model and db_constraint=False") self.to = to self.related_name = related_name self.related_query_name = related_query_name if limit_choices_to is None: limit_choices_to = {} self.limit_choices_to = limit_choices_to Loading Loading @@ -933,6 +935,7 @@ class ForeignObject(RelatedField): kwargs['rel'] = ForeignObjectRel( self, to, related_name=kwargs.pop('related_name', None), related_query_name=kwargs.pop('related_query_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), parent_link=kwargs.pop('parent_link', False), on_delete=kwargs.pop('on_delete', CASCADE), Loading Loading @@ -1141,6 +1144,7 @@ class ForeignKey(ForeignObject): kwargs['rel'] = rel_class( self, to, to_field, related_name=kwargs.pop('related_name', None), related_query_name=kwargs.pop('related_query_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), parent_link=kwargs.pop('parent_link', False), on_delete=kwargs.pop('on_delete', CASCADE), Loading Loading @@ -1340,6 +1344,7 @@ class ManyToManyField(RelatedField): kwargs['verbose_name'] = kwargs.get('verbose_name', None) kwargs['rel'] = ManyToManyRel(to, related_name=kwargs.pop('related_name', None), related_query_name=kwargs.pop('related_query_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), symmetrical=kwargs.pop('symmetrical', to == RECURSIVE_RELATIONSHIP_CONSTANT), through=kwargs.pop('through', None), Loading
tests/foreign_object/models.py +8 −0 Original line number Diff line number Diff line Loading @@ -150,3 +150,11 @@ class ArticleTranslation(models.Model): class Meta: unique_together = ('article', 'lang') ordering = ('active_translation__title',) class ArticleTag(models.Model): article = models.ForeignKey(Article, related_name="tags", related_query_name="tag") name = models.CharField(max_length=255) class ArticleIdea(models.Model): articles = models.ManyToManyField(Article, related_name="ideas", related_query_name="idea_things") name = models.CharField(max_length=255)
tests/foreign_object/tests.py +20 −1 Original line number Diff line number Diff line import datetime from operator import attrgetter from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation, ArticleTag, ArticleIdea from django.test import TestCase from django.utils.translation import activate from django.core.exceptions import FieldError from django import forms class MultiColumnFKTests(TestCase): Loading Loading @@ -321,6 +322,24 @@ class MultiColumnFKTests(TestCase): with self.assertRaisesMessage(Article.DoesNotExist, 'ArticleTranslation has no article'): referrer.article def test_foreign_key_related_query_name(self): a1 = Article.objects.create(pub_date=datetime.date.today()) ArticleTag.objects.create(article=a1, name="foo") self.assertEqual(Article.objects.filter(tag__name="foo").count(), 1) self.assertEqual(Article.objects.filter(tag__name="bar").count(), 0) with self.assertRaises(FieldError): Article.objects.filter(tags__name="foo") def test_many_to_many_related_query_name(self): a1 = Article.objects.create(pub_date=datetime.date.today()) i1 = ArticleIdea.objects.create(name="idea1") a1.ideas.add(i1) self.assertEqual(Article.objects.filter(idea_things__name="idea1").count(), 1) self.assertEqual(Article.objects.filter(idea_things__name="idea2").count(), 0) with self.assertRaises(FieldError): Article.objects.filter(ideas__name="idea1") class FormsTests(TestCase): # ForeignObjects should not have any form fields, currently the user needs # to manually deal with the foreignobject relation. Loading