Commit fa2e1371 authored by Simon Charette's avatar Simon Charette
Browse files

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

parent c4db7f07
Loading
Loading
Loading
Loading
+3 −5
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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,10 +31,12 @@ 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)
@@ -41,11 +45,18 @@ class Favorites(models.Model):
    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
Original line number Diff line number Diff line
@@ -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())
        )