summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2013-08-20 01:52:32 -0400
committerSimon Charette <charette.s@gmail.com>2013-10-03 13:20:05 -0400
commitfa2e1371cda1e72d82b4133ad0b49a18e43ba411 (patch)
tree9d732e2ac0eb0b89cfc37ecccab41fe64eb31b33
parentc4db7f075e269411287ff14b5518412250ba455f (diff)
Fixed #21216 -- Allow `OneToOneField` reverse accessor to be hidden.
-rw-r--r--django/db/models/fields/related.py8
-rw-r--r--docs/releases/1.7.txt5
-rw-r--r--tests/one_to_one_regress/models.py15
-rw-r--r--tests/one_to_one_regress/tests.py16
4 files changed, 34 insertions, 10 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 6cd8d193f5..aa2a83bd97 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -960,6 +960,7 @@ class ManyToManyRel(object):
class ForeignObject(RelatedField):
requires_unique_target = True
generate_reverse_relation = True
+ related_accessor_class = ForeignRelatedObjectsDescriptor
def __init__(self, to, from_fields, to_fields, **kwargs):
self.from_fields = from_fields
@@ -1160,7 +1161,7 @@ class ForeignObject(RelatedField):
# Internal FK's - i.e., those with a related name ending with '+' -
# and swapped models don't get a related descriptor.
if not self.rel.is_hidden() and not related.model._meta.swapped:
- setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
+ setattr(cls, related.get_accessor_name(), self.related_accessor_class(related))
if self.rel.limit_choices_to:
cls._meta.related_fkey_lookups.append(self.rel.limit_choices_to)
@@ -1334,6 +1335,7 @@ class OneToOneField(ForeignKey):
always returns the object pointed to (since there will only ever be one),
rather than returning a list.
"""
+ related_accessor_class = SingleRelatedObjectDescriptor
description = _("One-to-one relationship")
def __init__(self, to, to_field=None, **kwargs):
@@ -1346,10 +1348,6 @@ class OneToOneField(ForeignKey):
del kwargs['unique']
return name, path, args, kwargs
- def contribute_to_related_class(self, cls, related):
- setattr(cls, related.get_accessor_name(),
- SingleRelatedObjectDescriptor(related))
-
def formfield(self, **kwargs):
if self.rel.parent_link:
return None
diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt
index d613220508..8f650a1a9e 100644
--- a/docs/releases/1.7.txt
+++ b/docs/releases/1.7.txt
@@ -289,6 +289,11 @@ Models
* Explicit :class:`~django.db.models.OneToOneField` for
:ref:`multi-table-inheritance` are now discovered in abstract classes.
+* Is it now possible to avoid creating a backward relation for
+ :class:`~django.db.models.OneToOneField` by setting its
+ :attr:`~django.db.models.ForeignKey.related_name` to
+ `'+'` or ending it with `'+'`.
+
Signals
^^^^^^^
diff --git a/tests/one_to_one_regress/models.py b/tests/one_to_one_regress/models.py
index 9b65edf2ab..3cd4e17604 100644
--- a/tests/one_to_one_regress/models.py
+++ b/tests/one_to_one_regress/models.py
@@ -12,6 +12,7 @@ class Place(models.Model):
def __str__(self):
return "%s the place" % self.name
+
@python_2_unicode_compatible
class Restaurant(models.Model):
place = models.OneToOneField(Place)
@@ -21,6 +22,7 @@ class Restaurant(models.Model):
def __str__(self):
return "%s the restaurant" % self.place.name
+
@python_2_unicode_compatible
class Bar(models.Model):
place = models.OneToOneField(Place)
@@ -29,23 +31,32 @@ class Bar(models.Model):
def __str__(self):
return "%s the bar" % self.place.name
+
class UndergroundBar(models.Model):
place = models.OneToOneField(Place, null=True)
serves_cocktails = models.BooleanField(default=True)
+
@python_2_unicode_compatible
class Favorites(models.Model):
- name = models.CharField(max_length = 50)
+ name = models.CharField(max_length=50)
restaurants = models.ManyToManyField(Restaurant)
def __str__(self):
return "Favorites for %s" % self.name
+
class Target(models.Model):
pass
+
class Pointer(models.Model):
other = models.OneToOneField(Target, primary_key=True)
+
class Pointer2(models.Model):
- other = models.OneToOneField(Target)
+ other = models.OneToOneField(Target, related_name='second_pointer')
+
+
+class HiddenPointer(models.Model):
+ target = models.OneToOneField(Target, related_name='hidden+')
diff --git a/tests/one_to_one_regress/tests.py b/tests/one_to_one_regress/tests.py
index 3dfe3e466e..da4309e821 100644
--- a/tests/one_to_one_regress/tests.py
+++ b/tests/one_to_one_regress/tests.py
@@ -2,7 +2,8 @@ from __future__ import unicode_literals
from django.test import TestCase
-from .models import Place, Restaurant, Bar, Favorites, Target, UndergroundBar
+from .models import (Bar, Favorites, HiddenPointer, Place, Restaurant, Target,
+ UndergroundBar)
class OneToOneRegressionTests(TestCase):
@@ -125,11 +126,11 @@ class OneToOneRegressionTests(TestCase):
[]
)
self.assertQuerysetEqual(
- Target.objects.filter(pointer2=None),
+ Target.objects.filter(second_pointer=None),
['<Target: Target object>']
)
self.assertQuerysetEqual(
- Target.objects.exclude(pointer2=None),
+ Target.objects.exclude(second_pointer=None),
[]
)
@@ -250,3 +251,12 @@ class OneToOneRegressionTests(TestCase):
self.p1.delete()
self.assertTrue(UndergroundBar.objects.filter(pk=u.pk).exists())
self.assertIsNone(UndergroundBar.objects.get(pk=u.pk).place)
+
+ def test_hidden_accessor(self):
+ """
+ When a '+' ending related name is specified no reverse accessor should
+ be added to the related model.
+ """
+ self.assertFalse(
+ hasattr(Target, HiddenPointer._meta.get_field('target').related.get_accessor_name())
+ )