Django

Code

Changeset 7141

Show
Ignore:
Timestamp:
02/21/08 19:05:05 (5 months ago)
Author:
mtredinnick
Message:

queryset-refactor: Fixed up and documented Meta-class inheritance.

Should be mostly logical (for versions of "logical" that may require you to be
a Vulcan, admittedly, but that's not entirely my fault).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/queryset-refactor/django/db/models/base.py

    r7137 r7141  
    3636        # Create the class. 
    3737        module = attrs.pop('__module__') 
    38         meta = attrs.pop('Meta', None) 
    3938        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 
    4047        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 
    4661 
    4762        # Do the appropriate setup for any model parents. 
     
    87102                new_class.add_to_class(field.name, field) 
    88103 
    89         if new_class._meta.abstract: 
     104        if abstract: 
    90105            # Abstract base models can't be instantiated and don't appear in 
    91106            # the list of models for an app. We do the final setup for them a 
    92107            # little differently from normal models. 
     108            attr_meta.abstract = False 
     109            new_class.Meta = attr_meta 
    93110            return new_class 
    94111 
  • django/branches/queryset-refactor/django/db/models/options.py

    r7137 r7141  
    5656        # Next, apply any overridden values from 'class Meta'. 
    5757        if self.meta: 
    58             meta_attrs = self.meta.__dict__ 
     58            meta_attrs = self.meta.__dict__.copy() 
    5959            del meta_attrs['__module__'] 
    6060            del meta_attrs['__doc__'] 
  • django/branches/queryset-refactor/docs/model-api.txt

    r7126 r7141  
    20322032================= 
    20332033 
     2034**New in Django development version** 
     2035 
     2036Model inheritance in Django works almost identically to the way normal class 
     2037inheritance works in Python. The only decision you have to make is whether you 
     2038want the parent models to be models in their own right (with their own 
     2039database tables), or if the parents are just holders of common information 
     2040that will only be visible through the child models. 
     2041 
     2042Often, you will just want to use the parent class to hold information that you 
     2043don't want to have to type out for each child model. This class isn't going to 
     2044ever 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 
    20342046Abstract base classes 
    20352047--------------------- 
     
    20642076still only creating one database table per child model at the database level. 
    20652077 
     2078``Meta`` inheritance 
     2079~~~~~~~~~~~~~~~~~~~~ 
     2080 
     2081When an abstract base class is created, Django makes any ``Meta`` inner class 
     2082you declared on the base class available as an attribute. If a child class 
     2083does 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 
     2085subclass 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 
     2098Django does make one adjustment to the ``Meta`` class of an abstract base 
     2099class: before installing the ``Meta`` attribute, it sets ``abstract=False``. 
     2100This means that children of abstract base classes don't automatically become 
     2101abstract classes themselves. Of course, you can make an abstract base class 
     2102that inherits from another abstract base class. You just need to remember to 
     2103explicitly set ``abstract=True`` each time. 
     2104 
     2105Some attributes won't make sense to include in the ``Meta`` class of an 
     2106abstract base class. For example, including ``db_table`` would mean that all 
     2107the child classes (the ones that don't specify their own ``Meta``) would use 
     2108the same database table, which is almost certainly not what you want. 
     2109 
    20662110Multi-table inheritance 
    20672111----------------------- 
     
    21012145referring to ``p.restaurant`` would give an error. 
    21022146 
    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 
     2150In the multi-table inheritance situation, it doesn't make sense for a child 
     2151class to inherit from its parent's ``Meta`` class. All the ``Meta`` options 
     2152have already been applied to the parent class and applying them again would 
     2153normally only lead to contradictory behaviour (this is in contrast with the 
     2154abstract base class case, where the base class doesn't exist in its own 
     2155right). 
     2156 
     2157So a child model does not have access to its parent's ``Meta`` class. However, 
     2158there are a few limited cases where the child inherits behaviour from the 
     2159parent: if the child does not specify an ``ordering`` attribute or a 
     2160``get_latest_by`` attribute, it will inherit these from its parent. 
     2161 
     2162If the parent has an ordering and you don't want the child to have any natural 
     2163ordering, 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 = [] 
    21052170 
    21062171Inheritance and reverse relations 
     
    21282193you're writing your models and pay attention to the error messages. 
    21292194 
     2195Multiple inheritance 
     2196-------------------- 
     2197 
     2198Just as with Python's subclassing, it's possible for a Django model to inherit 
     2199from multiple parent models. Keep in mind that normal Python name resolution 
     2200rules 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 
     2202name once. This means that if multiple parents contain a ``Meta`` class, only 
     2203the first one is going to be used. All others will be ignored. 
     2204 
     2205Generally, you won't need to inherit from multiple parents. The main use-case 
     2206where this is useful is for ''mix-in'' classes: adding a particular extra 
     2207field or method to every class that inherits the mix-in. Try to keep your 
     2208inheritance hierarchies as simple and straightforward as possible so that you 
     2209won't have to struggle to work out where a particular piece of information is 
     2210coming from. 
     2211 
    21302212Models across files 
    21312213=================== 
  • django/branches/queryset-refactor/tests/modeltests/model_inheritance/models.py

    r7126 r7141  
    2121    class Meta: 
    2222        abstract = True 
     23        ordering = ['name'] 
    2324 
    2425    def __unicode__(self): 
     
    3031class Student(CommonInfo): 
    3132    school_class = models.CharField(max_length=10) 
     33 
     34    class Meta: 
     35        pass 
    3236 
    3337class Place(models.Model): 
     
    3842        return u"%s the place" % self.name 
    3943 
    40 class Restaurant(Place): 
     44class Rating(models.Model): 
     45    rating = models.IntegerField(null=True, blank=True) 
     46 
     47    class Meta: 
     48        abstract = True 
     49 
     50class Restaurant(Place, Rating): 
    4151    serves_hot_dogs = models.BooleanField() 
    4252    serves_pizza = models.BooleanField() 
     
    7282>>> w = Worker(name='Fred', age=35, job='Quarry worker') 
    7383>>> w.save() 
     84>>> w2 = Worker(name='Barney', age=34, job='Quarry worker') 
     85>>> w2.save() 
    7486>>> s = Student(name='Pebbles', age=5, school_class='1B') 
    7587>>> s.save() 
     
    7991u'Student Pebbles' 
    8092 
     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 
    81103# However, the CommonInfo class cannot be used as a normal model (it doesn't 
    82104# exist as a model). 
     
    97119 
    98120Test 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
    100122>>> r.save() 
    101123 
     
    107129# order. 
    108130>>> [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'] 
    110132>>> [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'] 
    112134 
    113135# Even though p.supplier for a Place 'p' (a parent of a Supplier), a Restaurant 
     
    119141Traceback (most recent call last): 
    120142    ... 
    121 TypeError: Cannot resolve keyword 'supplier' into field. Choices are: address, id, italianrestaurant, lot, name, place_ptr, provider, serves_hot_dogs, serves_pizza 
     143TypeError: Cannot resolve keyword 'supplier' into field. Choices are: address, id, italianrestaurant, lot, name, place_ptr, provider, rating, serves_hot_dogs, serves_pizza 
    122144 
    123145# Parent fields can be used directly in filters on the child model.