Permalink
Browse files

Fixed #8533: restored model inheritance docs, and updated one-to-one …

…docs to match

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8757 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 27f9b96 commit 78ca7a6c073e07f311e37b45d8dfe7fce7465e06 @ubernostrum ubernostrum committed Aug 31, 2008
Showing with 285 additions and 6 deletions.
  1. +27 −5 docs/ref/models/fields.txt
  2. +9 −1 docs/ref/models/options.txt
  3. +249 −0 docs/topics/db/models.txt
View
@@ -811,7 +811,10 @@ define the details of how the relation works.
The name to use for the relation from the related object back to this one.
See the :ref:`related objects documentation <backwards-related-objects>` for
- a full explanation and example.
+ a full explanation and example. Note that you must set this value
+ when defining relations on :ref:`abstract models
+ <abstract-base-classes>`; and when you do so
+ :ref:`some special syntax <abstract-related-name>` is available.
.. attribute:: ForeignKey.to_field
@@ -883,8 +886,27 @@ that control how the relationship functions.
``OneToOneField``
-----------------
-.. class:: OneToOneField(othermodel, [**options])
+.. class:: OneToOneField(othermodel, [parent_link=False, **options])
-The semantics of one-to-one relationships will be changing soon, so we don't
-recommend you use them. If that doesn't scare you away, however,
-:class:`OneToOneField` takes the same options that :class:`ForeignKey` does.
+A one-to-one relationship. Conceptually, this is similar to a
+:class:`ForeignKey` with :attr:`unique=True <Field.unique>`, but the
+"reverse" side of the relation will directly return a single object.
+
+This is most useful as the primary key of a model which "extends"
+another model in some way; :ref:`multi-table-inheritance` is
+implemented by adding an implicit one-to-one relation from the child
+model to the parent model, for example.
+
+One positional argument is required: the class to which the model will
+be related.
+
+Additionally, ``OneToOneField`` accepts all of the extra arguments
+accepted by :class:`ForeignKey`, plus one extra argument:
+
+.. attribute: OneToOneField.parent_link
+
+ When ``True`` and used in a model which inherits from another
+ (concrete) model, indicates that this field should be used as the
+ link back to the parent class, rather than the extra
+ ``OneToOneField`` which would normally be implicitly created by
+ subclassing.
@@ -4,13 +4,21 @@
Model ``Meta`` options
======================
-This document explains all the possible :ref:`metadata options <meta-options>` that you can give your model in its internal ``class Meta``.
+This document explains all the possible :ref:`metadata options <meta-options>` that you can give your model in its internal
+``class Meta``.
Available ``Meta`` options
==========================
.. currentmodule:: django.db.models
+``abstract``
+------------
+
+.. attribute:: Options.abstract
+
+If ``True``, this model will be an :ref:`abstract base class <abstract-base-classes>`.
+
``db_table``
------------
View
@@ -747,3 +747,252 @@ bindings. This is for the sake of consistency and sanity.)
A final note: If all you want to do is a custom ``WHERE`` clause, you can use
the :meth:`~QuerySet.extra` lookup method, which lets you add custom SQL to a
query.
+
+.. _model-inheritance:
+
+Model inheritance
+=================
+
+**New in Django development version**
+
+Model inheritance in Django works almost identically to the way normal
+class inheritance works in Python. The only decision you have to make
+is whether you want the parent models to be models in their own right
+(with their own database tables), or if the parents are just holders
+of common information that will only be visible through the child
+models.
+
+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
+application entirely), or want each model to have its own database
+table, :ref:`multi-table-inheritance` is the way to go.
+
+.. _abstract-base-classes:
+
+Abstract base classes
+---------------------
+
+Abstract base classes are useful when you want to put some common
+information into a number of other models. You write your base class
+and put ``abstract=True`` in the :ref:`Meta <meta-options>`
+class. This model will then not be used to create any database
+table. Instead, when it is used as a base class for other models, its
+fields will be added to those of the child class. It is an error to
+have fields in the abstract base class with the same name as those in
+the child (and Django will raise an exception).
+
+An example::
+
+ class CommonInfo(models.Model):
+ name = models.CharField(max_length=100)
+ age = models.PositiveIntegerField()
+
+ class Meta:
+ abstract = True
+
+ class Student(CommonInfo):
+ home_group = models.CharField(max_length=5)
+
+The ``Student`` model will have three fields: ``name``, ``age`` and
+``home_group``. The ``CommonInfo`` model cannot be used as a normal Django
+model, since it is an abstract base class. It does not generate a database
+table or have a manager, and cannot be instantiated or saved directly.
+
+For many uses, this type of model inheritance will be exactly what you want.
+It provides a way to factor out common information at the Python level, whilst
+still only creating one database table per child model at the database level.
+
+``Meta`` inheritance
+~~~~~~~~~~~~~~~~~~~~
+
+When an abstract base class is created, Django makes any :ref:`Meta <meta-options>`
+inner class you declared on the base class available as an
+attribute. If a child class does not declared its own :ref:`Meta <meta-options>`
+class, it will inherit the parent's :ref:`Meta <meta-options>`. If the child wants to
+extend the parent's :ref:`Meta <meta-options>` class, it can subclass it. For example::
+
+ class CommonInfo(models.Model):
+ ...
+ class Meta:
+ abstract = True
+ ordering = ['name']
+
+ class Student(CommonInfo):
+ ...
+ class Meta(CommonInfo.Meta):
+ db_table = 'student_info'
+
+Django does make one adjustment to the :ref:`Meta <meta-options>` class of an abstract base
+class: before installing the :ref:`Meta <meta-options>` attribute, it sets ``abstract=False``.
+This means that children of abstract base classes don't automatically become
+abstract classes themselves. Of course, you can make an abstract base class
+that inherits from another abstract base class. You just need to remember to
+explicitly set ``abstract=True`` each time.
+
+Some attributes won't make sense to include in the :ref:`Meta <meta-options>` class of an
+abstract base class. For example, including ``db_table`` would mean that all
+the child classes (the ones that don't specify their own :ref:`Meta <meta-options>`) would use
+the same database table, which is almost certainly not what you want.
+
+.. _abstract-related-name:
+
+Be careful with ``related_name``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are using the :attr:`~django.db.models.ForeignKey.related_name` attribute on a ``ForeignKey`` or
+``ManyToManyField``, you must always specify a *unique* reverse name for the
+field. This would normally cause a problem in abstract base classes, since the
+fields on this class are included into each of the child classes, with exactly
+the same values for the attributes (including :attr:`~django.db.models.ForeignKey.related_name`) each time.
+
+To work around this problem, when you are using :attr:`~django.db.models.ForeignKey.related_name` in an
+abstract base class (only), part of the name should be the string
+``'%(class)s'``. This is replaced by the lower-cased name of the child class
+that the field is used in. Since each class has a different name, each related
+name will end up being different. For example::
+
+ class Base(models.Model):
+ m2m = models.ManyToMany(OtherModel, related_name="%(class)s_related")
+
+ class Meta:
+ abstract = True
+
+ class ChildA(Base):
+ pass
+
+ class ChildB(Base):
+ pass
+
+The reverse name of the ``ChildA.m2m`` field will be ``childa_related``,
+whilst the reverse name of the ``ChildB.m2m`` field will be
+``childb_related``. It is up to you how you use the ``'%(class)s'`` portion to
+construct your related name, but if you forget to use it, Django will raise
+errors when you validate your models (or run :djadmin:`syncdb`).
+
+If you don't specify a :attr:`~django.db.models.ForeignKey.related_name` attribute for a field in an
+abstract base class, the default reverse name will be the name of the
+child class followed by ``'_set'``, just as it normally would be if
+you'd declared the field directly on the child class. For example, in
+the above code, if the :attr:`~django.db.models.ForeignKey.related_name` attribute was omitted, the
+reverse name for the ``m2m`` field would be ``childa_set`` in the
+``ChildA`` case and ``childb_set`` for the ``ChildB`` field.
+
+.. _multi-table-inheritance:
+
+Multi-table inheritance
+-----------------------
+
+The second type of model inheritance supported by Django is when each model in
+the hierarchy is a model all by itself. Each model corresponds to its own
+database table and can be queried and created indvidually. The inheritance
+relationship introduces links between the child model and each of its parents
+(via an automatically-created :class`~django.db.models.fields.OneToOneField`).
+For example::
+
+ class Place(models.Model):
+ name = models.CharField(max_length=50)
+ address = models.CharField(max_length=80)
+
+ class Restaurant(Place):
+ serves_hot_dogs = models.BooleanField()
+ serves_pizza = models.BooleanField()
+
+All of the fields of ``Place`` will also be available in ``Restaurant``,
+although the data will reside in a different database table. So these are both
+possible::
+
+ >>> Place.objects.filter(name="Bob's Cafe")
+ >>> Restaurant.objects.filter(name="Bob's Cafe")
+
+If you have a ``Place`` that is also a ``Restaurant``, you can get from the
+``Place`` object to the ``Restaurant`` object by using the lower-case version
+of the model name::
+
+ >>> p = Place.objects.filter(name="Bob's Cafe")
+ # If Bob's Cafe is a Restaurant object, this will give the child class:
+ >>> p.restaurant
+ <Restaurant: ...>
+
+However, if ``p`` in the above example was *not* a ``Restaurant`` (it had been
+created directly as a ``Place`` object or was the parent of some other class),
+referring to ``p.restaurant`` would give an error.
+
+``Meta`` and multi-table inheritance
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the multi-table inheritance situation, it doesn't make sense for a child
+class to inherit from its parent's :ref:`Meta <meta-options>` class. All the :ref:`Meta <meta-options>` options
+have already been applied to the parent class and applying them again would
+normally only lead to contradictory behaviour (this is in contrast with the
+abstract base class case, where the base class doesn't exist in its own
+right).
+
+So a child model does not have access to its parent's :ref:`Meta <meta-options>` class. However,
+there are a few limited cases where the child inherits behaviour from the
+parent: if the child does not specify an :attr:`django.db.models.Options.ordering` attribute or a
+:attr:`django.db.models.Options.get_latest_by` attribute, it will inherit these from its parent.
+
+If the parent has an ordering and you don't want the child to have any natural
+ordering, you can explicity set it to be empty::
+
+ class ChildModel(ParentModel):
+ ...
+ class Meta:
+ # Remove parent's ordering effect
+ ordering = []
+
+Inheritance and reverse relations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Because multi-table inheritance uses an implicit
+:class:`~django.db.models.fields.OneToOneField` to link the child and
+the parent, it's possible to move from the parent down to the child,
+as in the above example. However, this uses up the name that is the
+default :attr:`~django.db.models.ForeignKey.related_name` value for
+:class:`django.db.models.fields.ForeignKey` and
+:class:`django.db.models.fields.ManyToManyField` relations. If you
+are putting those types of relations on a subclass of another model,
+you **must** specify the
+:attr:`~django.db.models.ForeignKey.related_name` attribute on each
+such field. If you forget, Django will raise an error when you run
+:djadmin:`validate` or :djadmin:`syncdb`.
+
+For example, using the above ``Place`` class again, let's create another
+subclass with a :class:`~django.db.models.fields.ManyToManyField`::
+
+ class Supplier(Place):
+ # Must specify related_name on all relations.
+ customers = models.ManyToManyField(Restaurant,
+ related_name='provider')
+
+
+Specifying the parent link field
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As mentioned, Django will automatically create a
+:class:`~django.db.models.fields.OneToOneField` linking your child
+class back any non-abstract parent models. If you want to control the
+name of the attribute linking back to the parent, you can create your
+own :class:`~django.db.models.fields.OneToOneField` and set
+:attr:`parent_link=True <django.db.models.fields.OneToOneField.parent_link>`
+to indicate that your field is the link back to the parent class.
+
+Multiple inheritance
+--------------------
+
+Just as with Python's subclassing, it's possible for a Django model to inherit
+from multiple parent models. Keep in mind that normal Python name resolution
+rules apply. The first base class that a particular name appears in (e.g.
+:ref:`Meta <meta-options>`) will be the one that is used; for example,
+his means that if multiple parents contain a :ref:`Meta <meta-options>` class, only
+the first one is going to be used, and all others will be ignored.
+
+Generally, you won't need to inherit from multiple parents. The main use-case
+where this is useful is for "mix-in" classes: adding a particular extra
+field or method to every class that inherits the mix-in. Try to keep your
+inheritance hierarchies as simple and straightforward as possible so that you
+won't have to struggle to work out where a particular piece of information is
+coming from.

0 comments on commit 78ca7a6

Please sign in to comment.