the student and worker models both have name and age fields on them and inherit the str method just as with normal python subclassing this is useful if you want to factor out common information for programming purposes but still completely independent separate models at the database level the children inherit the meta class of their parents if they don t specify their own since student does not subclass commoninfo s meta it has the effect of completely overriding it so ordering by name doesn t take place for students however the commoninfo class cannot be used as a normal model it doesn t exist as a model even though p supplier for a place p a parent of a supplier a restaurant object cannot access that reverse relation since it s not part of the placesupplier hierarchy the post model has distinct accessors for the comment and link models the post model doesn t have an attribute called attachedclasssset the post model doesn t have a related query accessor based on relatedname attachedcommentset make sure restaurant and italianrestaurant have the right fields in the right order lowlevel test for relatedval higher level test for correct query values title foof not accidentally found 1 insert for each base 3 selects for the parents 1 update for the child updating a field of a model subclass doesn t issue an update query constrained by an inner query 10399 capture the expected query in a database agnostic way capture the queries executed when a subclassed model instance is saved creating a child with nonabstract parents only issues inserts def a grandchild objects create emailgrandparentexample com firstnamegrand lastnameparent def b grandchild save for i test in enumeratea b with self subtestii self assertnumqueries4 capturequeriescontext connection as queries test for query in queries sql querysql self assertininsert into sql sql def testcreatecopywithinheritedm2mself restaurant restaurant objects create supplier customsupplier objects create namecentral market address944 w fullerton supplier customers setrestaurant oldcustomers supplier customers all supplier pk none supplier id none supplier state adding true supplier save supplier customers setoldcustomers supplier supplier objects getpksupplier pk self assertcountequalsupplier customers all oldcustomers self assertsequenceequalsupplier customers all restaurant def testeqself equality doesn t transfer in multitable inheritance self assertnotequalplaceid1 restaurantid1 self assertnotequalrestaurantid1 placeid1 def testmixininitself m mixinmodel self assertequalm otherattr 1 isolateappsmodelinheritance def testabstractparentlinkself class amodels model pass class ba a models onetoonefielda parentlinktrue ondeletemodels cascade class meta abstract true class cb pass self assertisc meta parentsa c meta getfielda isolateappsmodelinheritance def testinitsubclassself savedkwargs class amodels model def initsubclasscls kwargs super initsubclass savedkwargs updatekwargs kwargs x 1 y 2 z 3 class ba kwargs pass self assertequalsavedkwargs kwargs isolateappsmodelinheritance def testsetnameself class classattr called none def setnameself owner name self assertisnoneself called self called owner name class amodels model attr classattr self assertequala attr called a attr def testinheritedorderingpkdescself p1 parent objects createfirstnamejoe emailjoeemail com p2 parent objects createfirstnamejon emailjonemail com expectedorderbysql order by s s desc connection ops quotenameparent meta dbtable connection ops quotenameparent meta getfieldgrandparentptr column qs parent objects all self assertsequenceequalqs p2 p1 self assertinexpectedorderbysql strqs query def testquerysetclassgetitemself self assertismodels querysetpost models queryset self assertismodels querysetpost post models queryset self assertismodels querysetpost int str models queryset def testshadowparentattributewithfieldself class scalarparentmodels model foo 1 class scalaroverridescalarparent foo models integerfield self assertequaltypescalaroverride foo deferredattribute def testshadowparentpropertywithfieldself class propertyparentmodels model property def fooself pass class propertyoverridepropertyparent foo models integerfield self assertequaltypepropertyoverride foo deferredattribute def testshadowparentmethodwithfieldself class methodparentmodels model def fooself pass class methodoverridemethodparent foo models integerfield self assertequaltypemethodoverride foo deferredattribute class modelinheritancedatateststestcase classmethod def setuptestdatacls cls restaurant restaurant objects create namedemon dogs address944 w fullerton serveshotdogstrue servespizzafalse rating2 chef chef objects createnamealbert cls italianrestaurant italianrestaurant objects create nameristorante miron address1234 w ash serveshotdogsfalse servespizzafalse servesgnocchitrue rating4 chefchef def testfilterinheritedmodelself self assertquerysetequal italianrestaurant objects filteraddress1234 w ash ristorante miron attrgettername def testupdateinheritedmodelself self italianrestaurant address 1234 w elm self italianrestaurant save self assertquerysetequal italianrestaurant objects filteraddress1234 w elm ristorante miron attrgettername def testparentfieldsavailableforfilteringinchildmodelself parent fields can be used directly in filters on the child model self assertquerysetequal restaurant objects filternamedemon dogs demon dogs attrgettername self assertquerysetequal italianrestaurant objects filteraddress1234 w ash ristorante miron attrgettername def testfilteronparentreturnsobjectofparenttypeself filters against the parent model return objects of the parent s type p place objects getnamedemon dogs self assertistypep place def testparentchildonetoonelinkself since the parent and child are linked by an automatically created onetoonefield you can get from the parent to the child by using the child s name self assertequal place objects getnamedemon dogs restaurant restaurant objects getnamedemon dogs self assertequal place objects getnameristorante miron restaurant italianrestaurant italianrestaurant objects getnameristorante miron self assertequal restaurant objects getnameristorante miron italianrestaurant italianrestaurant objects getnameristorante miron def testparentchildonetoonelinkonnonrelatedobjectsself this won t work because the demon dogs restaurant is not an italian restaurant with self assertraisesitalianrestaurant doesnotexist place objects getnamedemon dogs restaurant italianrestaurant def testinheriteddoesnotexistexceptionself an italianrestaurant which does not exist is also a place which does not exist with self assertraisesplace doesnotexist italianrestaurant objects getnamethe noodle void def testinheritedmultipleobjectsreturnedexceptionself multipleobjectsreturned is also inherited with self assertraisesplace multipleobjectsreturned restaurant objects get def testrelatedobjectsforinheritedmodelsself related objects work just as they normally do s1 supplier objects createnamejoe s chickens address123 sesame st s1 customers setself restaurant self italianrestaurant s2 supplier objects createnameluigi s pasta address456 sesame st s2 customers setself italianrestaurant this won t work because the place we select is not a restaurant it s a supplier p place objects getnamejoe s chickens with self assertraisesrestaurant doesnotexist p restaurant self assertequalp supplier s1 self assertquerysetequal self italianrestaurant provider orderbyname luigi s pasta joe s chickens attrgettername self assertquerysetequal restaurant objects filterprovidernamecontainschickens ristorante miron demon dogs attrgettername self assertquerysetequal italianrestaurant objects filterprovidernamecontainschickens ristorante miron attrgettername parkinglot objects createnamemain st address111 main st mainsites1 parkinglot objects create namewell lit address124 sesame st mainsiteself italianrestaurant self assertequal restaurant objects getlotnamewell lit name ristorante miron def testupdateworksonparentandchildmodelsatonceself the update command can update fields in parent and child classes at once although it executed multiple sql queries to do so rows restaurant objects filter serveshotdogstrue namecontainsd updatenamedemon puppies serveshotdogsfalse self assertequalrows 1 r1 restaurant objects getpkself restaurant pk self assertfalser1 serveshotdogs self assertequalr1 name demon puppies def testvaluesworksonparentmodelfieldsself the values command also works on fields from parent models self assertsequenceequal italianrestaurant objects valuesname rating rating 4 name ristorante miron def testselectrelatedworksonparentmodelfieldsself selectrelated works with fields from the parent object as if they were a normal part of the model self assertnumqueries2 lambda italianrestaurant objects all0 chef self assertnumqueries 1 lambda italianrestaurant objects selectrelatedchef0 chef def testselectrelateddeferself 23370 should be able to defer child fields when using qs restaurant objects selectrelateditalianrestaurant deferitalianrestaurantservesgnocchi orderbyrating the field was actually deferred with self assertnumqueries2 objs listqs all self asserttrueobjs1 italianrestaurant servesgnocchi model fields where assigned correct values self assertequalqs0 name demon dogs self assertequalqs0 rating 2 self assertequalqs1 italianrestaurant name ristorante miron self assertequalqs1 italianrestaurant rating 4 def testparentcachereuseself place place objects create grandchild objects createplaceplace grandparent grandparent objects latestpk with self assertnumqueries1 self assertequalgrandparent place place parent grandparent parent with self assertnumqueries0 self assertequalparent place place child parent child with self assertnumqueries0 self assertequalchild place place grandchild child grandchild with self assertnumqueries0 self assertequalgrandchild place place def testupdatequerycountsself with self assertnumqueries3 self italianrestaurant save def testfilterinheritedonnullself refs 12567 supplier objects create namecentral market address610 some street self assertquerysetequal place objects filtersupplierisnullfalse central market attrgettername self assertquerysetequal place objects filtersupplierisnulltrue orderbyname demon dogs ristorante miron attrgettername def testexcludeinheritedonnullself refs 12567 supplier objects create namecentral market address610 some street self assertquerysetequal place objects excludesupplierisnullfalse orderbyname demon dogs ristorante miron attrgettername self assertquerysetequal place objects excludesupplierisnulltrue central market attrgettername isolateappsmodelinheritance modelinheritance tests class inheritancesamemodelnametestssimpletestcase def testabstractfkrelatednameself relatedname applabelsclasssreferences class referencedmodels model class meta applabel modelinheritance class abstractreferentmodels model reference models foreignkey referenced models cascade relatednamerelatedname class meta applabel modelinheritance abstract true class referentabstractreferent class meta applabel modelinheritance localreferent referent class referentabstractreferent class meta applabel tests foreignreferent referent self assertfalsehasattrreferenced relatedname self assertis referenced modelinheritancereferentreferences field model localreferent self assertisreferenced testsreferentreferences field model foreignreferent class inheritanceuniqueteststestcase classmethod def setuptestdatacls cls grandparent grandparent objects create emailgrandparentexample com firstnamegrand lastnameparent def testuniqueself grandchild grandchild emailself grandparent email firstnamegrand lastnamechild msg grand parent with this email already exists with self assertraisesmessagevalidationerror msg grandchild validateunique def testuniquetogetherself grandchild grandchild emailgrandchildexample com firstnameself grandparent firstname lastnameself grandparent lastname msg grand parent with this first name and last name already exists with self assertraisesmessagevalidationerror msg grandchild validateunique the student and worker models both have name and age fields on them and inherit the __str__ method just as with normal python subclassing this is useful if you want to factor out common information for programming purposes but still completely independent separate models at the database level the children inherit the meta class of their parents if they don t specify their own since student does not subclass commoninfo s meta it has the effect of completely overriding it so ordering by name doesn t take place for students however the commoninfo class cannot be used as a normal model it doesn t exist as a model even though p supplier for a place p a parent of a supplier a restaurant object cannot access that reverse relation since it s not part of the place supplier hierarchy the post model has distinct accessors for the comment and link models the post model doesn t have an attribute called attached_ class s_set the post model doesn t have a related query accessor based on related_name attached_comment_set make sure restaurant and italianrestaurant have the right fields in the right order low level test for related_val higher level test for correct query values title foof not accidentally found 1 insert for each base 3 selects for the parents 1 update for the child updating a field of a model subclass doesn t issue an update query constrained by an inner query 10399 capture the expected query in a database agnostic way capture the queries executed when a subclassed model instance is saved creating a child with non abstract parents only issues inserts equality doesn t transfer in multitable inheritance parent fields can be used directly in filters on the child model filters against the parent model return objects of the parent s type since the parent and child are linked by an automatically created onetoonefield you can get from the parent to the child by using the child s name this won t work because the demon dogs restaurant is not an italian restaurant an italianrestaurant which does not exist is also a place which does not exist multipleobjectsreturned is also inherited related objects work just as they normally do this won t work because the place we select is not a restaurant it s a supplier the update command can update fields in parent and child classes at once although it executed multiple sql queries to do so the values command also works on fields from parent models select_related works with fields from the parent object as if they were a normal part of the model 23370 should be able to defer child fields when using select_related from parent to child the field was actually deferred model fields where assigned correct values update queries do not generate unnecessary queries 18304 refs 12567 refs 12567
from operator import attrgetter

from django.core.exceptions import FieldError, ValidationError
from django.db import connection, models
from django.db.models.query_utils import DeferredAttribute
from django.test import SimpleTestCase, TestCase
from django.test.utils import CaptureQueriesContext, isolate_apps

from .models import (
    Base,
    Chef,
    CommonChild,
    CommonInfo,
    CustomSupplier,
    GrandChild,
    GrandParent,
    ItalianRestaurant,
    ItalianRestaurantCommonParent,
    MixinModel,
    Parent,
    ParkingLot,
    Place,
    Post,
    Restaurant,
    Student,
    SubBase,
    Supplier,
    Title,
    Worker,
)


class ModelInheritanceTests(TestCase):
    def test_abstract(self):
        
        
        
        
        
        w1 = Worker.objects.create(name="Fred", age=35, job="Quarry worker")
        Worker.objects.create(name="Barney", age=34, job="Quarry worker")

        s = Student.objects.create(name="Pebbles", age=5, school_class="1B")

        self.assertEqual(str(w1), "Worker Fred")
        self.assertEqual(str(s), "Student Pebbles")

        
        
        self.assertSequenceEqual(
            Worker.objects.values("name"),
            [
                {"name": "Barney"},
                {"name": "Fred"},
            ],
        )

        
        
        
        self.assertEqual(Student._meta.ordering, [])

        
        
        with self.assertRaisesMessage(
            AttributeError, "'CommonInfo' has no attribute 'objects'"
        ):
            CommonInfo.objects.all()

    def test_reverse_relation_for_different_hierarchy_tree(self):
        
        
        
        self.assertSequenceEqual(Place.objects.filter(supplier__name="foo"), [])
        msg = (
            "Cannot resolve keyword 'supplier' into field. Choices are: "
            "address, chef, chef_id, id, italianrestaurant, lot, name, "
            "place_ptr, place_ptr_id, provider, rating, serves_hot_dogs, serves_pizza"
        )
        with self.assertRaisesMessage(FieldError, msg):
            Restaurant.objects.filter(supplier__name="foo")

    def test_model_with_distinct_accessors(self):
        
        post = Post.objects.create(title="Lorem Ipsum")
        post.attached_comment_set.create(content="Save $ on V1agr@", is_spam=True)
        post.attached_link_set.create(
            content="The web framework for perfections with deadlines.",
            url="http://www.djangoproject.com/",
        )

        
        
        msg = "'Post' object has no attribute 'attached_%(class)s_set'"
        with self.assertRaisesMessage(AttributeError, msg):
            getattr(post, "attached_%(class)s_set")

    def test_model_with_distinct_related_query_name(self):
        self.assertSequenceEqual(
            Post.objects.filter(attached_model_inheritance_comments__is_spam=True), []
        )

        
        
        msg = "Cannot resolve keyword 'attached_comment_set' into field."
        with self.assertRaisesMessage(FieldError, msg):
            Post.objects.filter(attached_comment_set__is_spam=True)

    def test_meta_fields_and_ordering(self):
        
        
        self.assertEqual(
            [f.name for f in Restaurant._meta.fields],
            [
                "id",
                "name",
                "address",
                "place_ptr",
                "rating",
                "serves_hot_dogs",
                "serves_pizza",
                "chef",
            ],
        )
        self.assertEqual(
            [f.name for f in ItalianRestaurant._meta.fields],
            [
                "id",
                "name",
                "address",
                "place_ptr",
                "rating",
                "serves_hot_dogs",
                "serves_pizza",
                "chef",
                "restaurant_ptr",
                "serves_gnocchi",
            ],
        )
        self.assertEqual(Restaurant._meta.ordering, ["-rating"])

    def test_custompk_m2m(self):
        b = Base.objects.create()
        b.titles.add(Title.objects.create(title="foof"))
        s = SubBase.objects.create(sub_id=b.id)
        b = Base.objects.get(pk=s.id)
        self.assertNotEqual(b.pk, s.pk)
        
        self.assertEqual(s.titles.related_val, (s.id,))
        
        
        self.assertSequenceEqual(s.titles.all(), [])

    def test_create_diamond_mti_default_pk(self):
        
        with self.assertNumQueries(4):
            common_child = CommonChild.objects.create()
        
        with self.assertNumQueries(4):
            common_child.save()

    def test_create_diamond_mti_common_parent(self):
        with self.assertNumQueries(4):
            italian_restaurant_child = ItalianRestaurantCommonParent.objects.create(
                name="Ristorante Miron",
                address="1234 W. Ash",
            )

        self.assertEqual(
            italian_restaurant_child.italianrestaurant_ptr.place_ptr,
            italian_restaurant_child.place_ptr_two,
        )
        self.assertEqual(
            italian_restaurant_child.italianrestaurant_ptr.restaurant_ptr,
            italian_restaurant_child.restaurant_ptr,
        )
        self.assertEqual(
            italian_restaurant_child.restaurant_ptr.place_ptr,
            italian_restaurant_child.place_ptr_two,
        )
        self.assertEqual(italian_restaurant_child.name, "Ristorante Miron")
        self.assertEqual(italian_restaurant_child.address, "1234 W. Ash")

    def test_update_parent_filtering(self):
        
        supplier = Supplier.objects.create(
            name="Central market",
            address="610 some street",
        )
        
        with CaptureQueriesContext(connection) as captured_queries:
            Place.objects.filter(pk=supplier.pk).update(name=supplier.name)
        expected_sql = captured_queries[0]["sql"]
        
        with CaptureQueriesContext(connection) as captured_queries:
            supplier.save(update_fields=("name",))
        for query in captured_queries:
            sql = query["sql"]
            if "UPDATE" in sql:
                self.assertEqual(expected_sql, sql)

    def test_create_child_no_update(self):
        

        def a():
            GrandChild.objects.create(
                email="grand_parent@example.com",
                first_name="grand",
                last_name="parent",
            )

        def b():
            GrandChild().save()

        for i, test in enumerate([a, b]):
            with self.subTest(i=i), self.assertNumQueries(4), CaptureQueriesContext(
                connection
            ) as queries:
                test()
                for query in queries:
                    sql = query["sql"]
                    self.assertIn("INSERT INTO", sql, sql)

    def test_create_copy_with_inherited_m2m(self):
        restaurant = Restaurant.objects.create()
        supplier = CustomSupplier.objects.create(
            name="Central market", address="944 W. Fullerton"
        )
        supplier.customers.set([restaurant])
        old_customers = supplier.customers.all()
        supplier.pk = None
        supplier.id = None
        supplier._state.adding = True
        supplier.save()
        supplier.customers.set(old_customers)
        supplier = Supplier.objects.get(pk=supplier.pk)
        self.assertCountEqual(supplier.customers.all(), old_customers)
        self.assertSequenceEqual(supplier.customers.all(), [restaurant])

    def test_eq(self):
        
        self.assertNotEqual(Place(id=1), Restaurant(id=1))
        self.assertNotEqual(Restaurant(id=1), Place(id=1))

    def test_mixin_init(self):
        m = MixinModel()
        self.assertEqual(m.other_attr, 1)

    @isolate_apps("model_inheritance")
    def test_abstract_parent_link(self):
        class A(models.Model):
            pass

        class B(A):
            a = models.OneToOneField("A", parent_link=True, on_delete=models.CASCADE)

            class Meta:
                abstract = True

        class C(B):
            pass

        self.assertIs(C._meta.parents[A], C._meta.get_field("a"))

    @isolate_apps("model_inheritance")
    def test_init_subclass(self):
        saved_kwargs = {}

        class A(models.Model):
            def __init_subclass__(cls, **kwargs):
                super().__init_subclass__()
                saved_kwargs.update(kwargs)

        kwargs = {"x": 1, "y": 2, "z": 3}

        class B(A, **kwargs):
            pass

        self.assertEqual(saved_kwargs, kwargs)

    @isolate_apps("model_inheritance")
    def test_set_name(self):
        class ClassAttr:
            called = None

            def __set_name__(self_, owner, name):
                self.assertIsNone(self_.called)
                self_.called = (owner, name)

        class A(models.Model):
            attr = ClassAttr()

        self.assertEqual(A.attr.called, (A, "attr"))

    def test_inherited_ordering_pk_desc(self):
        p1 = Parent.objects.create(first_name="Joe", email="joe@email.com")
        p2 = Parent.objects.create(first_name="Jon", email="jon@email.com")
        expected_order_by_sql = "ORDER BY %s.%s DESC" % (
            connection.ops.quote_name(Parent._meta.db_table),
            connection.ops.quote_name(Parent._meta.get_field("grandparent_ptr").column),
        )
        qs = Parent.objects.all()
        self.assertSequenceEqual(qs, [p2, p1])
        self.assertIn(expected_order_by_sql, str(qs.query))

    def test_queryset_class_getitem(self):
        self.assertIs(models.QuerySet[Post], models.QuerySet)
        self.assertIs(models.QuerySet[Post, Post], models.QuerySet)
        self.assertIs(models.QuerySet[Post, int, str], models.QuerySet)

    def test_shadow_parent_attribute_with_field(self):
        class ScalarParent(models.Model):
            foo = 1

        class ScalarOverride(ScalarParent):
            foo = models.IntegerField()

        self.assertEqual(type(ScalarOverride.foo), DeferredAttribute)

    def test_shadow_parent_property_with_field(self):
        class PropertyParent(models.Model):
            @property
            def foo(self):
                pass

        class PropertyOverride(PropertyParent):
            foo = models.IntegerField()

        self.assertEqual(type(PropertyOverride.foo), DeferredAttribute)

    def test_shadow_parent_method_with_field(self):
        class MethodParent(models.Model):
            def foo(self):
                pass

        class MethodOverride(MethodParent):
            foo = models.IntegerField()

        self.assertEqual(type(MethodOverride.foo), DeferredAttribute)


class ModelInheritanceDataTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.restaurant = Restaurant.objects.create(
            name="Demon Dogs",
            address="944 W. Fullerton",
            serves_hot_dogs=True,
            serves_pizza=False,
            rating=2,
        )

        chef = Chef.objects.create(name="Albert")
        cls.italian_restaurant = ItalianRestaurant.objects.create(
            name="Ristorante Miron",
            address="1234 W. Ash",
            serves_hot_dogs=False,
            serves_pizza=False,
            serves_gnocchi=True,
            rating=4,
            chef=chef,
        )

    def test_filter_inherited_model(self):
        self.assertQuerySetEqual(
            ItalianRestaurant.objects.filter(address="1234 W. Ash"),
            [
                "Ristorante Miron",
            ],
            attrgetter("name"),
        )

    def test_update_inherited_model(self):
        self.italian_restaurant.address = "1234 W. Elm"
        self.italian_restaurant.save()
        self.assertQuerySetEqual(
            ItalianRestaurant.objects.filter(address="1234 W. Elm"),
            [
                "Ristorante Miron",
            ],
            attrgetter("name"),
        )

    def test_parent_fields_available_for_filtering_in_child_model(self):
        
        self.assertQuerySetEqual(
            Restaurant.objects.filter(name="Demon Dogs"),
            [
                "Demon Dogs",
            ],
            attrgetter("name"),
        )
        self.assertQuerySetEqual(
            ItalianRestaurant.objects.filter(address="1234 W. Ash"),
            [
                "Ristorante Miron",
            ],
            attrgetter("name"),
        )

    def test_filter_on_parent_returns_object_of_parent_type(self):
        
        p = Place.objects.get(name="Demon Dogs")
        self.assertIs(type(p), Place)

    def test_parent_child_one_to_one_link(self):
        
        
        
        self.assertEqual(
            Place.objects.get(name="Demon Dogs").restaurant,
            Restaurant.objects.get(name="Demon Dogs"),
        )
        self.assertEqual(
            Place.objects.get(name="Ristorante Miron").restaurant.italianrestaurant,
            ItalianRestaurant.objects.get(name="Ristorante Miron"),
        )
        self.assertEqual(
            Restaurant.objects.get(name="Ristorante Miron").italianrestaurant,
            ItalianRestaurant.objects.get(name="Ristorante Miron"),
        )

    def test_parent_child_one_to_one_link_on_nonrelated_objects(self):
        
        
        with self.assertRaises(ItalianRestaurant.DoesNotExist):
            Place.objects.get(name="Demon Dogs").restaurant.italianrestaurant

    def test_inherited_does_not_exist_exception(self):
        
        
        with self.assertRaises(Place.DoesNotExist):
            ItalianRestaurant.objects.get(name="The Noodle Void")

    def test_inherited_multiple_objects_returned_exception(self):
        
        with self.assertRaises(Place.MultipleObjectsReturned):
            Restaurant.objects.get()

    def test_related_objects_for_inherited_models(self):
        
        s1 = Supplier.objects.create(name="Joe's Chickens", address="123 Sesame St")
        s1.customers.set([self.restaurant, self.italian_restaurant])
        s2 = Supplier.objects.create(name="Luigi's Pasta", address="456 Sesame St")
        s2.customers.set([self.italian_restaurant])

        
        
        p = Place.objects.get(name="Joe's Chickens")
        with self.assertRaises(Restaurant.DoesNotExist):
            p.restaurant

        self.assertEqual(p.supplier, s1)
        self.assertQuerySetEqual(
            self.italian_restaurant.provider.order_by("-name"),
            ["Luigi's Pasta", "Joe's Chickens"],
            attrgetter("name"),
        )
        self.assertQuerySetEqual(
            Restaurant.objects.filter(provider__name__contains="Chickens"),
            [
                "Ristorante Miron",
                "Demon Dogs",
            ],
            attrgetter("name"),
        )
        self.assertQuerySetEqual(
            ItalianRestaurant.objects.filter(provider__name__contains="Chickens"),
            [
                "Ristorante Miron",
            ],
            attrgetter("name"),
        )

        ParkingLot.objects.create(name="Main St", address="111 Main St", main_site=s1)
        ParkingLot.objects.create(
            name="Well Lit", address="124 Sesame St", main_site=self.italian_restaurant
        )

        self.assertEqual(
            Restaurant.objects.get(lot__name="Well Lit").name, "Ristorante Miron"
        )

    def test_update_works_on_parent_and_child_models_at_once(self):
        
        
        rows = Restaurant.objects.filter(
            serves_hot_dogs=True, name__contains="D"
        ).update(name="Demon Puppies", serves_hot_dogs=False)
        self.assertEqual(rows, 1)

        r1 = Restaurant.objects.get(pk=self.restaurant.pk)
        self.assertFalse(r1.serves_hot_dogs)
        self.assertEqual(r1.name, "Demon Puppies")

    def test_values_works_on_parent_model_fields(self):
        
        self.assertSequenceEqual(
            ItalianRestaurant.objects.values("name", "rating"),
            [
                {"rating": 4, "name": "Ristorante Miron"},
            ],
        )

    def test_select_related_works_on_parent_model_fields(self):
        
        
        self.assertNumQueries(2, lambda: ItalianRestaurant.objects.all()[0].chef)
        self.assertNumQueries(
            1, lambda: ItalianRestaurant.objects.select_related("chef")[0].chef
        )

    def test_select_related_defer(self):
        
        qs = (
            Restaurant.objects.select_related("italianrestaurant")
            .defer("italianrestaurant__serves_gnocchi")
            .order_by("rating")
        )

        
        with self.assertNumQueries(2):
            objs = list(qs.all())
            self.assertTrue(objs[1].italianrestaurant.serves_gnocchi)

        
        self.assertEqual(qs[0].name, "Demon Dogs")
        self.assertEqual(qs[0].rating, 2)
        self.assertEqual(qs[1].italianrestaurant.name, "Ristorante Miron")
        self.assertEqual(qs[1].italianrestaurant.rating, 4)

    def test_parent_cache_reuse(self):
        place = Place.objects.create()
        GrandChild.objects.create(place=place)
        grand_parent = GrandParent.objects.latest("pk")
        with self.assertNumQueries(1):
            self.assertEqual(grand_parent.place, place)
        parent = grand_parent.parent
        with self.assertNumQueries(0):
            self.assertEqual(parent.place, place)
        child = parent.child
        with self.assertNumQueries(0):
            self.assertEqual(child.place, place)
        grandchild = child.grandchild
        with self.assertNumQueries(0):
            self.assertEqual(grandchild.place, place)

    def test_update_query_counts(self):
        
        with self.assertNumQueries(3):
            self.italian_restaurant.save()

    def test_filter_inherited_on_null(self):
        
        Supplier.objects.create(
            name="Central market",
            address="610 some street",
        )
        self.assertQuerySetEqual(
            Place.objects.filter(supplier__isnull=False),
            [
                "Central market",
            ],
            attrgetter("name"),
        )
        self.assertQuerySetEqual(
            Place.objects.filter(supplier__isnull=True).order_by("name"),
            [
                "Demon Dogs",
                "Ristorante Miron",
            ],
            attrgetter("name"),
        )

    def test_exclude_inherited_on_null(self):
        
        Supplier.objects.create(
            name="Central market",
            address="610 some street",
        )
        self.assertQuerySetEqual(
            Place.objects.exclude(supplier__isnull=False).order_by("name"),
            [
                "Demon Dogs",
                "Ristorante Miron",
            ],
            attrgetter("name"),
        )
        self.assertQuerySetEqual(
            Place.objects.exclude(supplier__isnull=True),
            [
                "Central market",
            ],
            attrgetter("name"),
        )


@isolate_apps("model_inheritance", "model_inheritance.tests")
class InheritanceSameModelNameTests(SimpleTestCase):
    def test_abstract_fk_related_name(self):
        related_name = "%(app_label)s_%(class)s_references"

        class Referenced(models.Model):
            class Meta:
                app_label = "model_inheritance"

        class AbstractReferent(models.Model):
            reference = models.ForeignKey(
                Referenced, models.CASCADE, related_name=related_name
            )

            class Meta:
                app_label = "model_inheritance"
                abstract = True

        class Referent(AbstractReferent):
            class Meta:
                app_label = "model_inheritance"

        LocalReferent = Referent

        class Referent(AbstractReferent):
            class Meta:
                app_label = "tests"

        ForeignReferent = Referent

        self.assertFalse(hasattr(Referenced, related_name))
        self.assertIs(
            Referenced.model_inheritance_referent_references.field.model, LocalReferent
        )
        self.assertIs(Referenced.tests_referent_references.field.model, ForeignReferent)


class InheritanceUniqueTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.grand_parent = GrandParent.objects.create(
            email="grand_parent@example.com",
            first_name="grand",
            last_name="parent",
        )

    def test_unique(self):
        grand_child = GrandChild(
            email=self.grand_parent.email,
            first_name="grand",
            last_name="child",
        )
        msg = "Grand parent with this Email already exists."
        with self.assertRaisesMessage(ValidationError, msg):
            grand_child.validate_unique()

    def test_unique_together(self):
        grand_child = GrandChild(
            email="grand_child@example.com",
            first_name=self.grand_parent.first_name,
            last_name=self.grand_parent.last_name,
        )
        msg = "Grand parent with this First name and Last name already exists."
        with self.assertRaisesMessage(ValidationError, msg):
            grand_child.validate_unique()