diff --git a/django/db/models/base.py b/django/db/models/base.py
index 449c41a..4ed402f 100644
a
|
b
|
class ModelBase(type):
|
52 | 52 | |
53 | 53 | new_class.add_to_class('_meta', Options(meta, **kwargs)) |
54 | 54 | if not abstract: |
55 | | new_class.add_to_class('DoesNotExist', |
56 | | subclass_exception('DoesNotExist', ObjectDoesNotExist, module)) |
57 | | new_class.add_to_class('MultipleObjectsReturned', |
58 | | subclass_exception('MultipleObjectsReturned', MultipleObjectsReturned, module)) |
| 55 | new_class.add_to_class('DoesNotExist', subclass_exception('DoesNotExist', |
| 56 | tuple(x.DoesNotExist |
| 57 | for x in parents if hasattr(x, '_meta') and not x._meta.abstract) |
| 58 | or (ObjectDoesNotExist,), module)) |
| 59 | new_class.add_to_class('MultipleObjectsReturned', subclass_exception('MultipleObjectsReturned', |
| 60 | tuple(x.MultipleObjectsReturned |
| 61 | for x in parents if hasattr(x, '_meta') and not x._meta.abstract) |
| 62 | or (MultipleObjectsReturned,), module)) |
59 | 63 | if base_meta and not base_meta.abstract: |
60 | 64 | # Non-abstract child classes inherit some attributes from their |
61 | 65 | # non-abstract parent (unless an ABC comes before it in the |
… |
… |
model_unpickle.__safe_for_unpickle__ = True
|
919 | 923 | |
920 | 924 | if sys.version_info < (2, 5): |
921 | 925 | # Prior to Python 2.5, Exception was an old-style class |
922 | | def subclass_exception(name, parent, unused): |
923 | | return types.ClassType(name, (parent,), {}) |
| 926 | def subclass_exception(name, parents, unused): |
| 927 | return types.ClassType(name, parents, {}) |
924 | 928 | else: |
925 | | def subclass_exception(name, parent, module): |
926 | | return type(name, (parent,), {'__module__': module}) |
| 929 | def subclass_exception(name, parents, module): |
| 930 | return type(name, parents, {'__module__': module}) |
diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
index 36fc9fe..95bf5ab 100644
a
|
b
|
class Student(CommonInfo):
|
38 | 38 | class Meta: |
39 | 39 | pass |
40 | 40 | |
| 41 | class StudentWorker(Student, Worker): |
| 42 | pass |
| 43 | |
41 | 44 | # |
42 | 45 | # Abstract base classes with related models |
43 | 46 | # |
… |
… |
Traceback (most recent call last):
|
176 | 179 | ... |
177 | 180 | AttributeError: type object 'CommonInfo' has no attribute 'objects' |
178 | 181 | |
| 182 | # A StudentWorker which does not exist is both a Student and Worker which does not exist. |
| 183 | >>> try: |
| 184 | ... StudentWorker.objects.get(id=1) |
| 185 | ... except Student.DoesNotExist: |
| 186 | ... pass |
| 187 | >>> try: |
| 188 | ... StudentWorker.objects.get(id=1) |
| 189 | ... except Worker.DoesNotExist: |
| 190 | ... pass |
| 191 | |
| 192 | # MultipleObjectsReturned is also inherited. |
| 193 | >>> sw1 = StudentWorker() |
| 194 | >>> sw1.name = 'Wilma' |
| 195 | >>> sw1.age = 35 |
| 196 | >>> sw1.save() |
| 197 | >>> sw2 = StudentWorker() |
| 198 | >>> sw2.name = 'Betty' |
| 199 | >>> sw2.age = 34 |
| 200 | >>> sw2.save() |
| 201 | >>> try: |
| 202 | ... StudentWorker.objects.get(id__lt=10) |
| 203 | ... except Student.MultipleObjectsReturned: |
| 204 | ... pass |
| 205 | ... except Worker.MultipleObjectsReturned: |
| 206 | ... pass |
| 207 | |
179 | 208 | # Create a Post |
180 | 209 | >>> post = Post(title='Lorem Ipsum') |
181 | 210 | >>> post.save() |
… |
… |
Traceback (most recent call last):
|
267 | 296 | ... |
268 | 297 | DoesNotExist: ItalianRestaurant matching query does not exist. |
269 | 298 | |
| 299 | # An ItalianRestaurant which does not exist is also a Place which does not exist. |
| 300 | >>> try: |
| 301 | ... ItalianRestaurant.objects.get(name='The Noodle Void') |
| 302 | ... except Place.DoesNotExist: |
| 303 | ... pass |
| 304 | |
| 305 | # MultipleObjectsReturned is also inherited. |
| 306 | >>> try: |
| 307 | ... Restaurant.objects.get(id__lt=10) |
| 308 | ... except Place.MultipleObjectsReturned: |
| 309 | ... pass |
| 310 | |
270 | 311 | # Related objects work just as they normally do. |
271 | 312 | |
272 | 313 | >>> s1 = Supplier(name="Joe's Chickens", address='123 Sesame St') |
diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py
index 44fee9e..49972f3 100644
a
|
b
|
False
|
206 | 206 | >>> MyPersonProxy.objects.all() |
207 | 207 | [<MyPersonProxy: Bazza del Frob>, <MyPersonProxy: Foo McBar>, <MyPersonProxy: homer>] |
208 | 208 | |
| 209 | # Proxy models are included in the ancestors for a model's DoesNotExist and MultipleObjectsReturned |
| 210 | >>> try: |
| 211 | ... MyPersonProxy.objects.get(name='Zathras') |
| 212 | ... except Person.DoesNotExist: |
| 213 | ... pass |
| 214 | >>> try: |
| 215 | ... MyPersonProxy.objects.get(id__lt=10) |
| 216 | ... except Person.MultipleObjectsReturned: |
| 217 | ... pass |
| 218 | >>> try: |
| 219 | ... StatusPerson.objects.get(name='Zathras') |
| 220 | ... except Person.DoesNotExist: |
| 221 | ... pass |
| 222 | >>> sp1 = StatusPerson.objects.create(name='Bazza Jr.') |
| 223 | >>> sp2 = StatusPerson.objects.create(name='Foo Jr.') |
| 224 | >>> try: |
| 225 | ... StatusPerson.objects.get(id__lt=10) |
| 226 | ... except Person.MultipleObjectsReturned: |
| 227 | ... pass |
| 228 | |
209 | 229 | # And now for some things that shouldn't work... |
210 | 230 | # |
211 | 231 | # All base classes must be non-abstract |