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