parts/django/tests/regressiontests/model_inheritance_regress/tests.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 """
       
     2 Regression tests for Model inheritance behaviour.
       
     3 """
       
     4 
       
     5 import datetime
       
     6 from operator import attrgetter
       
     7 
       
     8 from django.test import TestCase
       
     9 
       
    10 from models import (Place, Restaurant, ItalianRestaurant, ParkingLot,
       
    11     ParkingLot2, ParkingLot3, Supplier, Wholesaler, Child, SelfRefParent,
       
    12     SelfRefChild, ArticleWithAuthor, M2MChild, QualityControl, DerivedM,
       
    13     Person, BirthdayParty, BachelorParty, MessyBachelorParty,
       
    14     InternalCertificationAudit)
       
    15 
       
    16 
       
    17 class ModelInheritanceTest(TestCase):
       
    18     def test_model_inheritance(self):
       
    19         # Regression for #7350, #7202
       
    20         # Check that when you create a Parent object with a specific reference
       
    21         # to an existent child instance, saving the Parent doesn't duplicate
       
    22         # the child. This behaviour is only activated during a raw save - it
       
    23         # is mostly relevant to deserialization, but any sort of CORBA style
       
    24         # 'narrow()' API would require a similar approach.
       
    25 
       
    26         # Create a child-parent-grandparent chain
       
    27         place1 = Place(
       
    28             name="Guido's House of Pasta",
       
    29             address='944 W. Fullerton')
       
    30         place1.save_base(raw=True)
       
    31         restaurant = Restaurant(
       
    32             place_ptr=place1,
       
    33             serves_hot_dogs=True,
       
    34             serves_pizza=False)
       
    35         restaurant.save_base(raw=True)
       
    36         italian_restaurant = ItalianRestaurant(
       
    37             restaurant_ptr=restaurant,
       
    38             serves_gnocchi=True)
       
    39         italian_restaurant.save_base(raw=True)
       
    40 
       
    41         # Create a child-parent chain with an explicit parent link
       
    42         place2 = Place(name='Main St', address='111 Main St')
       
    43         place2.save_base(raw=True)
       
    44         park = ParkingLot(parent=place2, capacity=100)
       
    45         park.save_base(raw=True)
       
    46 
       
    47         # Check that no extra parent objects have been created.
       
    48         places = list(Place.objects.all())
       
    49         self.assertEqual(places, [place1, place2])
       
    50 
       
    51         dicts = list(Restaurant.objects.values('name','serves_hot_dogs'))
       
    52         self.assertEqual(dicts, [{
       
    53             'name': u"Guido's House of Pasta",
       
    54             'serves_hot_dogs': True
       
    55         }])
       
    56 
       
    57         dicts = list(ItalianRestaurant.objects.values(
       
    58             'name','serves_hot_dogs','serves_gnocchi'))
       
    59         self.assertEqual(dicts, [{
       
    60             'name': u"Guido's House of Pasta",
       
    61             'serves_gnocchi': True,
       
    62             'serves_hot_dogs': True,
       
    63         }])
       
    64 
       
    65         dicts = list(ParkingLot.objects.values('name','capacity'))
       
    66         self.assertEqual(dicts, [{
       
    67             'capacity': 100,
       
    68             'name': u'Main St',
       
    69         }])
       
    70 
       
    71         # You can also update objects when using a raw save.
       
    72         place1.name = "Guido's All New House of Pasta"
       
    73         place1.save_base(raw=True)
       
    74 
       
    75         restaurant.serves_hot_dogs = False
       
    76         restaurant.save_base(raw=True)
       
    77 
       
    78         italian_restaurant.serves_gnocchi = False
       
    79         italian_restaurant.save_base(raw=True)
       
    80 
       
    81         place2.name='Derelict lot'
       
    82         place2.save_base(raw=True)
       
    83 
       
    84         park.capacity = 50
       
    85         park.save_base(raw=True)
       
    86 
       
    87         # No extra parent objects after an update, either.
       
    88         places = list(Place.objects.all())
       
    89         self.assertEqual(places, [place2, place1])
       
    90         self.assertEqual(places[0].name, 'Derelict lot')
       
    91         self.assertEqual(places[1].name, "Guido's All New House of Pasta")
       
    92 
       
    93         dicts = list(Restaurant.objects.values('name','serves_hot_dogs'))
       
    94         self.assertEqual(dicts, [{
       
    95             'name': u"Guido's All New House of Pasta",
       
    96             'serves_hot_dogs': False,
       
    97         }])
       
    98 
       
    99         dicts = list(ItalianRestaurant.objects.values(
       
   100             'name', 'serves_hot_dogs', 'serves_gnocchi'))
       
   101         self.assertEqual(dicts, [{
       
   102             'name': u"Guido's All New House of Pasta",
       
   103             'serves_gnocchi': False,
       
   104             'serves_hot_dogs': False,
       
   105         }])
       
   106 
       
   107         dicts = list(ParkingLot.objects.values('name','capacity'))
       
   108         self.assertEqual(dicts, [{
       
   109             'capacity': 50,
       
   110             'name': u'Derelict lot',
       
   111         }])
       
   112 
       
   113         # If you try to raw_save a parent attribute onto a child object,
       
   114         # the attribute will be ignored.
       
   115 
       
   116         italian_restaurant.name = "Lorenzo's Pasta Hut"
       
   117         italian_restaurant.save_base(raw=True)
       
   118 
       
   119         # Note that the name has not changed
       
   120         # - name is an attribute of Place, not ItalianRestaurant
       
   121         dicts = list(ItalianRestaurant.objects.values(
       
   122             'name','serves_hot_dogs','serves_gnocchi'))
       
   123         self.assertEqual(dicts, [{
       
   124             'name': u"Guido's All New House of Pasta",
       
   125             'serves_gnocchi': False,
       
   126             'serves_hot_dogs': False,
       
   127         }])
       
   128 
       
   129     def test_issue_7105(self):
       
   130         # Regressions tests for #7105: dates() queries should be able to use
       
   131         # fields from the parent model as easily as the child.
       
   132         obj = Child.objects.create(
       
   133             name='child',
       
   134             created=datetime.datetime(2008, 6, 26, 17, 0, 0))
       
   135         dates = list(Child.objects.dates('created', 'month'))
       
   136         self.assertEqual(dates, [datetime.datetime(2008, 6, 1, 0, 0)])
       
   137 
       
   138     def test_issue_7276(self):
       
   139         # Regression test for #7276: calling delete() on a model with
       
   140         # multi-table inheritance should delete the associated rows from any
       
   141         # ancestor tables, as well as any descendent objects.
       
   142         place1 = Place(
       
   143             name="Guido's House of Pasta",
       
   144             address='944 W. Fullerton')
       
   145         place1.save_base(raw=True)
       
   146         restaurant = Restaurant(
       
   147             place_ptr=place1,
       
   148             serves_hot_dogs=True,
       
   149             serves_pizza=False)
       
   150         restaurant.save_base(raw=True)
       
   151         italian_restaurant = ItalianRestaurant(
       
   152             restaurant_ptr=restaurant,
       
   153             serves_gnocchi=True)
       
   154         italian_restaurant.save_base(raw=True)
       
   155 
       
   156         ident = ItalianRestaurant.objects.all()[0].id
       
   157         self.assertEqual(Place.objects.get(pk=ident), place1)
       
   158         xx = Restaurant.objects.create(
       
   159             name='a',
       
   160             address='xx',
       
   161             serves_hot_dogs=True,
       
   162             serves_pizza=False)
       
   163 
       
   164         # This should delete both Restuarants, plus the related places, plus
       
   165         # the ItalianRestaurant.
       
   166         Restaurant.objects.all().delete()
       
   167 
       
   168         self.assertRaises(
       
   169             Place.DoesNotExist,
       
   170             Place.objects.get,
       
   171             pk=ident)
       
   172         self.assertRaises(
       
   173             ItalianRestaurant.DoesNotExist,
       
   174             ItalianRestaurant.objects.get,
       
   175             pk=ident)
       
   176 
       
   177     def test_issue_6755(self):
       
   178         """
       
   179         Regression test for #6755
       
   180         """
       
   181         r = Restaurant(serves_pizza=False)
       
   182         r.save()
       
   183         self.assertEqual(r.id, r.place_ptr_id)
       
   184         orig_id = r.id
       
   185         r = Restaurant(place_ptr_id=orig_id, serves_pizza=True)
       
   186         r.save()
       
   187         self.assertEqual(r.id, orig_id)
       
   188         self.assertEqual(r.id, r.place_ptr_id)
       
   189 
       
   190     def test_issue_7488(self):
       
   191         # Regression test for #7488. This looks a little crazy, but it's the
       
   192         # equivalent of what the admin interface has to do for the edit-inline
       
   193         # case.
       
   194         suppliers = Supplier.objects.filter(
       
   195             restaurant=Restaurant(name='xx', address='yy'))
       
   196         suppliers = list(suppliers)
       
   197         self.assertEqual(suppliers, [])
       
   198 
       
   199     def test_issue_11764(self):
       
   200         """
       
   201         Regression test for #11764
       
   202         """
       
   203         wholesalers = list(Wholesaler.objects.all().select_related())
       
   204         self.assertEqual(wholesalers, [])
       
   205 
       
   206     def test_issue_7853(self):
       
   207         """
       
   208         Regression test for #7853
       
   209         If the parent class has a self-referential link, make sure that any
       
   210         updates to that link via the child update the right table.
       
   211         """
       
   212         obj = SelfRefChild.objects.create(child_data=37, parent_data=42)
       
   213         obj.delete()
       
   214 
       
   215     def test_get_next_previous_by_date(self):
       
   216         """
       
   217         Regression tests for #8076
       
   218         get_(next/previous)_by_date should work
       
   219         """
       
   220         c1 = ArticleWithAuthor(
       
   221             headline='ArticleWithAuthor 1',
       
   222             author="Person 1",
       
   223             pub_date=datetime.datetime(2005, 8, 1, 3, 0))
       
   224         c1.save()
       
   225         c2 = ArticleWithAuthor(
       
   226             headline='ArticleWithAuthor 2',
       
   227             author="Person 2",
       
   228             pub_date=datetime.datetime(2005, 8, 1, 10, 0))
       
   229         c2.save()
       
   230         c3 = ArticleWithAuthor(
       
   231             headline='ArticleWithAuthor 3',
       
   232             author="Person 3",
       
   233             pub_date=datetime.datetime(2005, 8, 2))
       
   234         c3.save()
       
   235 
       
   236         self.assertEqual(c1.get_next_by_pub_date(), c2)
       
   237         self.assertEqual(c2.get_next_by_pub_date(), c3)
       
   238         self.assertRaises(
       
   239             ArticleWithAuthor.DoesNotExist,
       
   240             c3.get_next_by_pub_date)
       
   241         self.assertEqual(c3.get_previous_by_pub_date(), c2)
       
   242         self.assertEqual(c2.get_previous_by_pub_date(), c1)
       
   243         self.assertRaises(
       
   244             ArticleWithAuthor.DoesNotExist,
       
   245             c1.get_previous_by_pub_date)
       
   246 
       
   247     def test_inherited_fields(self):
       
   248         """
       
   249         Regression test for #8825 and #9390
       
   250         Make sure all inherited fields (esp. m2m fields, in this case) appear
       
   251         on the child class.
       
   252         """
       
   253         m2mchildren = list(M2MChild.objects.filter(articles__isnull=False))
       
   254         self.assertEqual(m2mchildren, [])
       
   255 
       
   256         # Ordering should not include any database column more than once (this
       
   257         # is most likely to ocurr naturally with model inheritance, so we
       
   258         # check it here). Regression test for #9390. This necessarily pokes at
       
   259         # the SQL string for the query, since the duplicate problems are only
       
   260         # apparent at that late stage.
       
   261         qs = ArticleWithAuthor.objects.order_by('pub_date', 'pk')
       
   262         sql = qs.query.get_compiler(qs.db).as_sql()[0]
       
   263         fragment = sql[sql.find('ORDER BY'):]
       
   264         pos = fragment.find('pub_date')
       
   265         self.assertEqual(fragment.find('pub_date', pos + 1), -1)
       
   266 
       
   267     def test_queryset_update_on_parent_model(self):
       
   268         """
       
   269         Regression test for #10362
       
   270         It is possible to call update() and only change a field in
       
   271         an ancestor model.
       
   272         """
       
   273         article = ArticleWithAuthor.objects.create(
       
   274             author="fred",
       
   275             headline="Hey there!",
       
   276             pub_date=datetime.datetime(2009, 3, 1, 8, 0, 0))
       
   277         update = ArticleWithAuthor.objects.filter(
       
   278             author="fred").update(headline="Oh, no!")
       
   279         self.assertEqual(update, 1)
       
   280         update = ArticleWithAuthor.objects.filter(
       
   281             pk=article.pk).update(headline="Oh, no!")
       
   282         self.assertEqual(update, 1)
       
   283 
       
   284         derivedm1 = DerivedM.objects.create(
       
   285             customPK=44,
       
   286             base_name="b1",
       
   287             derived_name="d1")
       
   288         self.assertEqual(derivedm1.customPK, 44)
       
   289         self.assertEqual(derivedm1.base_name, 'b1')
       
   290         self.assertEqual(derivedm1.derived_name, 'd1')
       
   291         derivedms = list(DerivedM.objects.all())
       
   292         self.assertEqual(derivedms, [derivedm1])
       
   293 
       
   294     def test_use_explicit_o2o_to_parent_as_pk(self):
       
   295         """
       
   296         Regression tests for #10406
       
   297         If there's a one-to-one link between a child model and the parent and
       
   298         no explicit pk declared, we can use the one-to-one link as the pk on
       
   299         the child.
       
   300         """
       
   301         self.assertEqual(ParkingLot2._meta.pk.name, "parent")
       
   302 
       
   303         # However, the connector from child to parent need not be the pk on
       
   304         # the child at all.
       
   305         self.assertEqual(ParkingLot3._meta.pk.name, "primary_key")
       
   306         # the child->parent link
       
   307         self.assertEqual(
       
   308             ParkingLot3._meta.get_ancestor_link(Place).name,
       
   309             "parent")
       
   310 
       
   311     def test_all_fields_from_abstract_base_class(self):
       
   312         """
       
   313         Regression tests for #7588
       
   314         """
       
   315         # All fields from an ABC, including those inherited non-abstractly
       
   316         # should be available on child classes (#7588). Creating this instance
       
   317         # should work without error.
       
   318         QualityControl.objects.create(
       
   319             headline="Problems in Django",
       
   320             pub_date=datetime.datetime.now(),
       
   321             quality=10,
       
   322             assignee="adrian")
       
   323 
       
   324     def test_abstract_base_class_m2m_relation_inheritance(self):
       
   325         # Check that many-to-many relations defined on an abstract base class
       
   326         # are correctly inherited (and created) on the child class.
       
   327         p1 = Person.objects.create(name='Alice')
       
   328         p2 = Person.objects.create(name='Bob')
       
   329         p3 = Person.objects.create(name='Carol')
       
   330         p4 = Person.objects.create(name='Dave')
       
   331 
       
   332         birthday = BirthdayParty.objects.create(
       
   333             name='Birthday party for Alice')
       
   334         birthday.attendees = [p1, p3]
       
   335 
       
   336         bachelor = BachelorParty.objects.create(name='Bachelor party for Bob')
       
   337         bachelor.attendees = [p2, p4]
       
   338 
       
   339         parties = list(p1.birthdayparty_set.all())
       
   340         self.assertEqual(parties, [birthday])
       
   341 
       
   342         parties = list(p1.bachelorparty_set.all())
       
   343         self.assertEqual(parties, [])
       
   344 
       
   345         parties = list(p2.bachelorparty_set.all())
       
   346         self.assertEqual(parties, [bachelor])
       
   347 
       
   348         # Check that a subclass of a subclass of an abstract model doesn't get
       
   349         # it's own accessor.
       
   350         self.assertFalse(hasattr(p2, 'messybachelorparty_set'))
       
   351 
       
   352         # ... but it does inherit the m2m from it's parent
       
   353         messy = MessyBachelorParty.objects.create(
       
   354             name='Bachelor party for Dave')
       
   355         messy.attendees = [p4]
       
   356         messy_parent = messy.bachelorparty_ptr
       
   357 
       
   358         parties = list(p4.bachelorparty_set.all())
       
   359         self.assertEqual(parties, [bachelor, messy_parent])
       
   360 
       
   361     def test_11369(self):
       
   362         """
       
   363         verbose_name_plural correctly inherited from ABC if inheritance chain
       
   364         includes an abstract model.
       
   365         """
       
   366         # Regression test for #11369: verbose_name_plural should be inherited
       
   367         # from an ABC even when there are one or more intermediate
       
   368         # abstract models in the inheritance chain, for consistency with
       
   369         # verbose_name.
       
   370         self.assertEquals(
       
   371                 InternalCertificationAudit._meta.verbose_name_plural,
       
   372                 u'Audits'
       
   373         )
       
   374 
       
   375     def test_inherited_nullable_exclude(self):
       
   376         obj = SelfRefChild.objects.create(child_data=37, parent_data=42)
       
   377         self.assertQuerysetEqual(
       
   378             SelfRefParent.objects.exclude(self_data=72), [
       
   379                 obj.pk
       
   380             ],
       
   381             attrgetter("pk")
       
   382         )
       
   383         self.assertQuerysetEqual(
       
   384             SelfRefChild.objects.exclude(self_data=72), [
       
   385                 obj.pk
       
   386             ],
       
   387             attrgetter("pk")
       
   388         )