Index: django/db/models/sql/query.py
===================================================================
--- django/db/models/sql/query.py	(revision 9904)
+++ django/db/models/sql/query.py	(working copy)
@@ -639,8 +639,13 @@
             try:
                 alias = seen[model]
             except KeyError:
-                alias = self.join((table_alias, model._meta.db_table,
-                        root_pk, model._meta.pk.column))
+                #  Don't create a join if the subclass is using the
+                #  same table as its superclass.
+                if table_alias == model._meta.db_table and root_pk == model._meta.pk.column:
+                    alias = table_alias
+                else:
+                    alias = self.join((table_alias, model._meta.db_table,
+                            root_pk, model._meta.pk.column))
                 seen[model] = alias
             if as_pairs:
                 result.append((alias, field.column))
@@ -1509,10 +1514,13 @@
                                 (id(opts), lhs_col), ()))
                         dupe_set.add((opts, lhs_col))
                     opts = int_model._meta
-                    alias = self.join((alias, opts.db_table, lhs_col,
-                            opts.pk.column), exclusions=exclusions)
-                    joins.append(alias)
-                    exclusions.add(alias)
+                    #  Don't create a join if the subclass is using the
+                    #  same table as its superclass.
+                    if alias != opts.db_table or lhs_col != opts.pk.column:
+                        alias = self.join((alias, opts.db_table, lhs_col,
+                                opts.pk.column), exclusions=exclusions)
+                        joins.append(alias)
+                        exclusions.add(alias)
                     for (dupe_opts, dupe_col) in dupe_set:
                         self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
             cached_data = opts._join_cache.get(name)
Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 9904)
+++ django/db/models/base.py	(working copy)
@@ -79,24 +79,42 @@
         for obj_name, obj in attrs.items():
             new_class.add_to_class(obj_name, obj)
 
+        # All the fields of any type declared on this model
+        new_fields = new_class._meta.local_fields + \
+                     new_class._meta.local_many_to_many + \
+                     new_class._meta.virtual_fields
+        field_names = set([f.name for f in new_fields])
+
         # Do the appropriate setup for any model parents.
         o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
                 if isinstance(f, OneToOneField)])
+
+        # Proxy models must have at least one concrete base class.
+        if new_class._meta.proxy:
+            for base in parents:
+                if hasattr(base, '_meta') and not base._meta.abstract:
+                    break
+            else:
+                raise TypeError('Proxy models must have at least one '\
+                                'non-abstract base class')
+
         for base in parents:
             if not hasattr(base, '_meta'):
                 # Things without _meta aren't functional models, so they're
                 # uninteresting parents.
                 continue
 
-            # All the fields of any type declared on this model
-            new_fields = new_class._meta.local_fields + \
-                         new_class._meta.local_many_to_many + \
-                         new_class._meta.virtual_fields
-            field_names = set([f.name for f in new_fields])
-
             if not base._meta.abstract:
                 # Concrete classes...
-                if base in o2o_map:
+                if new_class._meta.proxy:
+                    if new_fields:
+                        raise FieldError('Proxy models cannot define '\
+                                         'new fields')
+                    field = base._meta.pk
+                    new_class._meta.managed = False
+                    new_class._meta.db_table = base._meta.db_table
+                    new_class._meta.pk = field
+                elif base in o2o_map:
                     field = o2o_map[base]
                     field.primary_key = True
                     new_class._meta.setup_pk(field)
@@ -110,6 +128,9 @@
             else:
                 # .. and abstract ones.
 
+                if new_class._meta.proxy:
+                    raise TypeError('Proxy models cannot have '\
+                                    'abstract base classes')
                 # Check for clashes between locally declared fields and those
                 # on the ABC.
                 parent_fields = base._meta.local_fields + base._meta.local_many_to_many
Index: django/db/models/options.py
===================================================================
--- django/db/models/options.py	(revision 9904)
+++ django/db/models/options.py	(working copy)
@@ -21,7 +21,7 @@
 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
                  'unique_together', 'permissions', 'get_latest_by',
                  'order_with_respect_to', 'app_label', 'db_tablespace',
-                 'abstract')
+                 'abstract', 'proxy')
 
 class Options(object):
     def __init__(self, meta, app_label=None):
@@ -42,6 +42,7 @@
         self.pk = None
         self.has_auto_field, self.auto_field = False, None
         self.abstract = False
+        self.proxy = False
         self.parents = SortedDict()
         self.duplicate_targets = {}
         # Managers that have been inherited from abstract base classes. These

Property changes on: tests/modeltests/proxy_models
___________________________________________________________________
Added: svn:mergeinfo

Index: tests/modeltests/proxy_models/__init__.py
===================================================================
--- tests/modeltests/proxy_models/__init__.py	(revision 0)
+++ tests/modeltests/proxy_models/__init__.py	(revision 0)
@@ -0,0 +1,2 @@
+
+
Index: tests/modeltests/proxy_models/models.py
===================================================================
--- tests/modeltests/proxy_models/models.py	(revision 0)
+++ tests/modeltests/proxy_models/models.py	(revision 0)
@@ -0,0 +1,128 @@
+"""
+xx. proxy_models
+ 
+By specifying the 'proxy' Meta attribute, model subclasses can specify that
+they will take data directly from their base class's table rather than using
+a new table of their own. This allows them to act as simple proxies, providing
+a modified interface to the data from the base class.
+""" 
+ 
+from django.db import models 
+ 
+
+class Person(models.Model): 
+    """A simple concrete base class."""
+    name = models.CharField(max_length=50)
+
+
+class Abstract(models.Model):
+    """A simple abstract base class, to be used for error checking."""
+    class Meta:
+        abstract = True
+
+
+class Value(Abstract):
+    """A second concrente base class, for testing multiple inheritance."""
+    value = models.CharField(max_length=10)
+    def __unicode__(self):
+        return self.value
+
+
+class MyPerson(Person):
+    """A proxy subclass, this should not get a new table."""
+    class Meta:
+        proxy = True
+    def has_special_name(self):
+        """Check whether this person has a special name."""
+        if self.name.lower() == "special":
+            return True
+        return False
+        
+
+class StatusPerson(MyPerson):
+    """A non-proxy subclass of a proxy, it should get a new table."""
+    status = models.CharField(max_length=80)
+
+
+class ValuePerson(Person,Value):
+    """Multiple-inheritance proxy subclass."""
+    class Meta:
+        proxy = True
+
+
+__test__ = {'API_TESTS' : """ 
+# The MyPerson class should be using the main Person table
+>>> MyPerson._meta.db_table == Person._meta.db_table
+True
+ 
+# The StatusPerson class should get its own table
+>>> StatusPerson._meta.db_table != Person._meta.db_table
+True
+ 
+# Creating a Person makes them accessable through the MyPerson proxy
+>>> Person(name="Foo McBar").save()
+>>> len(Person.objects.all())
+1
+>>> len(MyPerson.objects.all())
+1
+>>> MyPerson.objects.get(name="Foo McBar").id
+1
+>>> MyPerson.objects.get(id=1).has_special_name()
+False
+
+# But not through the StatusPerson subclass
+>>> StatusPerson.objects.all()
+[]
+
+# A new MyPerson also shows up as a standard Person
+>>> MyPerson(name="Bazza del Frob").save()
+>>> len(MyPerson.objects.all())
+2
+>>> len(Person.objects.all())
+2
+
+# Since these people don't have a corresponding record in the Value table,
+# they are ignored by the ValuePerson proxy
+>>> ValuePerson.objects.all()
+[]
+
+# But if we insert a matching value record, they will show up
+>>> Value.objects.create(id=1,value="fourty-two")
+<Value: fourty-two>
+>>> len(Value.objects.all())
+1
+>>> len(ValuePerson.objects.all())
+1
+>>> ValuePerson.objects.get(id=1).name
+u"Foo McBar"
+>>> ValuePerson.objects.get(id=1).value
+u"fourty-two"
+
+# And now for some things that shouldn't work...
+#
+# All base classes must be non-abstract
+>>> class NoAbstract(Person,Abstract):
+...     class Meta:
+...         proxy = True
+Traceback (most recent call last):
+    ....
+TypeError: Proxy models cannot have abstract base classes
+
+# The proxy must actually have at least one concrete base class
+>>> class NoBaseClasses(models.Model):
+...     class Meta:
+...         proxy = True
+Traceback (most recent call last):
+    ....
+TypeError: Proxy models must have at least one non-abstract base class
+
+# A proxy cannot introduce any new fields
+>>> class NoNewFields(Person):
+...     class Meta:
+...         proxy = True
+...     newfield = models.BooleanField()
+Traceback (most recent call last):
+    ....
+FieldError: Proxy models cannot define new fields
+"""} 
+
Index: docs/topics/db/models.txt
===================================================================
--- docs/topics/db/models.txt	(revision 9904)
+++ docs/topics/db/models.txt	(working copy)
@@ -776,11 +776,16 @@
 Often, you will just want to use the parent class to hold information
 that you don't want to have to type out for each child model. This
 class isn't going to ever be used in isolation, so
-:ref:`abstract-base-classes` are what you're after. However, if you're
-subclassing an existing model (perhaps something from another
+:ref:`abstract-base-classes` are what you're after. If you're
+adding information to an existing model (perhaps something from another
 application entirely), or want each model to have its own database
-table, :ref:`multi-table-inheritance` is the way to go.
+table, :ref:`multi-table-inheritance` is the way to go.  Finally, if you
+want to modify the way a model behaves but are not storing any additional
+model data, you can make your subclass a :ref:`Proxy model <proxy-models>`
+to avoid creating a new database table.
 
+
+
 .. _abstract-base-classes:
 
 Abstract base classes
@@ -990,6 +995,50 @@
 :attr:`parent_link=True <django.db.models.fields.OneToOneField.parent_link>`
 to indicate that your field is the link back to the parent class.
 
+.. _proxy-models:
+
+Proxy models
+------------
+
+.. versionadded:: 1.1
+
+When using :ref:`multi-table inheritance <multi-table-inheritance>`, a new
+database table is created for each subclass of a model.  This is usually the
+desired behaviour, since the subclass needs a place to store any additional
+data fields that are not present on the base class.  However, it is also useful
+to be able to subclass a model *without* introducing a new database table.
+This is what proxy models are designed to achieve.
+
+For example, suppose we want to add a method to the standard ``User`` model that
+will look up some additional information from another source.  This does not
+require a new database table - rather, we want to access the entries in the
+standard user table through a customised interface.  We would mark our
+``User`` subclass as a proxy model as follows::
+
+    class MyUser(User):
+
+        def fetch_additional_data(self):
+            # ...fetch some additional data...
+            return data
+
+        class Meta:
+            proxy = True
+
+The ``MyUser`` class would then operate on the same database table as its
+parent ``User`` class.  In particular, any new instances of ``User`` will
+also be accessible through ``MyUser``, and vice-versa::
+
+    >>> u = User.objects.create(username="foobar")
+    >>> MyUser.objects.get(username="foobar")
+    <MyUser: foobar>
+
+Since no database table is created, proxy models cannot define any
+additional data fields and do not get an automatic ``OneToOneField`` linking to
+their parent class.  In all other respects (e.g. inheritance of the 
+:ref:`Meta <meta-options>` class) they behave identically to standard
+:ref:`multi-table inheritance <multi-table-inheritance>` subclasses.
+
+
 Multiple inheritance
 --------------------
 
Index: docs/ref/models/options.txt
===================================================================
--- docs/ref/models/options.txt	(revision 9904)
+++ docs/ref/models/options.txt	(working copy)
@@ -19,6 +19,16 @@
 
 If ``True``, this model will be an :ref:`abstract base class <abstract-base-classes>`.
 
+``proxy``
+-----------------
+
+.. attribute:: Options.proxy
+
+.. versionadded:: 1.1
+
+If ``True``, this model will be a :ref:`proxy model <proxy-models>`.
+
+
 ``db_table``
 ------------
 
