mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Add related_query_name to ForeignKey/M2M. Refs #20244
This commit is contained in:
		| @@ -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): | ||||
| @@ -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 | ||||
| @@ -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 | ||||
| @@ -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): | ||||
| @@ -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 | ||||
| @@ -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), | ||||
| @@ -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), | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| 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): | ||||
| @@ -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. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user