a restaurant can access its place a place can access its restaurant if available p2 doesn t have an associated restaurant the exception raised on attribute access when a related object doesn t exist should be an instance of a subclass of attributeerror refs 21563 set the place using assignment notation because place is the primary key on restaurant the save will create a new restaurant set the place back again using assignment in the reverse direction restaurant objects all just returns the restaurants not the places place objects all returns all places regardless of whether they have restaurants add a waiter to the restaurant query the waiters delete the restaurant the waiter should also be removed onetoone fields still work if you create your own primary key you can have multiple onetoone fields on a model too this will fail because each onetoone field must be unique and link2o1 was used for x1 above 10811 assigning an unsaved object to a onetoonefield should raise an exception place should not cache restaurant regression test for 9023 accessing the reverse relationship shouldn t result in a cascading delete the bug in 9023 if you access the onetoone relation before setting to none and deleting the cascade happens anyway models are created via the m2m relation if the remote model has a onetoonefield 1064 1506 the name of the cache for the reverse object is correct 7173 assigning none succeeds if field is nulltrue assigning none doesn t throw attributeerror if there isn t a related undergroundbar prime the relation s cache with a value of none assigning none works if there isn t a related undergroundbar and the reverse cache has a value of none reassigning the same value doesn t clear a cached instance regression test for 6886 the relatedobject cache look up the objects again so that we get fresh objects p place objects getnamedemon dogs r p restaurant accessing the related object again returns the exactly same object self assertisp restaurant r but if we kill the cache we get a new object del p state fieldscacherestaurant self assertisnotp restaurant r reassigning the restaurant object results in an immediate cache update we can t use a new restaurant because that ll violate onetoone but with a new instance the is test below will fail if 6886 regresses r2 restaurant objects getpkr pk p restaurant r2 self assertisp restaurant r2 assigning none succeeds if field is nulltrue ugbar undergroundbar objects createplacep servescocktailsfalse ugbar place none self assertisnoneugbar place assigning none will not fail place restaurant is nullfalse setattrp restaurant none you also can t assign an object of the wrong type here msg cannot assign place demon dogs the place place restaurant must be a restaurant instance with self assertraisesmessagevalueerror msg setattrp restaurant p creation using keyword argument should cache the related object p place objects getnamedemon dogs r restaurantplacep self assertisr place p creation using keyword argument and unsaved related instance 8070 p place r restaurantplacep self assertisr place p creation using attname keyword argument and an id will cause the related object to be fetched p place objects getnamedemon dogs r restaurantplaceidp id self assertisnotr place p self assertequalr place p def testfilteronetoonerelationsself target target objects create self assertsequenceequaltarget objects filterpointernone target self assertsequenceequaltarget objects excludepointernone self assertsequenceequaltarget objects filtersecondpointernone target self assertsequenceequaltarget objects excludesecondpointernone def testo2oprimarykeydeleteself t target objects createnamename pointer objects createothert numdeleted objs pointer objects filterothernamename delete self assertequalnumdeleted 1 self assertequalobjs onetoone pointer 1 def testsavenullableo2oafterparentself place placenamerose tattoo bar undergroundbarplaceplace place save bar save bar refreshfromdb self assertequalbar place place def testreverseobjectdoesnotexistcacheself p placenamezombie cats addressnot sure p save with self assertnumqueries1 with self assertraisesrestaurant doesnotexist p restaurant with self assertnumqueries0 with self assertraisesrestaurant doesnotexist p restaurant def testreverseobjectcachedwhenrelatedisaccessedself use a fresh object without caches r restaurant objects getpkself r1 pk p r place with self assertnumqueries0 self assertequalp restaurant r def testrelatedobjectcachedwhenreverseisaccessedself use a fresh object without caches p place objects getpkself p1 pk r p restaurant with self assertnumqueries0 self assertequalr place p def testreverseobjectcachedwhenrelatedissetself p placenamezombie cats addressnot sure p save self r1 place p self r1 save with self assertnumqueries0 self assertequalp restaurant self r1 def testreverseobjectcachedwhenrelatedisunsetself b undergroundbarplaceself p1 servescocktailstrue b save with self assertnumqueries0 self assertequalself p1 undergroundbar b b place none b save with self assertnumqueries0 with self assertraisesundergroundbar doesnotexist self p1 undergroundbar def testgetreverseonunsavedobjectself p place when there s no instance of the origin of the onetoone with self assertnumqueries0 with self assertraisesundergroundbar doesnotexist p undergroundbar undergroundbar objects create when there s one instance of the origin p undergroundbar used to return that instance with self assertnumqueries0 with self assertraisesundergroundbar doesnotexist p undergroundbar several instances of the origin are only possible if database allows inserting multiple null rows for a unique constraint if connection features supportsnullableuniqueconstraints undergroundbar objects create when there are several instances of the origin with self assertnumqueries0 with self assertraisesundergroundbar doesnotexist p undergroundbar def testsetreverseonunsavedobjectself p place b undergroundbar objects create assigning a reverse relation on an unsaved object is allowed p undergroundbar b however saving the object is not allowed msg save prohibited to prevent data loss due to unsaved related object place with self assertnumqueries0 with self assertraisesmessagevalueerror msg b save def testnullableo2odeleteself u undergroundbar objects createplaceself p1 u placeid none u save self p1 delete self asserttrueundergroundbar objects filterpku pk exists self assertisnoneundergroundbar objects getpku pk place def testhiddenaccessorself self assertfalse hasattr target hiddenpointer meta getfield target remotefield getaccessorname def testrelatedobjectself publicschool school objects createispublictrue publicdirector director objects createschoolpublicschool istempfalse privateschool school objects createispublicfalse privatedirector director objects createschoolprivateschool istemptrue only one school is available via all due to the custom default manager self assertsequenceequalschool objects all publicschool only one director is available via all due to the custom default manager self assertsequenceequaldirector objects all publicdirector self assertequalpublicdirector school publicschool self assertequalpublicschool director publicdirector make sure the base manager is used so that the related objects is still accessible even if the default manager doesn t normally allow it self assertequalprivatedirector school privateschool make sure the base manager is used so that an student can still access its related school even if the default manager doesn t normally allow it self assertequalprivateschool director privatedirector school meta basemanagername objects school meta expirecache try privatedirector director basemanager getpkprivatedirector pk with self assertraisesschool doesnotexist privatedirector school finally school meta basemanagername none school meta expirecache director meta basemanagername objects director meta expirecache try privateschool school basemanager getpkprivateschool pk with self assertraisesdirector doesnotexist privateschool director finally director meta basemanagername none director meta expirecache def testcreatereverseo2oerrorself msg the following fields do not exist in this model restaurant with self assertraisesmessagevalueerror msg place objects createrestaurantself r1 def testgetorcreatereverseo2oerrorself msg the following fields do not exist in this model restaurant r2 restaurant objects create placeself p2 serveshotdogstrue servespizzafalse with self assertraisesmessagevalueerror msg place objects getorcreatenamenonexistent defaultsrestaurant r2 def testupdateorcreatereverseo2oerrorself msg the following fields do not exist in this model restaurant r2 restaurant objects create placeself p2 serveshotdogstrue servespizzafalse with self assertraisesmessagevalueerror msg place objects updateorcreate namenonexistent defaultsrestaurant r2 def testhasattrrelatedobjectself the exception raised on attribute access when a related object doesn t exist should be an instance of a subclass of attributeerror refs 21563 self assertfalsehasattrdirector director self assertfalsehasattrschool school def testupdateonetoonepkself p1 place objects create p2 place objects create r1 restaurant objects createplacep1 r2 restaurant objects createplacep2 w waiter objects createrestaurantr1 waiter objects updaterestaurantr2 w refreshfromdb self assertequalw restaurant r2 def testrelpksubqueryself r restaurant objects first q1 restaurant objects filterplaceidr pk subquery using primary key and a query against the same model works correctly q2 restaurant objects filterplaceidinq1 self assertsequenceequalq2 r subquery using pkin instead of placeidin work too q2 restaurant objects filter pkinrestaurant objects filterplaceidr place pk self assertsequenceequalq2 r q3 restaurant objects filterplaceinplace objects all self assertsequenceequalq3 r q4 restaurant objects filterplaceinplace objects filteridr pk self assertsequenceequalq4 r def testrelpkexactself r restaurant objects first r2 restaurant objects filterpkexactr first self assertequalr r2 def testprimarykeytofieldfilterself target target objects createnamefoo pointer tofieldpointer objects createtargettarget self assertsequenceequal tofieldpointer objects filtertargettarget pointer self assertsequenceequal tofieldpointer objects filterpkexactpointer pointer def testcachedrelationinvalidatedonsaveself self assertequalself b1 place self p1 caches b1 place self b1 placeid self p2 pk self b1 save self assertequalself b1 place self p2 def testgetprefetchquerysetwarningself places place objects all msg getprefetchqueryset is deprecated use getprefetchquerysets instead with self assertwarnsmessageremovedindjango60warning msg place bar getprefetchquerysetplaces def testgetprefetchquerysetsinvalidquerysetslengthself places place objects all msg querysets argument of getprefetchquerysets should have a length of 1 with self assertraisesmessagevalueerror msg place bar getprefetchquerysets instancesplaces querysetsbar objects all bar objects all a restaurant can access its place a place can access its restaurant if available p2 doesn t have an associated restaurant the exception raised on attribute access when a related object doesn t exist should be an instance of a subclass of attributeerror refs 21563 set the place using assignment notation because place is the primary key on restaurant the save will create a new restaurant set the place back again using assignment in the reverse direction restaurant objects all just returns the restaurants not the places place objects all returns all places regardless of whether they have restaurants add a waiter to the restaurant query the waiters delete the restaurant the waiter should also be removed one to one fields still work if you create your own primary key you can have multiple one to one fields on a model too this will fail because each one to one field must be unique and link2 o1 was used for x1 above 10811 assigning an unsaved object to a onetoonefield should raise an exception place should not cache restaurant regression test for 9023 accessing the reverse relationship shouldn t result in a cascading delete the bug in 9023 if you access the one to one relation before setting to none and deleting the cascade happens anyway models are created via the m2m relation if the remote model has a onetoonefield 1064 1506 the name of the cache for the reverse object is correct 7173 assigning none succeeds if field is null true assigning none doesn t throw attributeerror if there isn t a related undergroundbar prime the relation s cache with a value of none assigning none works if there isn t a related undergroundbar and the reverse cache has a value of none reassigning the same value doesn t clear a cached instance regression test for 6886 the related object cache look up the objects again so that we get fresh objects accessing the related object again returns the exactly same object but if we kill the cache we get a new object reassigning the restaurant object results in an immediate cache update we can t use a new restaurant because that ll violate one to one but with a new instance the is test below will fail if 6886 regresses assigning none succeeds if field is null true assigning none will not fail place restaurant is null false you also can t assign an object of the wrong type here creation using keyword argument should cache the related object creation using keyword argument and unsaved related instance 8070 creation using attname keyword argument and an id will cause the related object to be fetched regression test for 9968 filtering reverse one to one relations with primary_key true was misbehaving we test both primary_key true false cases here to prevent any reappearance of the problem regression for 13839 and 17439 doesnotexist on a reverse one to one relation is cached regression for 13839 and 17439 the target of a one to one relation is cached when the origin is accessed through the reverse relation use a fresh object without caches regression for 13839 and 17439 the origin of a one to one relation is cached when the target is accessed through the reverse relation use a fresh object without caches regression for 13839 and 17439 the target of a one to one relation is always cached regression for 13839 and 17439 the target of a one to one relation is always cached regression for 18153 and 19089 accessing the reverse relation on an unsaved object always raises an exception when there s no instance of the origin of the one to one when there s one instance of the origin p undergroundbar used to return that instance several instances of the origin are only possible if database allows inserting multiple null rows for a unique constraint when there are several instances of the origin writing to the reverse relation on an unsaved object is impossible too assigning a reverse relation on an unsaved object is allowed however saving the object is not allowed when a ending related name is specified no reverse accessor should be added to the related model only one school is available via all due to the custom default manager only one director is available via all due to the custom default manager make sure the base manager is used so that the related objects is still accessible even if the default manager doesn t normally allow it make sure the base manager is used so that an student can still access its related school even if the default manager doesn t normally allow it the exception raised on attribute access when a related object doesn t exist should be an instance of a subclass of attributeerror refs 21563 subquery using primary key and a query against the same model works correctly subquery using pk__in instead of place_id__in work too model save invalidates stale onetoonefield relations after a primary key assignment caches b1 place
from django.db import IntegrityError, connection, transaction
from django.test import TestCase
from django.utils.deprecation import RemovedInDjango60Warning

from .models import (
    Bar,
    Director,
    Favorites,
    HiddenPointer,
    ManualPrimaryKey,
    MultiModel,
    Place,
    Pointer,
    RelatedModel,
    Restaurant,
    School,
    Target,
    ToFieldPointer,
    UndergroundBar,
    Waiter,
)


class OneToOneTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.p1 = Place.objects.create(name="Demon Dogs", address="944 W. Fullerton")
        cls.p2 = Place.objects.create(name="Ace Hardware", address="1013 N. Ashland")
        cls.r1 = Restaurant.objects.create(
            place=cls.p1, serves_hot_dogs=True, serves_pizza=False
        )
        cls.b1 = Bar.objects.create(place=cls.p1, serves_cocktails=False)

    def test_getter(self):
        
        self.assertEqual(repr(self.r1.place), "<Place: Demon Dogs the place>")
        
        self.assertEqual(
            repr(self.p1.restaurant), "<Restaurant: Demon Dogs the restaurant>"
        )
        
        with self.assertRaisesMessage(
            Restaurant.DoesNotExist, "Place has no restaurant"
        ):
            self.p2.restaurant
        
        
        
        self.assertFalse(hasattr(self.p2, "restaurant"))

    def test_setter(self):
        
        
        self.r1.place = self.p2
        self.r1.save()
        self.assertEqual(
            repr(self.p2.restaurant), "<Restaurant: Ace Hardware the restaurant>"
        )
        self.assertEqual(repr(self.r1.place), "<Place: Ace Hardware the place>")
        self.assertEqual(self.p2.pk, self.r1.pk)
        
        self.p1.restaurant = self.r1
        self.assertEqual(
            repr(self.p1.restaurant), "<Restaurant: Demon Dogs the restaurant>"
        )
        r = Restaurant.objects.get(pk=self.p1.id)
        self.assertEqual(repr(r.place), "<Place: Demon Dogs the place>")

    def test_manager_all(self):
        
        self.assertSequenceEqual(Restaurant.objects.all(), [self.r1])
        
        
        self.assertSequenceEqual(Place.objects.order_by("name"), [self.p2, self.p1])

    def test_manager_get(self):
        def assert_get_restaurant(**params):
            self.assertEqual(
                repr(Restaurant.objects.get(**params)),
                "<Restaurant: Demon Dogs the restaurant>",
            )

        assert_get_restaurant(place__id__exact=self.p1.pk)
        assert_get_restaurant(place__id=self.p1.pk)
        assert_get_restaurant(place__exact=self.p1.pk)
        assert_get_restaurant(place__exact=self.p1)
        assert_get_restaurant(place=self.p1.pk)
        assert_get_restaurant(place=self.p1)
        assert_get_restaurant(pk=self.p1.pk)
        assert_get_restaurant(place__pk__exact=self.p1.pk)
        assert_get_restaurant(place__pk=self.p1.pk)
        assert_get_restaurant(place__name__startswith="Demon")

        def assert_get_place(**params):
            self.assertEqual(
                repr(Place.objects.get(**params)), "<Place: Demon Dogs the place>"
            )

        assert_get_place(restaurant__place__exact=self.p1.pk)
        assert_get_place(restaurant__place__exact=self.p1)
        assert_get_place(restaurant__place__pk=self.p1.pk)
        assert_get_place(restaurant__exact=self.p1.pk)
        assert_get_place(restaurant__exact=self.r1)
        assert_get_place(restaurant__pk=self.p1.pk)
        assert_get_place(restaurant=self.p1.pk)
        assert_get_place(restaurant=self.r1)
        assert_get_place(id__exact=self.p1.pk)
        assert_get_place(pk=self.p1.pk)

    def test_foreign_key(self):
        
        w = self.r1.waiter_set.create(name="Joe")
        self.assertEqual(
            repr(w), "<Waiter: Joe the waiter at Demon Dogs the restaurant>"
        )

        
        def assert_filter_waiters(**params):
            self.assertSequenceEqual(Waiter.objects.filter(**params), [w])

        assert_filter_waiters(restaurant__place__exact=self.p1.pk)
        assert_filter_waiters(restaurant__place__exact=self.p1)
        assert_filter_waiters(restaurant__place__pk=self.p1.pk)
        assert_filter_waiters(restaurant__exact=self.r1.pk)
        assert_filter_waiters(restaurant__exact=self.r1)
        assert_filter_waiters(restaurant__pk=self.r1.pk)
        assert_filter_waiters(restaurant=self.r1.pk)
        assert_filter_waiters(restaurant=self.r1)
        assert_filter_waiters(id__exact=w.pk)
        assert_filter_waiters(pk=w.pk)
        
        r = Restaurant.objects.get(pk=self.r1.pk)
        r.delete()
        self.assertEqual(Waiter.objects.count(), 0)

    def test_multiple_o2o(self):
        
        o1 = ManualPrimaryKey(primary_key="abc123", name="primary")
        o1.save()
        o2 = RelatedModel(link=o1, name="secondary")
        o2.save()

        
        x1 = MultiModel(link1=self.p1, link2=o1, name="x1")
        x1.save()
        self.assertEqual(repr(o1.multimodel), "<MultiModel: Multimodel x1>")
        
        
        mm = MultiModel(link1=self.p2, link2=o1, name="x1")
        with self.assertRaises(IntegrityError):
            with transaction.atomic():
                mm.save()

    def test_unsaved_object(self):
        
        place = Place(name="User", address="London")
        with self.assertRaises(Restaurant.DoesNotExist):
            place.restaurant
        msg = (
            "save() prohibited to prevent data loss due to unsaved related object "
            "'place'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            Restaurant.objects.create(
                place=place, serves_hot_dogs=True, serves_pizza=False
            )
        
        with self.assertRaises(Restaurant.DoesNotExist):
            place.restaurant

    def test_reverse_relationship_cache_cascade(self):
        
        bar = UndergroundBar.objects.create(place=self.p1, serves_cocktails=False)

        
        
        self.p1.undergroundbar
        bar.place.name = "foo"
        bar.place = None
        bar.save()
        self.p1.delete()

        self.assertEqual(Place.objects.count(), 1)
        self.assertEqual(UndergroundBar.objects.count(), 1)

    def test_create_models_m2m(self):
        
        f = Favorites(name="Fred")
        f.save()
        f.restaurants.set([self.r1])
        self.assertSequenceEqual(f.restaurants.all(), [self.r1])

    def test_reverse_object_cache(self):
        
        self.assertEqual(self.p1.restaurant, self.r1)
        self.assertEqual(self.p1.bar, self.b1)

    def test_assign_none_reverse_relation(self):
        p = Place.objects.get(name="Demon Dogs")
        
        ug_bar = UndergroundBar.objects.create(place=p, serves_cocktails=False)
        p.undergroundbar = None
        self.assertIsNone(ug_bar.place)
        ug_bar.save()
        ug_bar.refresh_from_db()
        self.assertIsNone(ug_bar.place)

    def test_assign_none_null_reverse_relation(self):
        p = Place.objects.get(name="Demon Dogs")
        
        
        p.undergroundbar = None

    def test_assign_none_to_null_cached_reverse_relation(self):
        p = Place.objects.get(name="Demon Dogs")
        
        with self.assertRaises(Place.undergroundbar.RelatedObjectDoesNotExist):
            getattr(p, "undergroundbar")
        
        
        p.undergroundbar = None

    def test_assign_o2o_id_value(self):
        b = UndergroundBar.objects.create(place=self.p1)
        b.place_id = self.p2.pk
        b.save()
        self.assertEqual(b.place_id, self.p2.pk)
        self.assertFalse(UndergroundBar.place.is_cached(b))
        self.assertEqual(b.place, self.p2)
        self.assertTrue(UndergroundBar.place.is_cached(b))
        
        b.place_id = self.p2.pk
        self.assertTrue(UndergroundBar.place.is_cached(b))

    def test_assign_o2o_id_none(self):
        b = UndergroundBar.objects.create(place=self.p1)
        b.place_id = None
        b.save()
        self.assertIsNone(b.place_id)
        self.assertFalse(UndergroundBar.place.is_cached(b))
        self.assertIsNone(b.place)
        self.assertTrue(UndergroundBar.place.is_cached(b))

    def test_related_object_cache(self):
        

        
        p = Place.objects.get(name="Demon Dogs")
        r = p.restaurant

        
        self.assertIs(p.restaurant, r)

        
        del p._state.fields_cache["restaurant"]
        self.assertIsNot(p.restaurant, r)

        
        
        
        r2 = Restaurant.objects.get(pk=r.pk)
        p.restaurant = r2
        self.assertIs(p.restaurant, r2)

        
        ug_bar = UndergroundBar.objects.create(place=p, serves_cocktails=False)
        ug_bar.place = None
        self.assertIsNone(ug_bar.place)

        
        setattr(p, "restaurant", None)

        
        msg = (
            'Cannot assign "<Place: Demon Dogs the place>": '
            '"Place.restaurant" must be a "Restaurant" instance.'
        )
        with self.assertRaisesMessage(ValueError, msg):
            setattr(p, "restaurant", p)

        
        p = Place.objects.get(name="Demon Dogs")
        r = Restaurant(place=p)
        self.assertIs(r.place, p)

        
        p = Place()
        r = Restaurant(place=p)
        self.assertIs(r.place, p)

        
        
        p = Place.objects.get(name="Demon Dogs")
        r = Restaurant(place_id=p.id)
        self.assertIsNot(r.place, p)
        self.assertEqual(r.place, p)

    def test_filter_one_to_one_relations(self):
        
        target = Target.objects.create()
        self.assertSequenceEqual(Target.objects.filter(pointer=None), [target])
        self.assertSequenceEqual(Target.objects.exclude(pointer=None), [])
        self.assertSequenceEqual(Target.objects.filter(second_pointer=None), [target])
        self.assertSequenceEqual(Target.objects.exclude(second_pointer=None), [])

    def test_o2o_primary_key_delete(self):
        t = Target.objects.create(name="name")
        Pointer.objects.create(other=t)
        num_deleted, objs = Pointer.objects.filter(other__name="name").delete()
        self.assertEqual(num_deleted, 1)
        self.assertEqual(objs, {"one_to_one.Pointer": 1})

    def test_save_nullable_o2o_after_parent(self):
        place = Place(name="Rose tattoo")
        bar = UndergroundBar(place=place)
        place.save()
        bar.save()
        bar.refresh_from_db()
        self.assertEqual(bar.place, place)

    def test_reverse_object_does_not_exist_cache(self):
        
        p = Place(name="Zombie Cats", address="Not sure")
        p.save()
        with self.assertNumQueries(1):
            with self.assertRaises(Restaurant.DoesNotExist):
                p.restaurant
        with self.assertNumQueries(0):
            with self.assertRaises(Restaurant.DoesNotExist):
                p.restaurant

    def test_reverse_object_cached_when_related_is_accessed(self):
        
        
        r = Restaurant.objects.get(pk=self.r1.pk)
        p = r.place
        with self.assertNumQueries(0):
            self.assertEqual(p.restaurant, r)

    def test_related_object_cached_when_reverse_is_accessed(self):
        
        
        p = Place.objects.get(pk=self.p1.pk)
        r = p.restaurant
        with self.assertNumQueries(0):
            self.assertEqual(r.place, p)

    def test_reverse_object_cached_when_related_is_set(self):
        
        p = Place(name="Zombie Cats", address="Not sure")
        p.save()
        self.r1.place = p
        self.r1.save()
        with self.assertNumQueries(0):
            self.assertEqual(p.restaurant, self.r1)

    def test_reverse_object_cached_when_related_is_unset(self):
        
        b = UndergroundBar(place=self.p1, serves_cocktails=True)
        b.save()
        with self.assertNumQueries(0):
            self.assertEqual(self.p1.undergroundbar, b)
        b.place = None
        b.save()
        with self.assertNumQueries(0):
            with self.assertRaises(UndergroundBar.DoesNotExist):
                self.p1.undergroundbar

    def test_get_reverse_on_unsaved_object(self):
        
        p = Place()

        
        with self.assertNumQueries(0):
            with self.assertRaises(UndergroundBar.DoesNotExist):
                p.undergroundbar

        UndergroundBar.objects.create()

        
        
        with self.assertNumQueries(0):
            with self.assertRaises(UndergroundBar.DoesNotExist):
                p.undergroundbar

        
        
        if connection.features.supports_nullable_unique_constraints:
            UndergroundBar.objects.create()

            
            with self.assertNumQueries(0):
                with self.assertRaises(UndergroundBar.DoesNotExist):
                    p.undergroundbar

    def test_set_reverse_on_unsaved_object(self):
        
        p = Place()
        b = UndergroundBar.objects.create()

        
        p.undergroundbar = b

        
        msg = (
            "save() prohibited to prevent data loss due to unsaved related object "
            "'place'."
        )
        with self.assertNumQueries(0):
            with self.assertRaisesMessage(ValueError, msg):
                b.save()

    def test_nullable_o2o_delete(self):
        u = UndergroundBar.objects.create(place=self.p1)
        u.place_id = None
        u.save()
        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):
        
        self.assertFalse(
            hasattr(
                Target,
                HiddenPointer._meta.get_field(
                    "target"
                ).remote_field.get_accessor_name(),
            )
        )

    def test_related_object(self):
        public_school = School.objects.create(is_public=True)
        public_director = Director.objects.create(school=public_school, is_temp=False)

        private_school = School.objects.create(is_public=False)
        private_director = Director.objects.create(school=private_school, is_temp=True)

        
        self.assertSequenceEqual(School.objects.all(), [public_school])

        
        self.assertSequenceEqual(Director.objects.all(), [public_director])

        self.assertEqual(public_director.school, public_school)
        self.assertEqual(public_school.director, public_director)

        
        
        
        self.assertEqual(private_director.school, private_school)

        
        
        
        self.assertEqual(private_school.director, private_director)

        School._meta.base_manager_name = "objects"
        School._meta._expire_cache()
        try:
            private_director = Director._base_manager.get(pk=private_director.pk)
            with self.assertRaises(School.DoesNotExist):
                private_director.school
        finally:
            School._meta.base_manager_name = None
            School._meta._expire_cache()

        Director._meta.base_manager_name = "objects"
        Director._meta._expire_cache()
        try:
            private_school = School._base_manager.get(pk=private_school.pk)
            with self.assertRaises(Director.DoesNotExist):
                private_school.director
        finally:
            Director._meta.base_manager_name = None
            Director._meta._expire_cache()

    def test_create_reverse_o2o_error(self):
        msg = "The following fields do not exist in this model: restaurant"
        with self.assertRaisesMessage(ValueError, msg):
            Place.objects.create(restaurant=self.r1)

    def test_get_or_create_reverse_o2o_error(self):
        msg = "The following fields do not exist in this model: restaurant"
        r2 = Restaurant.objects.create(
            place=self.p2, serves_hot_dogs=True, serves_pizza=False
        )
        with self.assertRaisesMessage(ValueError, msg):
            Place.objects.get_or_create(name="nonexistent", defaults={"restaurant": r2})

    def test_update_or_create_reverse_o2o_error(self):
        msg = "The following fields do not exist in this model: restaurant"
        r2 = Restaurant.objects.create(
            place=self.p2, serves_hot_dogs=True, serves_pizza=False
        )
        with self.assertRaisesMessage(ValueError, msg):
            Place.objects.update_or_create(
                name="nonexistent", defaults={"restaurant": r2}
            )

    def test_hasattr_related_object(self):
        
        
        
        self.assertFalse(hasattr(Director(), "director"))
        self.assertFalse(hasattr(School(), "school"))

    def test_update_one_to_one_pk(self):
        p1 = Place.objects.create()
        p2 = Place.objects.create()
        r1 = Restaurant.objects.create(place=p1)
        r2 = Restaurant.objects.create(place=p2)
        w = Waiter.objects.create(restaurant=r1)

        Waiter.objects.update(restaurant=r2)
        w.refresh_from_db()
        self.assertEqual(w.restaurant, r2)

    def test_rel_pk_subquery(self):
        r = Restaurant.objects.first()
        q1 = Restaurant.objects.filter(place_id=r.pk)
        
        
        q2 = Restaurant.objects.filter(place_id__in=q1)
        self.assertSequenceEqual(q2, [r])
        
        q2 = Restaurant.objects.filter(
            pk__in=Restaurant.objects.filter(place__id=r.place.pk)
        )
        self.assertSequenceEqual(q2, [r])
        q3 = Restaurant.objects.filter(place__in=Place.objects.all())
        self.assertSequenceEqual(q3, [r])
        q4 = Restaurant.objects.filter(place__in=Place.objects.filter(id=r.pk))
        self.assertSequenceEqual(q4, [r])

    def test_rel_pk_exact(self):
        r = Restaurant.objects.first()
        r2 = Restaurant.objects.filter(pk__exact=r).first()
        self.assertEqual(r, r2)

    def test_primary_key_to_field_filter(self):
        target = Target.objects.create(name="foo")
        pointer = ToFieldPointer.objects.create(target=target)
        self.assertSequenceEqual(
            ToFieldPointer.objects.filter(target=target), [pointer]
        )
        self.assertSequenceEqual(
            ToFieldPointer.objects.filter(pk__exact=pointer), [pointer]
        )

    def test_cached_relation_invalidated_on_save(self):
        
        self.assertEqual(self.b1.place, self.p1)  
        self.b1.place_id = self.p2.pk
        self.b1.save()
        self.assertEqual(self.b1.place, self.p2)

    def test_get_prefetch_queryset_warning(self):
        places = Place.objects.all()
        msg = (
            "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() "
            "instead."
        )
        with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
            Place.bar.get_prefetch_queryset(places)

    def test_get_prefetch_querysets_invalid_querysets_length(self):
        places = Place.objects.all()
        msg = (
            "querysets argument of get_prefetch_querysets() should have a length of 1."
        )
        with self.assertRaisesMessage(ValueError, msg):
            Place.bar.get_prefetch_querysets(
                instances=places,
                querysets=[Bar.objects.all(), Bar.objects.all()],
            )