Changeset 7141
- Timestamp:
- 02/21/08 19:05:05 (5 months ago)
- Files:
-
- django/branches/queryset-refactor/django/db/models/base.py (modified) (2 diffs)
- django/branches/queryset-refactor/django/db/models/options.py (modified) (1 diff)
- django/branches/queryset-refactor/docs/model-api.txt (modified) (4 diffs)
- django/branches/queryset-refactor/tests/modeltests/model_inheritance/models.py (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/queryset-refactor/django/db/models/base.py
r7137 r7141 36 36 # Create the class. 37 37 module = attrs.pop('__module__') 38 meta = attrs.pop('Meta', None)39 38 new_class = type.__new__(cls, name, bases, {'__module__': module}) 39 attr_meta = attrs.pop('Meta', None) 40 abstract = getattr(attr_meta, 'abstract', False) 41 if not attr_meta: 42 meta = getattr(new_class, 'Meta', None) 43 else: 44 meta = attr_meta 45 base_meta = getattr(new_class, '_meta', None) 46 40 47 new_class.add_to_class('_meta', Options(meta)) 41 new_class.add_to_class('DoesNotExist', 42 subclass_exception('DoesNotExist', ObjectDoesNotExist, module)) 43 new_class.add_to_class('MultipleObjectsReturned', 44 subclass_exception('MultipleObjectsReturned', 45 MultipleObjectsReturned, module)) 48 if not abstract: 49 new_class.add_to_class('DoesNotExist', 50 subclass_exception('DoesNotExist', ObjectDoesNotExist, module)) 51 new_class.add_to_class('MultipleObjectsReturned', 52 subclass_exception('MultipleObjectsReturned', MultipleObjectsReturned, module)) 53 if base_meta and not base_meta.abstract: 54 # Non-abstract child classes inherit some attributes from their 55 # non-abstract parent (unless an ABC comes before it in the 56 # method resolution order). 57 if not hasattr(meta, 'ordering'): 58 new_class._meta.ordering = base_meta.ordering 59 if not hasattr(meta, 'get_latest_by'): 60 new_class._meta.get_latest_by = base_meta.get_latest_by 46 61 47 62 # Do the appropriate setup for any model parents. … … 87 102 new_class.add_to_class(field.name, field) 88 103 89 if new_class._meta.abstract:104 if abstract: 90 105 # Abstract base models can't be instantiated and don't appear in 91 106 # the list of models for an app. We do the final setup for them a 92 107 # little differently from normal models. 108 attr_meta.abstract = False 109 new_class.Meta = attr_meta 93 110 return new_class 94 111 django/branches/queryset-refactor/django/db/models/options.py
r7137 r7141 56 56 # Next, apply any overridden values from 'class Meta'. 57 57 if self.meta: 58 meta_attrs = self.meta.__dict__ 58 meta_attrs = self.meta.__dict__.copy() 59 59 del meta_attrs['__module__'] 60 60 del meta_attrs['__doc__'] django/branches/queryset-refactor/docs/model-api.txt
r7126 r7141 2032 2032 ================= 2033 2033 2034 **New in Django development version** 2035 2036 Model inheritance in Django works almost identically to the way normal class 2037 inheritance works in Python. The only decision you have to make is whether you 2038 want the parent models to be models in their own right (with their own 2039 database tables), or if the parents are just holders of common information 2040 that will only be visible through the child models. 2041 2042 Often, you will just want to use the parent class to hold information that you 2043 don't want to have to type out for each child model. This class isn't going to 2044 ever be used in isolation, so `abstract base classes`_ are what you're after. However, if you're subclassing an existing model (perhaps something from another application entirely), or want each model to have its own database table, `multi-table inheritance`_ is the way to go. 2045 2034 2046 Abstract base classes 2035 2047 --------------------- … … 2064 2076 still only creating one database table per child model at the database level. 2065 2077 2078 ``Meta`` inheritance 2079 ~~~~~~~~~~~~~~~~~~~~ 2080 2081 When an abstract base class is created, Django makes any ``Meta`` inner class 2082 you declared on the base class available as an attribute. If a child class 2083 does not declared its own ``Meta`` class, it will inherit the parent's 2084 ``Meta``. If the child wants to extend the parent's ``Meta`` class, it can 2085 subclass it. For example:: 2086 2087 class CommonInfo(models.Model): 2088 ... 2089 class Meta: 2090 abstract = True 2091 ordering = ['name'] 2092 2093 class Student(CommonInfo): 2094 ... 2095 class Meta(CommonInfo.Meta): 2096 db_table = 'student_info' 2097 2098 Django does make one adjustment to the ``Meta`` class of an abstract base 2099 class: before installing the ``Meta`` attribute, it sets ``abstract=False``. 2100 This means that children of abstract base classes don't automatically become 2101 abstract classes themselves. Of course, you can make an abstract base class 2102 that inherits from another abstract base class. You just need to remember to 2103 explicitly set ``abstract=True`` each time. 2104 2105 Some attributes won't make sense to include in the ``Meta`` class of an 2106 abstract base class. For example, including ``db_table`` would mean that all 2107 the child classes (the ones that don't specify their own ``Meta``) would use 2108 the same database table, which is almost certainly not what you want. 2109 2066 2110 Multi-table inheritance 2067 2111 ----------------------- … … 2101 2145 referring to ``p.restaurant`` would give an error. 2102 2146 2103 Normally you won't need to worry too much about how model inheritance works. 2104 It will behave similarly to Python class inheritance. 2147 ``Meta`` and multi-table inheritance 2148 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2149 2150 In the multi-table inheritance situation, it doesn't make sense for a child 2151 class to inherit from its parent's ``Meta`` class. All the ``Meta`` options 2152 have already been applied to the parent class and applying them again would 2153 normally only lead to contradictory behaviour (this is in contrast with the 2154 abstract base class case, where the base class doesn't exist in its own 2155 right). 2156 2157 So a child model does not have access to its parent's ``Meta`` class. However, 2158 there are a few limited cases where the child inherits behaviour from the 2159 parent: if the child does not specify an ``ordering`` attribute or a 2160 ``get_latest_by`` attribute, it will inherit these from its parent. 2161 2162 If the parent has an ordering and you don't want the child to have any natural 2163 ordering, you can explicity set it to be empty:: 2164 2165 class ChildModel(ParentModel): 2166 ... 2167 class Meta: 2168 # Remove parent's ordering effect 2169 ordering = [] 2105 2170 2106 2171 Inheritance and reverse relations … … 2128 2193 you're writing your models and pay attention to the error messages. 2129 2194 2195 Multiple inheritance 2196 -------------------- 2197 2198 Just as with Python's subclassing, it's possible for a Django model to inherit 2199 from multiple parent models. Keep in mind that normal Python name resolution 2200 rules apply. The first base class that a particular name appears in (e.g. 2201 ``Meta``) will be the one that is used. We stop searching once we find the 2202 name once. This means that if multiple parents contain a ``Meta`` class, only 2203 the first one is going to be used. All others will be ignored. 2204 2205 Generally, you won't need to inherit from multiple parents. The main use-case 2206 where this is useful is for ''mix-in'' classes: adding a particular extra 2207 field or method to every class that inherits the mix-in. Try to keep your 2208 inheritance hierarchies as simple and straightforward as possible so that you 2209 won't have to struggle to work out where a particular piece of information is 2210 coming from. 2211 2130 2212 Models across files 2131 2213 =================== django/branches/queryset-refactor/tests/modeltests/model_inheritance/models.py
r7126 r7141 21 21 class Meta: 22 22 abstract = True 23 ordering = ['name'] 23 24 24 25 def __unicode__(self): … … 30 31 class Student(CommonInfo): 31 32 school_class = models.CharField(max_length=10) 33 34 class Meta: 35 pass 32 36 33 37 class Place(models.Model): … … 38 42 return u"%s the place" % self.name 39 43 40 class Restaurant(Place): 44 class Rating(models.Model): 45 rating = models.IntegerField(null=True, blank=True) 46 47 class Meta: 48 abstract = True 49 50 class Restaurant(Place, Rating): 41 51 serves_hot_dogs = models.BooleanField() 42 52 serves_pizza = models.BooleanField() … … 72 82 >>> w = Worker(name='Fred', age=35, job='Quarry worker') 73 83 >>> w.save() 84 >>> w2 = Worker(name='Barney', age=34, job='Quarry worker') 85 >>> w2.save() 74 86 >>> s = Student(name='Pebbles', age=5, school_class='1B') 75 87 >>> s.save() … … 79 91 u'Student Pebbles' 80 92 93 # The children inherit the Meta class of their parents (if they don't specify 94 # their own). 95 >>> Worker.objects.values('name') 96 [{'name': u'Barney'}, {'name': u'Fred'}] 97 98 # Since Student does not subclass CommonInfo's Meta, it has the effect of 99 # completely overriding it. So ordering by name doesn't take place for Students. 100 >>> Student._meta.ordering 101 [] 102 81 103 # However, the CommonInfo class cannot be used as a normal model (it doesn't 82 104 # exist as a model). … … 97 119 98 120 Test constructor for Restaurant. 99 >>> r = Restaurant(name='Demon Dogs', address='944 W. Fullerton', serves_hot_dogs=True, serves_pizza=False)121 >>> r = Restaurant(name='Demon Dogs', address='944 W. Fullerton',serves_hot_dogs=True, serves_pizza=False, rating=2) 100 122 >>> r.save() 101 123 … … 107 129 # order. 108 130 >>> [f.name for f in Restaurant._meta.fields] 109 ['id', 'name', 'address', 'place_ptr', ' serves_hot_dogs', 'serves_pizza']131 ['id', 'name', 'address', 'place_ptr', 'rating', 'serves_hot_dogs', 'serves_pizza'] 110 132 >>> [f.name for f in ItalianRestaurant._meta.fields] 111 ['id', 'name', 'address', 'place_ptr', ' serves_hot_dogs', 'serves_pizza', 'restaurant_ptr', 'serves_gnocchi']133 ['id', 'name', 'address', 'place_ptr', 'rating', 'serves_hot_dogs', 'serves_pizza', 'restaurant_ptr', 'serves_gnocchi'] 112 134 113 135 # Even though p.supplier for a Place 'p' (a parent of a Supplier), a Restaurant … … 119 141 Traceback (most recent call last): 120 142 ... 121 TypeError: Cannot resolve keyword 'supplier' into field. Choices are: address, id, italianrestaurant, lot, name, place_ptr, provider, serves_hot_dogs, serves_pizza143 TypeError: Cannot resolve keyword 'supplier' into field. Choices are: address, id, italianrestaurant, lot, name, place_ptr, provider, rating, serves_hot_dogs, serves_pizza 122 144 123 145 # Parent fields can be used directly in filters on the child model.
