Browse Source

Fixed #21216 -- Allow `OneToOneField` reverse accessor to be hidden.

Simon Charette 11 years ago
parent
commit
fa2e1371cd

+ 3 - 5
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

+ 5 - 0
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
 ^^^^^^^
 

+ 13 - 2
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+')

+ 13 - 3
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())
+        )