PageRenderTime 467ms CodeModel.GetById 242ms app.highlight 84ms RepoModel.GetById 137ms app.codeStats 0ms

/tests/modeltests/model_inheritance/tests.py

https://code.google.com/p/mango-py/
Python | 275 lines | 194 code | 32 blank | 49 comment | 4 complexity | 00749b6b999857e5b4a8f15b6ce5d7ac MD5 | raw file
  1from operator import attrgetter
  2
  3from django.core.exceptions import FieldError
  4from django.test import TestCase
  5
  6from models import (Chef, CommonInfo, ItalianRestaurant, ParkingLot, Place,
  7    Post, Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel)
  8
  9
 10class ModelInheritanceTests(TestCase):
 11    def test_abstract(self):
 12        # The Student and Worker models both have 'name' and 'age' fields on
 13        # them and inherit the __unicode__() method, just as with normal Python
 14        # subclassing. This is useful if you want to factor out common
 15        # information for programming purposes, but still completely
 16        # independent separate models at the database level.
 17        w1 = Worker.objects.create(name="Fred", age=35, job="Quarry worker")
 18        w2 = Worker.objects.create(name="Barney", age=34, job="Quarry worker")
 19
 20        s = Student.objects.create(name="Pebbles", age=5, school_class="1B")
 21
 22        self.assertEqual(unicode(w1), "Worker Fred")
 23        self.assertEqual(unicode(s), "Student Pebbles")
 24
 25        # The children inherit the Meta class of their parents (if they don't
 26        # specify their own).
 27        self.assertQuerysetEqual(
 28            Worker.objects.values("name"), [
 29                {"name": "Barney"},
 30                {"name": "Fred"},
 31            ],
 32            lambda o: o
 33        )
 34
 35        # Since Student does not subclass CommonInfo's Meta, it has the effect
 36        # of completely overriding it. So ordering by name doesn't take place
 37        # for Students.
 38        self.assertEqual(Student._meta.ordering, [])
 39
 40        # However, the CommonInfo class cannot be used as a normal model (it
 41        # doesn't exist as a model).
 42        self.assertRaises(AttributeError, lambda: CommonInfo.objects.all())
 43
 44        # A StudentWorker which does not exist is both a Student and Worker
 45        # which does not exist.
 46        self.assertRaises(Student.DoesNotExist,
 47            StudentWorker.objects.get, pk=12321321
 48        )
 49        self.assertRaises(Worker.DoesNotExist,
 50            StudentWorker.objects.get, pk=12321321
 51        )
 52
 53        # MultipleObjectsReturned is also inherited.
 54        # This is written out "long form", rather than using __init__/create()
 55        # because of a bug with diamond inheritance (#10808)
 56        sw1 = StudentWorker()
 57        sw1.name = "Wilma"
 58        sw1.age = 35
 59        sw1.save()
 60        sw2 = StudentWorker()
 61        sw2.name = "Betty"
 62        sw2.age = 24
 63        sw2.save()
 64
 65        self.assertRaises(Student.MultipleObjectsReturned,
 66            StudentWorker.objects.get, pk__lt=sw2.pk + 100
 67        )
 68        self.assertRaises(Worker.MultipleObjectsReturned,
 69            StudentWorker.objects.get, pk__lt=sw2.pk + 100
 70        )
 71
 72    def test_multiple_table(self):
 73        post = Post.objects.create(title="Lorem Ipsum")
 74        # The Post model has distinct accessors for the Comment and Link models.
 75        post.attached_comment_set.create(content="Save $ on V1agr@", is_spam=True)
 76        post.attached_link_set.create(
 77            content="The Web framework for perfections with deadlines.",
 78            url="http://www.djangoproject.com/"
 79        )
 80
 81        # The Post model doesn't have an attribute called
 82        # 'attached_%(class)s_set'.
 83        self.assertRaises(AttributeError,
 84            getattr, post, "attached_%(class)s_set"
 85        )
 86
 87        # The Place/Restaurant/ItalianRestaurant models all exist as
 88        # independent models. However, the subclasses also have transparent
 89        # access to the fields of their ancestors.
 90        # Create a couple of Places.
 91        p1 = Place.objects.create(name="Master Shakes", address="666 W. Jersey")
 92        p2 = Place.objects.create(name="Ace Harware", address="1013 N. Ashland")
 93
 94        # Test constructor for Restaurant.
 95        r = Restaurant.objects.create(
 96            name="Demon Dogs",
 97            address="944 W. Fullerton",
 98            serves_hot_dogs=True,
 99            serves_pizza=False,
100            rating=2
101        )
102        # Test the constructor for ItalianRestaurant.
103        c = Chef.objects.create(name="Albert")
104        ir = ItalianRestaurant.objects.create(
105            name="Ristorante Miron",
106            address="1234 W. Ash",
107            serves_hot_dogs=False,
108            serves_pizza=False,
109            serves_gnocchi=True,
110            rating=4,
111            chef=c
112        )
113        self.assertQuerysetEqual(
114            ItalianRestaurant.objects.filter(address="1234 W. Ash"), [
115                "Ristorante Miron",
116            ],
117            attrgetter("name")
118        )
119        ir.address = "1234 W. Elm"
120        ir.save()
121        self.assertQuerysetEqual(
122            ItalianRestaurant.objects.filter(address="1234 W. Elm"), [
123                "Ristorante Miron",
124            ],
125            attrgetter("name")
126        )
127
128        # Make sure Restaurant and ItalianRestaurant have the right fields in
129        # the right order.
130        self.assertEqual(
131            [f.name for f in Restaurant._meta.fields],
132            ["id", "name", "address", "place_ptr", "rating", "serves_hot_dogs", "serves_pizza", "chef"]
133        )
134        self.assertEqual(
135            [f.name for f in ItalianRestaurant._meta.fields],
136            ["id", "name", "address", "place_ptr", "rating", "serves_hot_dogs", "serves_pizza", "chef", "restaurant_ptr", "serves_gnocchi"],
137        )
138        self.assertEqual(Restaurant._meta.ordering, ["-rating"])
139
140        # Even though p.supplier for a Place 'p' (a parent of a Supplier), a
141        # Restaurant object cannot access that reverse relation, since it's not
142        # part of the Place-Supplier Hierarchy.
143        self.assertQuerysetEqual(Place.objects.filter(supplier__name="foo"), [])
144        self.assertRaises(FieldError,
145            Restaurant.objects.filter, supplier__name="foo"
146        )
147
148        # Parent fields can be used directly in filters on the child model.
149        self.assertQuerysetEqual(
150            Restaurant.objects.filter(name="Demon Dogs"), [
151                "Demon Dogs",
152            ],
153            attrgetter("name")
154        )
155        self.assertQuerysetEqual(
156            ItalianRestaurant.objects.filter(address="1234 W. Elm"), [
157                "Ristorante Miron",
158            ],
159            attrgetter("name")
160        )
161
162        # Filters against the parent model return objects of the parent's type.
163        p = Place.objects.get(name="Demon Dogs")
164        self.assertIs(type(p), Place)
165
166        # Since the parent and child are linked by an automatically created
167        # OneToOneField, you can get from the parent to the child by using the
168        # child's name.
169        self.assertEqual(
170            p.restaurant, Restaurant.objects.get(name="Demon Dogs")
171        )
172        self.assertEqual(
173            Place.objects.get(name="Ristorante Miron").restaurant.italianrestaurant,
174            ItalianRestaurant.objects.get(name="Ristorante Miron")
175        )
176        self.assertEqual(
177            Restaurant.objects.get(name="Ristorante Miron").italianrestaurant,
178            ItalianRestaurant.objects.get(name="Ristorante Miron")
179        )
180
181        # This won't work because the Demon Dogs restaurant is not an Italian
182        # restaurant.
183        self.assertRaises(ItalianRestaurant.DoesNotExist,
184            lambda: p.restaurant.italianrestaurant
185        )
186        # An ItalianRestaurant which does not exist is also a Place which does
187        # not exist.
188        self.assertRaises(Place.DoesNotExist,
189            ItalianRestaurant.objects.get, name="The Noodle Void"
190        )
191        # MultipleObjectsReturned is also inherited.
192        self.assertRaises(Place.MultipleObjectsReturned,
193            Restaurant.objects.get, id__lt=12321
194        )
195
196        # Related objects work just as they normally do.
197        s1 = Supplier.objects.create(name="Joe's Chickens", address="123 Sesame St")
198        s1.customers = [r, ir]
199        s2 = Supplier.objects.create(name="Luigi's Pasta", address="456 Sesame St")
200        s2.customers = [ir]
201
202        # This won't work because the Place we select is not a Restaurant (it's
203        # a Supplier).
204        p = Place.objects.get(name="Joe's Chickens")
205        self.assertRaises(Restaurant.DoesNotExist,
206            lambda: p.restaurant
207        )
208
209        self.assertEqual(p.supplier, s1)
210        self.assertQuerysetEqual(
211            ir.provider.order_by("-name"), [
212                "Luigi's Pasta",
213                "Joe's Chickens"
214            ],
215            attrgetter("name")
216        )
217        self.assertQuerysetEqual(
218            Restaurant.objects.filter(provider__name__contains="Chickens"), [
219                "Ristorante Miron",
220                "Demon Dogs",
221            ],
222            attrgetter("name")
223        )
224        self.assertQuerysetEqual(
225            ItalianRestaurant.objects.filter(provider__name__contains="Chickens"), [
226                "Ristorante Miron",
227            ],
228            attrgetter("name"),
229        )
230
231        park1 = ParkingLot.objects.create(
232            name="Main St", address="111 Main St", main_site=s1
233        )
234        park2 = ParkingLot.objects.create(
235            name="Well Lit", address="124 Sesame St", main_site=ir
236        )
237
238        self.assertEqual(
239            Restaurant.objects.get(lot__name="Well Lit").name,
240            "Ristorante Miron"
241        )
242
243        # The update() command can update fields in parent and child classes at
244        # once (although it executed multiple SQL queries to do so).
245        rows = Restaurant.objects.filter(
246            serves_hot_dogs=True, name__contains="D"
247        ).update(
248            name="Demon Puppies", serves_hot_dogs=False
249        )
250        self.assertEqual(rows, 1)
251
252        r1 = Restaurant.objects.get(pk=r.pk)
253        self.assertFalse(r1.serves_hot_dogs)
254        self.assertEqual(r1.name, "Demon Puppies")
255
256        # The values() command also works on fields from parent models.
257        self.assertQuerysetEqual(
258            ItalianRestaurant.objects.values("name", "rating"), [
259                {"rating": 4, "name": "Ristorante Miron"}
260            ],
261            lambda o: o
262        )
263
264        # select_related works with fields from the parent object as if they
265        # were a normal part of the model.
266        self.assertNumQueries(2,
267            lambda: ItalianRestaurant.objects.all()[0].chef
268        )
269        self.assertNumQueries(1,
270            lambda: ItalianRestaurant.objects.select_related("chef")[0].chef
271        )
272
273    def test_mixin_init(self):
274        m = MixinModel()
275        self.assertEqual(m.other_attr, 1)