Ticket #10356: proxy_models.diff
File proxy_models.diff, 13.2 KB (added by , 16 years ago) |
---|
-
django/db/models/sql/query.py
639 639 try: 640 640 alias = seen[model] 641 641 except KeyError: 642 alias = self.join((table_alias, model._meta.db_table, 643 root_pk, model._meta.pk.column)) 642 # Don't create a join if the subclass is using the 643 # same table as its superclass. 644 if table_alias == model._meta.db_table and root_pk == model._meta.pk.column: 645 alias = table_alias 646 else: 647 alias = self.join((table_alias, model._meta.db_table, 648 root_pk, model._meta.pk.column)) 644 649 seen[model] = alias 645 650 if as_pairs: 646 651 result.append((alias, field.column)) … … 1509 1514 (id(opts), lhs_col), ())) 1510 1515 dupe_set.add((opts, lhs_col)) 1511 1516 opts = int_model._meta 1512 alias = self.join((alias, opts.db_table, lhs_col, 1513 opts.pk.column), exclusions=exclusions) 1514 joins.append(alias) 1515 exclusions.add(alias) 1517 # Don't create a join if the subclass is using the 1518 # same table as its superclass. 1519 if alias != opts.db_table or lhs_col != opts.pk.column: 1520 alias = self.join((alias, opts.db_table, lhs_col, 1521 opts.pk.column), exclusions=exclusions) 1522 joins.append(alias) 1523 exclusions.add(alias) 1516 1524 for (dupe_opts, dupe_col) in dupe_set: 1517 1525 self.update_dupe_avoidance(dupe_opts, dupe_col, alias) 1518 1526 cached_data = opts._join_cache.get(name) -
django/db/models/base.py
79 79 for obj_name, obj in attrs.items(): 80 80 new_class.add_to_class(obj_name, obj) 81 81 82 # All the fields of any type declared on this model 83 new_fields = new_class._meta.local_fields + \ 84 new_class._meta.local_many_to_many + \ 85 new_class._meta.virtual_fields 86 field_names = set([f.name for f in new_fields]) 87 82 88 # Do the appropriate setup for any model parents. 83 89 o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields 84 90 if isinstance(f, OneToOneField)]) 91 92 # Proxy models must have at least one concrete base class. 93 if new_class._meta.proxy: 94 for base in parents: 95 if hasattr(base, '_meta') and not base._meta.abstract: 96 break 97 else: 98 raise TypeError('Proxy models must have at least one '\ 99 'non-abstract base class') 100 85 101 for base in parents: 86 102 if not hasattr(base, '_meta'): 87 103 # Things without _meta aren't functional models, so they're 88 104 # uninteresting parents. 89 105 continue 90 106 91 # All the fields of any type declared on this model92 new_fields = new_class._meta.local_fields + \93 new_class._meta.local_many_to_many + \94 new_class._meta.virtual_fields95 field_names = set([f.name for f in new_fields])96 97 107 if not base._meta.abstract: 98 108 # Concrete classes... 99 if base in o2o_map: 109 if new_class._meta.proxy: 110 if new_fields: 111 raise FieldError('Proxy models cannot define '\ 112 'new fields') 113 field = base._meta.pk 114 new_class._meta.managed = False 115 new_class._meta.db_table = base._meta.db_table 116 new_class._meta.pk = field 117 elif base in o2o_map: 100 118 field = o2o_map[base] 101 119 field.primary_key = True 102 120 new_class._meta.setup_pk(field) … … 110 128 else: 111 129 # .. and abstract ones. 112 130 131 if new_class._meta.proxy: 132 raise TypeError('Proxy models cannot have '\ 133 'abstract base classes') 113 134 # Check for clashes between locally declared fields and those 114 135 # on the ABC. 115 136 parent_fields = base._meta.local_fields + base._meta.local_many_to_many -
django/db/models/options.py
21 21 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 22 22 'unique_together', 'permissions', 'get_latest_by', 23 23 'order_with_respect_to', 'app_label', 'db_tablespace', 24 'abstract' )24 'abstract', 'proxy') 25 25 26 26 class Options(object): 27 27 def __init__(self, meta, app_label=None): … … 42 42 self.pk = None 43 43 self.has_auto_field, self.auto_field = False, None 44 44 self.abstract = False 45 self.proxy = False 45 46 self.parents = SortedDict() 46 47 self.duplicate_targets = {} 47 48 # Managers that have been inherited from abstract base classes. These -
tests/modeltests/proxy_models/__init__.py
Property changes on: tests/modeltests/proxy_models ___________________________________________________________________ Added: svn:mergeinfo
1 2 -
tests/modeltests/proxy_models/models.py
1 """ 2 xx. proxy_models 3 4 By specifying the 'proxy' Meta attribute, model subclasses can specify that 5 they will take data directly from their base class's table rather than using 6 a new table of their own. This allows them to act as simple proxies, providing 7 a modified interface to the data from the base class. 8 """ 9 10 from django.db import models 11 12 13 class Person(models.Model): 14 """A simple concrete base class.""" 15 name = models.CharField(max_length=50) 16 17 18 class Abstract(models.Model): 19 """A simple abstract base class, to be used for error checking.""" 20 class Meta: 21 abstract = True 22 23 24 class Value(Abstract): 25 """A second concrente base class, for testing multiple inheritance.""" 26 value = models.CharField(max_length=10) 27 def __unicode__(self): 28 return self.value 29 30 31 class MyPerson(Person): 32 """A proxy subclass, this should not get a new table.""" 33 class Meta: 34 proxy = True 35 def has_special_name(self): 36 """Check whether this person has a special name.""" 37 if self.name.lower() == "special": 38 return True 39 return False 40 41 42 class StatusPerson(MyPerson): 43 """A non-proxy subclass of a proxy, it should get a new table.""" 44 status = models.CharField(max_length=80) 45 46 47 class ValuePerson(Person,Value): 48 """Multiple-inheritance proxy subclass.""" 49 class Meta: 50 proxy = True 51 52 53 __test__ = {'API_TESTS' : """ 54 # The MyPerson class should be using the main Person table 55 >>> MyPerson._meta.db_table == Person._meta.db_table 56 True 57 58 # The StatusPerson class should get its own table 59 >>> StatusPerson._meta.db_table != Person._meta.db_table 60 True 61 62 # Creating a Person makes them accessable through the MyPerson proxy 63 >>> Person(name="Foo McBar").save() 64 >>> len(Person.objects.all()) 65 1 66 >>> len(MyPerson.objects.all()) 67 1 68 >>> MyPerson.objects.get(name="Foo McBar").id 69 1 70 >>> MyPerson.objects.get(id=1).has_special_name() 71 False 72 73 # But not through the StatusPerson subclass 74 >>> StatusPerson.objects.all() 75 [] 76 77 # A new MyPerson also shows up as a standard Person 78 >>> MyPerson(name="Bazza del Frob").save() 79 >>> len(MyPerson.objects.all()) 80 2 81 >>> len(Person.objects.all()) 82 2 83 84 # Since these people don't have a corresponding record in the Value table, 85 # they are ignored by the ValuePerson proxy 86 >>> ValuePerson.objects.all() 87 [] 88 89 # But if we insert a matching value record, they will show up 90 >>> Value.objects.create(id=1,value="fourty-two") 91 <Value: fourty-two> 92 >>> len(Value.objects.all()) 93 1 94 >>> len(ValuePerson.objects.all()) 95 1 96 >>> ValuePerson.objects.get(id=1).name 97 u"Foo McBar" 98 >>> ValuePerson.objects.get(id=1).value 99 u"fourty-two" 100 101 # And now for some things that shouldn't work... 102 # 103 # All base classes must be non-abstract 104 >>> class NoAbstract(Person,Abstract): 105 ... class Meta: 106 ... proxy = True 107 Traceback (most recent call last): 108 .... 109 TypeError: Proxy models cannot have abstract base classes 110 111 # The proxy must actually have at least one concrete base class 112 >>> class NoBaseClasses(models.Model): 113 ... class Meta: 114 ... proxy = True 115 Traceback (most recent call last): 116 .... 117 TypeError: Proxy models must have at least one non-abstract base class 118 119 # A proxy cannot introduce any new fields 120 >>> class NoNewFields(Person): 121 ... class Meta: 122 ... proxy = True 123 ... newfield = models.BooleanField() 124 Traceback (most recent call last): 125 .... 126 FieldError: Proxy models cannot define new fields 127 """} 128 -
docs/topics/db/models.txt
776 776 Often, you will just want to use the parent class to hold information 777 777 that you don't want to have to type out for each child model. This 778 778 class isn't going to ever be used in isolation, so 779 :ref:`abstract-base-classes` are what you're after. However, if you're780 subclassingan existing model (perhaps something from another779 :ref:`abstract-base-classes` are what you're after. If you're 780 adding information to an existing model (perhaps something from another 781 781 application entirely), or want each model to have its own database 782 table, :ref:`multi-table-inheritance` is the way to go. 782 table, :ref:`multi-table-inheritance` is the way to go. Finally, if you 783 want to modify the way a model behaves but are not storing any additional 784 model data, you can make your subclass a :ref:`Proxy model <proxy-models>` 785 to avoid creating a new database table. 783 786 787 788 784 789 .. _abstract-base-classes: 785 790 786 791 Abstract base classes … … 990 995 :attr:`parent_link=True <django.db.models.fields.OneToOneField.parent_link>` 991 996 to indicate that your field is the link back to the parent class. 992 997 998 .. _proxy-models: 999 1000 Proxy models 1001 ------------ 1002 1003 .. versionadded:: 1.1 1004 1005 When using :ref:`multi-table inheritance <multi-table-inheritance>`, a new 1006 database table is created for each subclass of a model. This is usually the 1007 desired behaviour, since the subclass needs a place to store any additional 1008 data fields that are not present on the base class. However, it is also useful 1009 to be able to subclass a model *without* introducing a new database table. 1010 This is what proxy models are designed to achieve. 1011 1012 For example, suppose we want to add a method to the standard ``User`` model that 1013 will look up some additional information from another source. This does not 1014 require a new database table - rather, we want to access the entries in the 1015 standard user table through a customised interface. We would mark our 1016 ``User`` subclass as a proxy model as follows:: 1017 1018 class MyUser(User): 1019 1020 def fetch_additional_data(self): 1021 # ...fetch some additional data... 1022 return data 1023 1024 class Meta: 1025 proxy = True 1026 1027 The ``MyUser`` class would then operate on the same database table as its 1028 parent ``User`` class. In particular, any new instances of ``User`` will 1029 also be accessible through ``MyUser``, and vice-versa:: 1030 1031 >>> u = User.objects.create(username="foobar") 1032 >>> MyUser.objects.get(username="foobar") 1033 <MyUser: foobar> 1034 1035 Since no database table is created, proxy models cannot define any 1036 additional data fields and do not get an automatic ``OneToOneField`` linking to 1037 their parent class. In all other respects (e.g. inheritance of the 1038 :ref:`Meta <meta-options>` class) they behave identically to standard 1039 :ref:`multi-table inheritance <multi-table-inheritance>` subclasses. 1040 1041 993 1042 Multiple inheritance 994 1043 -------------------- 995 1044 -
docs/ref/models/options.txt
19 19 20 20 If ``True``, this model will be an :ref:`abstract base class <abstract-base-classes>`. 21 21 22 ``proxy`` 23 ----------------- 24 25 .. attribute:: Options.proxy 26 27 .. versionadded:: 1.1 28 29 If ``True``, this model will be a :ref:`proxy model <proxy-models>`. 30 31 22 32 ``db_table`` 23 33 ------------ 24 34