Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Implements #18668 -- related_query_name is now a configurable field vari... #693

Closed
wants to merge 1 commit into from

2 participants

@zmsmith

...able

This patch allows full control over both related_name and related_query_name in the ORM.
With this change, a django user can control how to access a related manager and the query
field name separately.

adding tests and fixing generic

@zmsmith zmsmith Implements #18668 -- related_query_name is now a configurable field v…
…ariable

This patch allows full control over both related_name and related_query_name in the ORM.
With this change, a django user can control how to access a related manager and the query
field name separately.

adding tests and fixing generic
37dbc08
@timgraham
Owner

Part of what's included in this PR was committed in 99b467f and 12cb0df. It looks like this contains some things that weren't added (e.g. adding this on generic relations) but it needs to be updated to merge cleanly. Could you do that and open a new PR? Thanks!

@timgraham timgraham closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 1, 2013
  1. @zmsmith

    Implements #18668 -- related_query_name is now a configurable field v…

    zmsmith authored
    …ariable
    
    This patch allows full control over both related_name and related_query_name in the ORM.
    With this change, a django user can control how to access a related manager and the query
    field name separately.
    
    adding tests and fixing generic
This page is out of date. Refresh to see the latest.
View
4 django/contrib/contenttypes/generic.py
@@ -371,9 +371,11 @@ def create(self, **kwargs):
return GenericRelatedObjectManager
class GenericRel(ManyToManyRel):
- def __init__(self, to, related_name=None, limit_choices_to=None, symmetrical=True):
+ def __init__(self, to, related_name=None, related_query_name=None,
+ limit_choices_to=None, symmetrical=True):
self.to = to
self.related_name = related_name
+ self.related_query_name = related_query_name
self.limit_choices_to = limit_choices_to or {}
self.symmetrical = symmetrical
self.multiple = True
View
24 django/db/models/fields/related.py
@@ -222,7 +222,9 @@ def related_query_name(self):
# 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.object_name.lower()
+ return (self.rel.related_query_name or
+ self.rel.related_name or
+ self.opts.object_name.lower())
class SingleRelatedObjectDescriptor(object):
@@ -908,14 +910,15 @@ def __set__(self, instance, value):
class ManyToOneRel(object):
- def __init__(self, to, field_name, related_name=None, limit_choices_to=None,
- parent_link=False, on_delete=None):
+ def __init__(self, to, field_name, related_name=None, related_query_name=None,
+ limit_choices_to=None, parent_link=False, on_delete=None):
try:
to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert isinstance(to, six.string_types), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
self.to, self.field_name = to, field_name
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
@@ -940,20 +943,21 @@ def get_related_field(self):
class OneToOneRel(ManyToOneRel):
- def __init__(self, to, field_name, related_name=None, limit_choices_to=None,
- parent_link=False, on_delete=None):
+ def __init__(self, to, field_name, related_name=None, related_query_name=None,
+ limit_choices_to=None, parent_link=False, on_delete=None):
super(OneToOneRel, self).__init__(to, field_name,
- related_name=related_name, limit_choices_to=limit_choices_to,
- parent_link=parent_link, on_delete=on_delete
+ related_name=related_name, related_query_name=related_query_name,
+ limit_choices_to=limit_choices_to, parent_link=parent_link, on_delete=on_delete
)
self.multiple = False
class ManyToManyRel(object):
- def __init__(self, to, related_name=None, limit_choices_to=None,
- symmetrical=True, through=None):
+ def __init__(self, to, related_name=None, related_query_name=None,
+ limit_choices_to=None, symmetrical=True, through=None):
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
@@ -999,6 +1003,7 @@ def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
kwargs['rel'] = rel_class(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),
@@ -1211,6 +1216,7 @@ def __init__(self, to, **kwargs):
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))
View
0  tests/modeltests/related_names/__init__.py
No changes.
View
47 tests/modeltests/related_names/models.py
@@ -0,0 +1,47 @@
+from django.db import models
+from django.utils.encoding import python_2_unicode_compatible
+
+
+@python_2_unicode_compatible
+class Building(models.Model):
+ name = models.CharField(max_length=255)
+
+ def __str__(self):
+ return self.name
+
+
+@python_2_unicode_compatible
+class ClassRoom(models.Model):
+ room_number = models.IntegerField()
+ building = models.ForeignKey(Building)
+
+ def __str__(self):
+ return self.room_number
+
+
+@python_2_unicode_compatible
+class BathRoom(models.Model):
+ room_number = models.IntegerField()
+ building = models.ForeignKey(Building,
+ related_name='loo_set', related_query_name='wc')
+
+ def __str__(self):
+ return self.room_number
+
+
+@python_2_unicode_compatible
+class Office(models.Model):
+ room_number = models.IntegerField()
+ building = models.ForeignKey(Building, related_name="offices")
+
+ def __str__(self):
+ return self.room_number
+
+
+@python_2_unicode_compatible
+class Cafeteria(models.Model):
+ room_number = models.IntegerField()
+ building = models.ForeignKey(Building, related_query_name='cafe')
+
+ def __str__(self):
+ return self.room_number
View
57 tests/modeltests/related_names/tests.py
@@ -0,0 +1,57 @@
+from __future__ import absolute_import
+
+from django.test import TestCase
+
+from .models import (
+ BathRoom,
+ Building,
+ Cafeteria,
+ ClassRoom,
+ Office,
+)
+
+
+class RelatedNameTests(TestCase):
+
+ def setUp(self):
+ building = Building.objects.create(name="Django HQ")
+ BathRoom.objects.create(building=building, room_number=1)
+ Cafeteria.objects.create(building=building, room_number=2)
+ ClassRoom.objects.create(building=building, room_number=3)
+ Office.objects.create(building=building, room_number=4)
+
+ # Abbreviations in function names to keep them from getting too long
+ # rn - related_name
+ # rqn - related_query_name
+
+ def test_rn_with_no_rn_and_no_rqn(self):
+ building = Building.objects.get(name="Django HQ")
+ self.assertEquals(building.classroom_set.get().room_number, 3)
+
+ def test_rqn_with_no_rn_and_no_rqn(self):
+ building = Building.objects.get(classroom__room_number=3)
+ self.assertEquals(building.name, "Django HQ")
+
+ def test_rn_with_rn_and_no_rqn(self):
+ building = Building.objects.get(name="Django HQ")
+ self.assertEquals(building.offices.get().room_number, 4)
+
+ def test_rqn_with_rn_and_no_rqn(self):
+ building = Building.objects.get(offices__room_number=4)
+ self.assertEquals(building.name, "Django HQ")
+
+ def test_rn_with_no_rn_and_rqn(self):
+ building = Building.objects.get(name="Django HQ")
+ self.assertEquals(building.cafeteria_set.get().room_number, 2)
+
+ def test_rqn_with_no_rn_and_rqn(self):
+ building = Building.objects.get(cafe__room_number=2)
+ self.assertEquals(building.name, "Django HQ")
+
+ def test_rn_with_rn_and_rqn(self):
+ building = Building.objects.get(name="Django HQ")
+ self.assertEquals(building.loo_set.get().room_number, 1)
+
+ def test_rqn_with_rn_and_rqn(self):
+ building = Building.objects.get(wc__room_number=1)
+ self.assertEquals(building.name, "Django HQ")
Something went wrong with that request. Please try again.