Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion django/contrib/contenttypes/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 15 additions & 9 deletions django/db/models/fields/related.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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))
Expand Down
Empty file.
47 changes: 47 additions & 0 deletions tests/modeltests/related_names/models.py
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions tests/modeltests/related_names/tests.py
Original file line number Diff line number Diff line change
@@ -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")