diff -r 5ff1fc726848 -r c6bca38c1cbf parts/django/tests/regressiontests/model_inheritance_regress/tests.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/parts/django/tests/regressiontests/model_inheritance_regress/tests.py Sat Jan 08 11:20:57 2011 +0530 @@ -0,0 +1,388 @@ +""" +Regression tests for Model inheritance behaviour. +""" + +import datetime +from operator import attrgetter + +from django.test import TestCase + +from models import (Place, Restaurant, ItalianRestaurant, ParkingLot, + ParkingLot2, ParkingLot3, Supplier, Wholesaler, Child, SelfRefParent, + SelfRefChild, ArticleWithAuthor, M2MChild, QualityControl, DerivedM, + Person, BirthdayParty, BachelorParty, MessyBachelorParty, + InternalCertificationAudit) + + +class ModelInheritanceTest(TestCase): + def test_model_inheritance(self): + # Regression for #7350, #7202 + # Check that when you create a Parent object with a specific reference + # to an existent child instance, saving the Parent doesn't duplicate + # the child. This behaviour 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 + 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) + + # Create a child-parent chain with an explicit parent link + 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) + + # Check that no extra parent objects have been created. + places = list(Place.objects.all()) + self.assertEqual(places, [place1, place2]) + + dicts = list(Restaurant.objects.values('name','serves_hot_dogs')) + self.assertEqual(dicts, [{ + 'name': u"Guido's House of Pasta", + 'serves_hot_dogs': True + }]) + + dicts = list(ItalianRestaurant.objects.values( + 'name','serves_hot_dogs','serves_gnocchi')) + self.assertEqual(dicts, [{ + 'name': u"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': u'Main St', + }]) + + # You can also update objects when using a raw save. + 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) + + # No extra parent objects after an update, either. + 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': u"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': u"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': u'Derelict lot', + }]) + + # If you try to raw_save a parent attribute onto a child object, + # the attribute will be ignored. + + italian_restaurant.name = "Lorenzo's Pasta Hut" + italian_restaurant.save_base(raw=True) + + # Note that the name has not changed + # - name is an attribute of Place, not ItalianRestaurant + dicts = list(ItalianRestaurant.objects.values( + 'name','serves_hot_dogs','serves_gnocchi')) + self.assertEqual(dicts, [{ + 'name': u"Guido's All New House of Pasta", + 'serves_gnocchi': False, + 'serves_hot_dogs': False, + }]) + + def test_issue_7105(self): + # Regressions tests for #7105: dates() queries should be able to use + # fields from the parent model as easily as the child. + obj = Child.objects.create( + name='child', + created=datetime.datetime(2008, 6, 26, 17, 0, 0)) + dates = list(Child.objects.dates('created', 'month')) + self.assertEqual(dates, [datetime.datetime(2008, 6, 1, 0, 0)]) + + def test_issue_7276(self): + # 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. + 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) + xx = Restaurant.objects.create( + name='a', + address='xx', + serves_hot_dogs=True, + serves_pizza=False) + + # This should delete both Restuarants, plus the related places, plus + # the ItalianRestaurant. + Restaurant.objects.all().delete() + + self.assertRaises( + Place.DoesNotExist, + Place.objects.get, + pk=ident) + self.assertRaises( + ItalianRestaurant.DoesNotExist, + ItalianRestaurant.objects.get, + pk=ident) + + def test_issue_6755(self): + """ + Regression test for #6755 + """ + r = Restaurant(serves_pizza=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) + r.save() + self.assertEqual(r.id, orig_id) + self.assertEqual(r.id, r.place_ptr_id) + + def test_issue_7488(self): + # Regression test for #7488. This looks a little crazy, but it's the + # equivalent of what the admin interface has to do for the edit-inline + # case. + suppliers = Supplier.objects.filter( + restaurant=Restaurant(name='xx', address='yy')) + suppliers = list(suppliers) + self.assertEqual(suppliers, []) + + def test_issue_11764(self): + """ + Regression test for #11764 + """ + wholesalers = list(Wholesaler.objects.all().select_related()) + self.assertEqual(wholesalers, []) + + def test_issue_7853(self): + """ + 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. + """ + obj = SelfRefChild.objects.create(child_data=37, parent_data=42) + obj.delete() + + def test_get_next_previous_by_date(self): + """ + Regression tests for #8076 + get_(next/previous)_by_date should work + """ + 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) + 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) + self.assertRaises( + ArticleWithAuthor.DoesNotExist, + c1.get_previous_by_pub_date) + + def test_inherited_fields(self): + """ + Regression test for #8825 and #9390 + Make sure all inherited fields (esp. m2m fields, in this case) appear + on the child class. + """ + m2mchildren = list(M2MChild.objects.filter(articles__isnull=False)) + self.assertEqual(m2mchildren, []) + + # Ordering should not include any database column more than once (this + # is most likely to ocurr 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. + 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): + """ + Regression test for #10362 + It is possible to call update() and only change a field in + an ancestor model. + """ + 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): + """ + Regression tests for #10406 + If there's a one-to-one link between a child model and the parent and + no explicit pk declared, we can use the one-to-one link as the pk on + the child. + """ + self.assertEqual(ParkingLot2._meta.pk.name, "parent") + + # However, the connector from child to parent need not be the pk on + # the child at all. + self.assertEqual(ParkingLot3._meta.pk.name, "primary_key") + # the child->parent link + self.assertEqual( + ParkingLot3._meta.get_ancestor_link(Place).name, + "parent") + + def test_all_fields_from_abstract_base_class(self): + """ + 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. + 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): + # Check that many-to-many relations defined on an abstract base class + # are correctly inherited (and created) on the child class. + 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 = [p1, p3] + + bachelor = BachelorParty.objects.create(name='Bachelor party for Bob') + bachelor.attendees = [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]) + + # Check that a subclass of a subclass of an abstract model doesn't get + # it's own accessor. + self.assertFalse(hasattr(p2, 'messybachelorparty_set')) + + # ... but it does inherit the m2m from it's parent + messy = MessyBachelorParty.objects.create( + name='Bachelor party for Dave') + messy.attendees = [p4] + messy_parent = messy.bachelorparty_ptr + + parties = list(p4.bachelorparty_set.all()) + self.assertEqual(parties, [bachelor, messy_parent]) + + def test_11369(self): + """ + 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. + self.assertEquals( + InternalCertificationAudit._meta.verbose_name_plural, + u'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") + )