Code

Ticket #12152: django-exception-inheritance.diff

File django-exception-inheritance.diff, 5.0 KB (added by Alex, 4 years ago)
Line 
1diff --git a/django/db/models/base.py b/django/db/models/base.py
2index 449c41a..4ed402f 100644
3--- a/django/db/models/base.py
4+++ b/django/db/models/base.py
5@@ -52,10 +52,14 @@ class ModelBase(type):
6 
7         new_class.add_to_class('_meta', Options(meta, **kwargs))
8         if not abstract:
9-            new_class.add_to_class('DoesNotExist',
10-                    subclass_exception('DoesNotExist', ObjectDoesNotExist, module))
11-            new_class.add_to_class('MultipleObjectsReturned',
12-                    subclass_exception('MultipleObjectsReturned', MultipleObjectsReturned, module))
13+            new_class.add_to_class('DoesNotExist', subclass_exception('DoesNotExist',
14+                    tuple(x.DoesNotExist
15+                            for x in parents if hasattr(x, '_meta') and not x._meta.abstract)
16+                                    or (ObjectDoesNotExist,), module))
17+            new_class.add_to_class('MultipleObjectsReturned', subclass_exception('MultipleObjectsReturned',
18+                    tuple(x.MultipleObjectsReturned
19+                            for x in parents if hasattr(x, '_meta') and not x._meta.abstract)
20+                                    or (MultipleObjectsReturned,), module))
21             if base_meta and not base_meta.abstract:
22                 # Non-abstract child classes inherit some attributes from their
23                 # non-abstract parent (unless an ABC comes before it in the
24@@ -919,8 +923,8 @@ model_unpickle.__safe_for_unpickle__ = True
25 
26 if sys.version_info < (2, 5):
27     # Prior to Python 2.5, Exception was an old-style class
28-    def subclass_exception(name, parent, unused):
29-        return types.ClassType(name, (parent,), {})
30+    def subclass_exception(name, parents, unused):
31+        return types.ClassType(name, parents, {})
32 else:
33-    def subclass_exception(name, parent, module):
34-        return type(name, (parent,), {'__module__': module})
35+    def subclass_exception(name, parents, module):
36+        return type(name, parents, {'__module__': module})
37diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
38index 36fc9fe..95bf5ab 100644
39--- a/tests/modeltests/model_inheritance/models.py
40+++ b/tests/modeltests/model_inheritance/models.py
41@@ -38,6 +38,9 @@ class Student(CommonInfo):
42     class Meta:
43         pass
44 
45+class StudentWorker(Student, Worker):
46+    pass
47+
48 #
49 # Abstract base classes with related models
50 #
51@@ -176,6 +179,32 @@ Traceback (most recent call last):
52     ...
53 AttributeError: type object 'CommonInfo' has no attribute 'objects'
54 
55+# A StudentWorker which does not exist is both a Student and Worker which does not exist.
56+>>> try:
57+...     StudentWorker.objects.get(id=1)
58+... except Student.DoesNotExist:
59+...     pass
60+>>> try:
61+...     StudentWorker.objects.get(id=1)
62+... except Worker.DoesNotExist:
63+...     pass
64+
65+# MultipleObjectsReturned is also inherited.
66+>>> sw1 = StudentWorker()
67+>>> sw1.name = 'Wilma'
68+>>> sw1.age = 35
69+>>> sw1.save()
70+>>> sw2 = StudentWorker()
71+>>> sw2.name = 'Betty'
72+>>> sw2.age = 34
73+>>> sw2.save()
74+>>> try:
75+...     StudentWorker.objects.get(id__lt=10)
76+... except Student.MultipleObjectsReturned:
77+...     pass
78+... except Worker.MultipleObjectsReturned:
79+...     pass
80+
81 # Create a Post
82 >>> post = Post(title='Lorem Ipsum')
83 >>> post.save()
84@@ -267,6 +296,18 @@ Traceback (most recent call last):
85     ...
86 DoesNotExist: ItalianRestaurant matching query does not exist.
87 
88+# An ItalianRestaurant which does not exist is also a Place which does not exist.
89+>>> try:
90+...     ItalianRestaurant.objects.get(name='The Noodle Void')
91+... except Place.DoesNotExist:
92+...     pass
93+
94+# MultipleObjectsReturned is also inherited.
95+>>> try:
96+...     Restaurant.objects.get(id__lt=10)
97+... except Place.MultipleObjectsReturned:
98+...     pass
99+
100 # Related objects work just as they normally do.
101 
102 >>> s1 = Supplier(name="Joe's Chickens", address='123 Sesame St')
103diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py
104index 44fee9e..49972f3 100644
105--- a/tests/modeltests/proxy_models/models.py
106+++ b/tests/modeltests/proxy_models/models.py
107@@ -206,6 +206,26 @@ False
108 >>> MyPersonProxy.objects.all()
109 [<MyPersonProxy: Bazza del Frob>, <MyPersonProxy: Foo McBar>, <MyPersonProxy: homer>]
110 
111+# Proxy models are included in the ancestors for a model's DoesNotExist and MultipleObjectsReturned
112+>>> try:
113+...     MyPersonProxy.objects.get(name='Zathras')
114+... except Person.DoesNotExist:
115+...     pass
116+>>> try:
117+...     MyPersonProxy.objects.get(id__lt=10)
118+... except Person.MultipleObjectsReturned:
119+...     pass
120+>>> try:
121+...     StatusPerson.objects.get(name='Zathras')
122+... except Person.DoesNotExist:
123+...     pass
124+>>> sp1 = StatusPerson.objects.create(name='Bazza Jr.')
125+>>> sp2 = StatusPerson.objects.create(name='Foo Jr.')
126+>>> try:
127+...     StatusPerson.objects.get(id__lt=10)
128+... except Person.MultipleObjectsReturned:
129+...     pass
130+
131 # And now for some things that shouldn't work...
132 #
133 # All base classes must be non-abstract