regression tests for model inheritance behavior regression for 7350 7202 when you create a parent object with a specific reference to an existent child instance saving the parent doesn t duplicate the child this behavior is only activated during a raw save it is mostly relevant to deserialization but any sort of corba style narrow api would require a similar approach create a childparentgrandparent chain create a childparent chain with an explicit parent link no extra parent objects have been created you can also update objects when using a raw save no extra parent objects after an update either if you try to rawsave a parent attribute onto a child object the attribute will be ignored note that the name has not changed name is an attribute of place not italianrestaurant regressions tests for 7105 dates queries should be able to use fields from the parent model as easily as the child regression test for 7276 calling delete on a model with multitable inheritance should delete the associated rows from any ancestor tables as well as any descendent objects this should delete both restaurants plus the related places plus the italianrestaurant regression test for 6755 regression test for 11764 regression test for 7853 if the parent class has a selfreferential link make sure that any updates to that link via the child update the right table regression tests for 8076 getnextpreviousbydate should work regression test for 8825 and 9390 make sure all inherited fields esp m2m fields in this case appear on the child class ordering should not include any database column more than once this is most likely to occur naturally with model inheritance so we check it here regression test for 9390 this necessarily pokes at the sql string for the query since the duplicate problems are only apparent at that late stage regression test for 10362 it is possible to call update and only change a field in an ancestor model the connector from child to parent need not be the pk on the child the childparent link regression tests for 7588 all fields from an abc including those inherited nonabstractly should be available on child classes 7588 creating this instance should work without error manytomany relations defined on an abstract base class are correctly inherited and created on the child class a subclass of a subclass of an abstract model doesn t get its own accessor but it does inherit the m2m from its parent verbosenameplural correctly inherited from abc if inheritance chain includes an abstract model regression test for 11369 verbosenameplural should be inherited from an abc even when there are one or more intermediate abstract models in the inheritance chain for consistency with verbosename primary key set correctly with concreteabstractconcrete inheritance regression test for 13987 primary key is incorrectly determined when more than one model has a concreteabstractconcrete inheritance hierarchy a model which has different primary key for the parent model passes unique field checking correctly 17615 test for 17502 check that filtering through two levels of inheritance chain doesn t generate extra joins it would be nice but not too important to skip the middle join in this case skipping is possible as nothing from the middle model is used in the qs and top contains direct pointer to the bottom model regression test for 7246 the mismatch between restaurant and place is intentional 28175 no queries are made when accessing the parent objects one query is made when accessing the parent objects when the instance is deferred no queries are made when accessing the parent objects when the instance has deferred a field not present in the parent table changing the parent of a restaurant changes the restaurant s id pk changing the parent of an italian restaurant changes the restaurant s id pk create a new profile by setting pk none create a new congressman by setting pk none regression for 7350 7202 when you create a parent object with a specific reference to an existent child instance saving the parent doesn t duplicate the child this behavior is only activated during a raw save it is mostly relevant to deserialization but any sort of corba style narrow api would require a similar approach create a child parent grandparent chain create a child parent chain with an explicit parent link no extra parent objects have been created you can also update objects when using a raw save no extra parent objects after an update either if you try to raw_save a parent attribute onto a child object the attribute will be ignored note that the name has not changed name is an attribute of place not italianrestaurant regressions tests for 7105 dates queries should be able to use fields from the parent model as easily as the child regression test for 7276 calling delete on a model with multi table inheritance should delete the associated rows from any ancestor tables as well as any descendent objects this should delete both restaurants plus the related places plus the italianrestaurant regression test for 6755 regression test for 11764 regression test for 7853 if the parent class has a self referential link make sure that any updates to that link via the child update the right table regression tests for 8076 get_ next previous _by_date should work regression test for 8825 and 9390 make sure all inherited fields esp m2m fields in this case appear on the child class ordering should not include any database column more than once this is most likely to occur naturally with model inheritance so we check it here regression test for 9390 this necessarily pokes at the sql string for the query since the duplicate problems are only apparent at that late stage regression test for 10362 it is possible to call update and only change a field in an ancestor model the connector from child to parent need not be the pk on the child the child parent link regression tests for 7588 all fields from an abc including those inherited non abstractly should be available on child classes 7588 creating this instance should work without error many to many relations defined on an abstract base class are correctly inherited and created on the child class a subclass of a subclass of an abstract model doesn t get its own accessor but it does inherit the m2m from its parent verbose_name_plural correctly inherited from abc if inheritance chain includes an abstract model regression test for 11369 verbose_name_plural should be inherited from an abc even when there are one or more intermediate abstract models in the inheritance chain for consistency with verbose_name primary key set correctly with concrete abstract concrete inheritance regression test for 13987 primary key is incorrectly determined when more than one model has a concrete abstract concrete inheritance hierarchy a model which has different primary key for the parent model passes unique field checking correctly 17615 test for 17502 check that filtering through two levels of inheritance chain doesn t generate extra joins it would be nice but not too important to skip the middle join in this case skipping is possible as nothing from the middle model is used in the qs and top contains direct pointer to the bottom model regression test for 7246 the mismatch between restaurant and place is intentional 28175 no queries are made when accessing the parent objects one query is made when accessing the parent objects when the instance is deferred no queries are made when accessing the parent objects when the instance has deferred a field not present in the parent table changing the parent of a restaurant changes the restaurant s id pk changing the parent of an italian restaurant changes the restaurant s id pk create a new profile by setting pk none create a new congressman by setting pk none
import datetime
from operator import attrgetter
from unittest import expectedFailure

from django import forms
from django.test import TestCase

from .models import (
    ArticleWithAuthor,
    BachelorParty,
    BirthdayParty,
    BusStation,
    Child,
    Congressman,
    DerivedM,
    InternalCertificationAudit,
    ItalianRestaurant,
    M2MChild,
    MessyBachelorParty,
    ParkingLot,
    ParkingLot3,
    ParkingLot4A,
    ParkingLot4B,
    Person,
    Place,
    Politician,
    Profile,
    QualityControl,
    Restaurant,
    SelfRefChild,
    SelfRefParent,
    Senator,
    Supplier,
    TrainStation,
    User,
    Wholesaler,
)


class ModelInheritanceTest(TestCase):
    def test_model_inheritance(self):
        
        
        
        
        
        

        
        place1 = Place(name="Guido's House of Pasta", address="944 W. Fullerton")
        place1.save_base(raw=True)
        restaurant = Restaurant(
            place_ptr=place1,
            serves_hot_dogs=True,
            serves_pizza=False,
        )
        restaurant.save_base(raw=True)
        italian_restaurant = ItalianRestaurant(
            restaurant_ptr=restaurant, serves_gnocchi=True
        )
        italian_restaurant.save_base(raw=True)

        
        place2 = Place(name="Main St", address="111 Main St")
        place2.save_base(raw=True)
        park = ParkingLot(parent=place2, capacity=100)
        park.save_base(raw=True)

        
        places = list(Place.objects.all())
        self.assertEqual(places, [place1, place2])

        dicts = list(Restaurant.objects.values("name", "serves_hot_dogs"))
        self.assertEqual(
            dicts, [{"name": "Guido's House of Pasta", "serves_hot_dogs": True}]
        )

        dicts = list(
            ItalianRestaurant.objects.values(
                "name", "serves_hot_dogs", "serves_gnocchi"
            )
        )
        self.assertEqual(
            dicts,
            [
                {
                    "name": "Guido's House of Pasta",
                    "serves_gnocchi": True,
                    "serves_hot_dogs": True,
                }
            ],
        )

        dicts = list(ParkingLot.objects.values("name", "capacity"))
        self.assertEqual(
            dicts,
            [
                {
                    "capacity": 100,
                    "name": "Main St",
                }
            ],
        )

        
        place1.name = "Guido's All New House of Pasta"
        place1.save_base(raw=True)

        restaurant.serves_hot_dogs = False
        restaurant.save_base(raw=True)

        italian_restaurant.serves_gnocchi = False
        italian_restaurant.save_base(raw=True)

        place2.name = "Derelict lot"
        place2.save_base(raw=True)

        park.capacity = 50
        park.save_base(raw=True)

        
        places = list(Place.objects.all())
        self.assertEqual(places, [place2, place1])
        self.assertEqual(places[0].name, "Derelict lot")
        self.assertEqual(places[1].name, "Guido's All New House of Pasta")

        dicts = list(Restaurant.objects.values("name", "serves_hot_dogs"))
        self.assertEqual(
            dicts,
            [
                {
                    "name": "Guido's All New House of Pasta",
                    "serves_hot_dogs": False,
                }
            ],
        )

        dicts = list(
            ItalianRestaurant.objects.values(
                "name", "serves_hot_dogs", "serves_gnocchi"
            )
        )
        self.assertEqual(
            dicts,
            [
                {
                    "name": "Guido's All New House of Pasta",
                    "serves_gnocchi": False,
                    "serves_hot_dogs": False,
                }
            ],
        )

        dicts = list(ParkingLot.objects.values("name", "capacity"))
        self.assertEqual(
            dicts,
            [
                {
                    "capacity": 50,
                    "name": "Derelict lot",
                }
            ],
        )

        
        

        italian_restaurant.name = "Lorenzo's Pasta Hut"
        italian_restaurant.save_base(raw=True)

        
        
        dicts = list(
            ItalianRestaurant.objects.values(
                "name", "serves_hot_dogs", "serves_gnocchi"
            )
        )
        self.assertEqual(
            dicts,
            [
                {
                    "name": "Guido's All New House of Pasta",
                    "serves_gnocchi": False,
                    "serves_hot_dogs": False,
                }
            ],
        )

    def test_issue_7105(self):
        
        
        Child.objects.create(
            name="child", created=datetime.datetime(2008, 6, 26, 17, 0, 0)
        )
        datetimes = list(Child.objects.datetimes("created", "month"))
        self.assertEqual(datetimes, [datetime.datetime(2008, 6, 1, 0, 0)])

    def test_issue_7276(self):
        
        
        
        place1 = Place(name="Guido's House of Pasta", address="944 W. Fullerton")
        place1.save_base(raw=True)
        restaurant = Restaurant(
            place_ptr=place1,
            serves_hot_dogs=True,
            serves_pizza=False,
        )
        restaurant.save_base(raw=True)
        italian_restaurant = ItalianRestaurant(
            restaurant_ptr=restaurant, serves_gnocchi=True
        )
        italian_restaurant.save_base(raw=True)

        ident = ItalianRestaurant.objects.all()[0].id
        self.assertEqual(Place.objects.get(pk=ident), place1)
        Restaurant.objects.create(
            name="a",
            address="xx",
            serves_hot_dogs=True,
            serves_pizza=False,
        )

        
        
        Restaurant.objects.all().delete()

        with self.assertRaises(Place.DoesNotExist):
            Place.objects.get(pk=ident)
        with self.assertRaises(ItalianRestaurant.DoesNotExist):
            ItalianRestaurant.objects.get(pk=ident)

    def test_issue_6755(self):
        
        r = Restaurant(serves_pizza=False, serves_hot_dogs=False)
        r.save()
        self.assertEqual(r.id, r.place_ptr_id)
        orig_id = r.id
        r = Restaurant(place_ptr_id=orig_id, serves_pizza=True, serves_hot_dogs=False)
        r.save()
        self.assertEqual(r.id, orig_id)
        self.assertEqual(r.id, r.place_ptr_id)

    def test_issue_11764(self):
        
        wholesalers = list(Wholesaler.objects.select_related())
        self.assertEqual(wholesalers, [])

    def test_issue_7853(self):
        
        obj = SelfRefChild.objects.create(child_data=37, parent_data=42)
        obj.delete()

    def test_get_next_previous_by_date(self):
        
        c1 = ArticleWithAuthor(
            headline="ArticleWithAuthor 1",
            author="Person 1",
            pub_date=datetime.datetime(2005, 8, 1, 3, 0),
        )
        c1.save()
        c2 = ArticleWithAuthor(
            headline="ArticleWithAuthor 2",
            author="Person 2",
            pub_date=datetime.datetime(2005, 8, 1, 10, 0),
        )
        c2.save()
        c3 = ArticleWithAuthor(
            headline="ArticleWithAuthor 3",
            author="Person 3",
            pub_date=datetime.datetime(2005, 8, 2),
        )
        c3.save()

        self.assertEqual(c1.get_next_by_pub_date(), c2)
        self.assertEqual(c2.get_next_by_pub_date(), c3)
        with self.assertRaises(ArticleWithAuthor.DoesNotExist):
            c3.get_next_by_pub_date()
        self.assertEqual(c3.get_previous_by_pub_date(), c2)
        self.assertEqual(c2.get_previous_by_pub_date(), c1)
        with self.assertRaises(ArticleWithAuthor.DoesNotExist):
            c1.get_previous_by_pub_date()

    def test_inherited_fields(self):
        
        m2mchildren = list(M2MChild.objects.filter(articles__isnull=False))
        self.assertEqual(m2mchildren, [])

        
        
        
        
        
        qs = ArticleWithAuthor.objects.order_by("pub_date", "pk")
        sql = qs.query.get_compiler(qs.db).as_sql()[0]
        fragment = sql[sql.find("ORDER BY") :]
        pos = fragment.find("pub_date")
        self.assertEqual(fragment.find("pub_date", pos + 1), -1)

    def test_queryset_update_on_parent_model(self):
        
        article = ArticleWithAuthor.objects.create(
            author="fred",
            headline="Hey there!",
            pub_date=datetime.datetime(2009, 3, 1, 8, 0, 0),
        )
        update = ArticleWithAuthor.objects.filter(author="fred").update(
            headline="Oh, no!"
        )
        self.assertEqual(update, 1)
        update = ArticleWithAuthor.objects.filter(pk=article.pk).update(
            headline="Oh, no!"
        )
        self.assertEqual(update, 1)

        derivedm1 = DerivedM.objects.create(
            customPK=44,
            base_name="b1",
            derived_name="d1",
        )
        self.assertEqual(derivedm1.customPK, 44)
        self.assertEqual(derivedm1.base_name, "b1")
        self.assertEqual(derivedm1.derived_name, "d1")
        derivedms = list(DerivedM.objects.all())
        self.assertEqual(derivedms, [derivedm1])

    def test_use_explicit_o2o_to_parent_as_pk(self):
        
        self.assertEqual(ParkingLot3._meta.pk.name, "primary_key")
        
        self.assertEqual(ParkingLot3._meta.get_ancestor_link(Place).name, "parent")

    def test_use_explicit_o2o_to_parent_from_abstract_model(self):
        self.assertEqual(ParkingLot4A._meta.pk.name, "parent")
        ParkingLot4A.objects.create(
            name="Parking4A",
            address="21 Jump Street",
        )

        self.assertEqual(ParkingLot4B._meta.pk.name, "parent")
        ParkingLot4A.objects.create(
            name="Parking4B",
            address="21 Jump Street",
        )

    def test_all_fields_from_abstract_base_class(self):
        
        
        
        
        QualityControl.objects.create(
            headline="Problems in Django",
            pub_date=datetime.datetime.now(),
            quality=10,
            assignee="adrian",
        )

    def test_abstract_base_class_m2m_relation_inheritance(self):
        
        
        p1 = Person.objects.create(name="Alice")
        p2 = Person.objects.create(name="Bob")
        p3 = Person.objects.create(name="Carol")
        p4 = Person.objects.create(name="Dave")

        birthday = BirthdayParty.objects.create(name="Birthday party for Alice")
        birthday.attendees.set([p1, p3])

        bachelor = BachelorParty.objects.create(name="Bachelor party for Bob")
        bachelor.attendees.set([p2, p4])

        parties = list(p1.birthdayparty_set.all())
        self.assertEqual(parties, [birthday])

        parties = list(p1.bachelorparty_set.all())
        self.assertEqual(parties, [])

        parties = list(p2.bachelorparty_set.all())
        self.assertEqual(parties, [bachelor])

        
        
        self.assertFalse(hasattr(p2, "messybachelorparty_set"))

        
        messy = MessyBachelorParty.objects.create(name="Bachelor party for Dave")
        messy.attendees.set([p4])
        messy_parent = messy.bachelorparty_ptr

        parties = list(p4.bachelorparty_set.all())
        self.assertEqual(parties, [bachelor, messy_parent])

    def test_abstract_verbose_name_plural_inheritance(self):
        
        
        
        
        
        self.assertEqual(InternalCertificationAudit._meta.verbose_name_plural, "Audits")

    def test_inherited_nullable_exclude(self):
        obj = SelfRefChild.objects.create(child_data=37, parent_data=42)
        self.assertQuerySetEqual(
            SelfRefParent.objects.exclude(self_data=72), [obj.pk], attrgetter("pk")
        )
        self.assertQuerySetEqual(
            SelfRefChild.objects.exclude(self_data=72), [obj.pk], attrgetter("pk")
        )

    def test_concrete_abstract_concrete_pk(self):
        
        
        
        
        self.assertEqual(
            len(
                [field for field in BusStation._meta.local_fields if field.primary_key]
            ),
            1,
        )
        self.assertEqual(
            len(
                [
                    field
                    for field in TrainStation._meta.local_fields
                    if field.primary_key
                ]
            ),
            1,
        )
        self.assertIs(BusStation._meta.pk.model, BusStation)
        self.assertIs(TrainStation._meta.pk.model, TrainStation)

    def test_inherited_unique_field_with_form(self):
        

        class ProfileForm(forms.ModelForm):
            class Meta:
                model = Profile
                fields = "__all__"

        User.objects.create(username="user_only")
        p = Profile.objects.create(username="user_with_profile")
        form = ProfileForm(
            {"username": "user_with_profile", "extra": "hello"}, instance=p
        )
        self.assertTrue(form.is_valid())

    def test_inheritance_joins(self):
        
        
        qs = ItalianRestaurant.objects.all()
        self.assertEqual(str(qs.query).count("JOIN"), 2)
        qs = ItalianRestaurant.objects.filter(name="foo")
        self.assertEqual(str(qs.query).count("JOIN"), 2)

    @expectedFailure
    def test_inheritance_values_joins(self):
        
        
        
        qs = ItalianRestaurant.objects.values_list("serves_gnocchi").filter(name="foo")
        self.assertEqual(str(qs.query).count("JOIN"), 1)

    def test_issue_21554(self):
        senator = Senator.objects.create(name="John Doe", title="X", state="Y")
        senator = Senator.objects.get(pk=senator.pk)
        self.assertEqual(senator.name, "John Doe")
        self.assertEqual(senator.title, "X")
        self.assertEqual(senator.state, "Y")

    def test_inheritance_resolve_columns(self):
        Restaurant.objects.create(
            name="Bobs Cafe",
            address="Somewhere",
            serves_pizza=True,
            serves_hot_dogs=True,
        )
        p = Place.objects.select_related("restaurant")[0]
        self.assertIsInstance(p.restaurant.serves_pizza, bool)

    def test_inheritance_select_related(self):
        
        r1 = Restaurant.objects.create(
            name="Nobu", serves_hot_dogs=True, serves_pizza=False
        )
        r2 = Restaurant.objects.create(
            name="Craft", serves_hot_dogs=False, serves_pizza=True
        )
        Supplier.objects.create(name="John", restaurant=r1)
        Supplier.objects.create(name="Jane", restaurant=r2)

        self.assertQuerySetEqual(
            Supplier.objects.order_by("name").select_related(),
            [
                "Jane",
                "John",
            ],
            attrgetter("name"),
        )

        jane = Supplier.objects.order_by("name").select_related("restaurant")[0]
        self.assertEqual(jane.restaurant.name, "Craft")

    def test_filter_with_parent_fk(self):
        r = Restaurant.objects.create()
        s = Supplier.objects.create(restaurant=r)
        
        self.assertSequenceEqual(
            Supplier.objects.filter(restaurant__in=Place.objects.all()), [s]
        )

    def test_ptr_accessor_assigns_state(self):
        r = Restaurant.objects.create()
        self.assertIs(r.place_ptr._state.adding, False)
        self.assertEqual(r.place_ptr._state.db, "default")

    def test_related_filtering_query_efficiency_ticket_15844(self):
        r = Restaurant.objects.create(
            name="Guido's House of Pasta",
            address="944 W. Fullerton",
            serves_hot_dogs=True,
            serves_pizza=False,
        )
        s = Supplier.objects.create(restaurant=r)
        with self.assertNumQueries(1):
            self.assertSequenceEqual(Supplier.objects.filter(restaurant=r), [s])
        with self.assertNumQueries(1):
            self.assertSequenceEqual(r.supplier_set.all(), [s])

    def test_queries_on_parent_access(self):
        italian_restaurant = ItalianRestaurant.objects.create(
            name="Guido's House of Pasta",
            address="944 W. Fullerton",
            serves_hot_dogs=True,
            serves_pizza=False,
            serves_gnocchi=True,
        )

        
        italian_restaurant = ItalianRestaurant.objects.get(pk=italian_restaurant.pk)
        with self.assertNumQueries(0):
            restaurant = italian_restaurant.restaurant_ptr
            self.assertEqual(restaurant.place_ptr.restaurant, restaurant)
            self.assertEqual(restaurant.italianrestaurant, italian_restaurant)

        
        
        italian_restaurant = ItalianRestaurant.objects.only("serves_gnocchi").get(
            pk=italian_restaurant.pk
        )
        with self.assertNumQueries(1):
            restaurant = italian_restaurant.restaurant_ptr
            self.assertEqual(restaurant.place_ptr.restaurant, restaurant)
            self.assertEqual(restaurant.italianrestaurant, italian_restaurant)

        
        
        italian_restaurant = ItalianRestaurant.objects.defer("serves_gnocchi").get(
            pk=italian_restaurant.pk
        )
        with self.assertNumQueries(0):
            restaurant = italian_restaurant.restaurant_ptr
            self.assertEqual(restaurant.place_ptr.restaurant, restaurant)
            self.assertEqual(restaurant.italianrestaurant, italian_restaurant)

    def test_id_field_update_on_ancestor_change(self):
        place1 = Place.objects.create(name="House of Pasta", address="944 Fullerton")
        place2 = Place.objects.create(name="House of Pizza", address="954 Fullerton")
        place3 = Place.objects.create(name="Burger house", address="964 Fullerton")
        restaurant1 = Restaurant.objects.create(
            place_ptr=place1,
            serves_hot_dogs=True,
            serves_pizza=False,
        )
        restaurant2 = Restaurant.objects.create(
            place_ptr=place2,
            serves_hot_dogs=True,
            serves_pizza=False,
        )

        italian_restaurant = ItalianRestaurant.objects.create(
            restaurant_ptr=restaurant1,
            serves_gnocchi=True,
        )
        
        restaurant1.place_ptr = place3
        self.assertEqual(restaurant1.pk, place3.pk)
        self.assertEqual(restaurant1.id, place3.id)
        self.assertEqual(restaurant1.pk, restaurant1.id)
        restaurant1.place_ptr = None
        self.assertIsNone(restaurant1.pk)
        self.assertIsNone(restaurant1.id)
        
        
        italian_restaurant.restaurant_ptr = restaurant2
        self.assertEqual(italian_restaurant.pk, restaurant2.pk)
        self.assertEqual(italian_restaurant.id, restaurant2.id)
        self.assertEqual(italian_restaurant.pk, italian_restaurant.id)
        italian_restaurant.restaurant_ptr = None
        self.assertIsNone(italian_restaurant.pk)
        self.assertIsNone(italian_restaurant.id)

    def test_create_new_instance_with_pk_equals_none(self):
        p1 = Profile.objects.create(username="john")
        p2 = User.objects.get(pk=p1.user_ptr_id).profile
        
        p2.pk = None
        p2.user_ptr_id = None
        p2.username = "bill"
        p2.save()
        self.assertEqual(Profile.objects.count(), 2)
        self.assertEqual(User.objects.get(pk=p1.user_ptr_id).username, "john")

    def test_create_new_instance_with_pk_equals_none_multi_inheritance(self):
        c1 = Congressman.objects.create(state="PA", name="John", title="senator 1")
        c2 = Person.objects.get(pk=c1.pk).congressman
        
        c2.pk = None
        c2.id = None
        c2.politician_ptr_id = None
        c2.name = "Bill"
        c2.title = "senator 2"
        c2.save()
        self.assertEqual(Congressman.objects.count(), 2)
        self.assertEqual(Person.objects.get(pk=c1.pk).name, "John")
        self.assertEqual(
            Politician.objects.get(pk=c1.politician_ptr_id).title,
            "senator 1",
        )

    def test_mti_update_parent_through_child(self):
        Politician.objects.create()
        Congressman.objects.create()
        Congressman.objects.update(title="senator 1")
        self.assertEqual(Congressman.objects.get().title, "senator 1")

    def test_mti_update_grand_parent_through_child(self):
        Politician.objects.create()
        Senator.objects.create()
        Senator.objects.update(title="senator 1")
        self.assertEqual(Senator.objects.get().title, "senator 1")