diff options
| author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-04-27 02:50:16 +0000 |
|---|---|---|
| committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-04-27 02:50:16 +0000 |
| commit | 9c52d56f6f8a9cdafb231adf9f4110473099c9b5 (patch) | |
| tree | eeded174bec983e4415f5f52f187b3d5d9a1882d /docs | |
| parent | c91a30f00fd182faf8ca5c03cd7dbcf8b735b458 (diff) | |
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/db-api.txt | 436 | ||||
| -rw-r--r-- | docs/model-api.txt | 276 |
2 files changed, 642 insertions, 70 deletions
diff --git a/docs/db-api.txt b/docs/db-api.txt index f8ed16988e..6a3fe88080 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -160,37 +160,6 @@ When you save an object, Django performs the following steps: is used to provide notification that an object has been successfully saved. (These signals are not yet documented.) -Raw saves -~~~~~~~~~ - -**New in Django development version** - -The pre-processing step (#2 in the previous section) is useful, but it modifies -the data stored in a field. This can cause problems if you're relying upon the -data you provide being used as-is. - -For example, if you're setting up conditions for a test, you'll want the test -conditions to be repeatable. If pre-processing is performed, the data used -to specify test conditions may be modified, changing the conditions for the -test each time the test is run. - -In cases such as this, you need to prevent pre-processing from being performed -when you save an object. To do this, you can invoke a **raw save** by passing -``raw=True`` as an argument to the ``save()`` method:: - - b4.save(raw=True) # Save object, but do no pre-processing - -A raw save skips the usual data pre-processing that is performed during the -save. All other steps in the save (pre-save signal, data preparation, data -insertion, and post-save signal) are performed as normal. - -.. admonition:: When to use a raw save - - Generally speaking, you shouldn't need to use a raw save. Disabling field - pre-processing is an extraordinary measure that should only be required - in extraordinary circumstances, such as setting up reliable test - conditions. - Saving changes to objects ========================= @@ -211,11 +180,11 @@ Saving ForeignKey and ManyToManyField fields -------------------------------------------- Updating ``ForeignKey`` fields works exactly the same way as saving a normal -field; simply assign an object of the right type to the field in question:: +field; simply assign an object of the right type to the field in question:: - cheese_blog = Blog.objects.get(name="Cheddar Talk") - entry.blog = cheese_blog - entry.save() + cheese_blog = Blog.objects.get(name="Cheddar Talk") + entry.blog = cheese_blog + entry.save() Updating a ``ManyToManyField`` works a little differently; use the ``add()`` method on the field to add a record to the relation:: @@ -422,6 +391,14 @@ This returns the sixth through tenth objects (``OFFSET 5 LIMIT 5``):: Entry.objects.all()[5:10] +You can also slice from the item ''N'' to the end of the queryset. For +example, to return everything from the fixth item onwards:: + + Entry.objects.all()[5:] + +How this last example is implemented in SQL varies depending upon the database +used, but it is supported in all cases. + Generally, slicing a ``QuerySet`` returns a new ``QuerySet`` -- it doesn't evaluate the query. An exception is if you use the "step" parameter of Python slice syntax. For example, this would actually execute the query in order to @@ -514,15 +491,70 @@ like so:: Note: ``order_by('?')`` queries may be expensive and slow, depending on the database backend you're using. -To order by a field in a different table, add the other table's name and a dot, -like so:: +To order by a field in a different model, use the same syntax as when you are +querying across model relations. That is, the name of the field, followed by a +double underscore (``__``), followed by the name of the field in the new model, +and so on for as many models as you want to join. For example:: + + Entry.objects.order_by('blog__name', 'headline') + +If you try to order by a field that is a relation to another model, Django will +use the default ordering on the related model (or order by the related model's +primary key if there is no ``Meta.ordering`` specified. For example:: + + Entry.objects.order_by('blog') + +...is identical to:: + + Entry.objects.order_by('blog__id') + +...since the ``Blog`` model has no default ordering specified. - Entry.objects.order_by('blogs_blog.name', 'headline') +Be cautious when ordering by fields in related models if you are also using +``distinct()``. See the note in the `distinct()`_ section for an explanation +of how related model ordering can change the expected results. + +It is permissible to specify a multi-valued field to order the results by (for +example, a ``ManyToMany`` field). Normally this won't be a sensible thing to +do and it's really an advanced usage feature. However, if you know that your +queryset's filtering or available data implies that there will only be one +ordering piece of data for each of the main items you are selecting, the +ordering may well be exactly what you want to do. Use ordering on multi-valued +fields with care and make sure the results are what you expect. + +**New in Django development version:** If you don't want any ordering to be +applied to a query, not even the default ordering, call ``order_by()`` with no +parameters. + +**New in Django development version:** The syntax for ordering across related +models has changed. See the `Django 0.96 documentation`_ for the old behaviour. + +.. _Django 0.96 documentation: http://www.djangoproject.com/documentation/0.96/model-api/#floatfield There's no way to specify whether ordering should be case sensitive. With respect to case-sensitivity, Django will order results however your database backend normally orders them. +``reverse()`` +~~~~~~~~~~~~~ + +**New in Django development version** + +If you want to reverse the order in which a queryset's elements are returned, +you can use the ``reverse()`` method. Calling ``reverse()`` a second time +restores the ordering back to the normal direction. + +To retrieve the ''last'' five items in a queryset, you could do this:: + + my_queryset.reverse()[:5] + +Note that this is not quite the same as slicing from the end of a sequence in +Python. The above example will return the last item first, then the +penultimate item and so on. If we had a Python sequence and looked at +``seq[:-5]``, we would see the fifth-last item first. Django doesn't support +that mode of access (slicing from the end), since it is not possible to do it +efficiently in SQL. + ``distinct()`` ~~~~~~~~~~~~~~ @@ -531,10 +563,28 @@ eliminates duplicate rows from the query results. By default, a ``QuerySet`` will not eliminate duplicate rows. In practice, this is rarely a problem, because simple queries such as ``Blog.objects.all()`` -don't introduce the possibility of duplicate result rows. +don't introduce the possibility of duplicate result rows. However, if your +query spans multiple tables, it's possible to get duplicate results when a +``QuerySet`` is evaluated. That's when you'd use ``distinct()``. + +.. note:: + Any fields used in an ``order_by()`` call are included in the SQL + ``SELECT`` columns. This can sometimes lead to unexpected results when + used in conjuntion with ``distinct()``. If you order by fields from a + related model, those fields will be added to the selected columns and they + may make otherwise duplicate rows appear to be distinct. Since the extra + columns don't appear in the returned results (they are only there to + support ordering), it sometimes looks like non-distinct results are being + returned. + + Similarly, if you use a ``values()`` query to restrict the columns + selected, the columns used in any ``order_by()`` (or default model + ordering) will still be involved and may affect uniqueness of the results. -However, if your query spans multiple tables, it's possible to get duplicate -results when a ``QuerySet`` is evaluated. That's when you'd use ``distinct()``. + The moral here is that if you are using ``distinct()`` be careful about + ordering by related models. Similarly, when using ``distinct()`` and + ``values()`` together, be careful when ordering by fields not in the + ``values()`` call. ``values(*fields)`` ~~~~~~~~~~~~~~~~~~~ @@ -569,6 +619,43 @@ Example:: >>> Blog.objects.values('id', 'name') [{'id': 1, 'name': 'Beatles Blog'}] +You can also retrieve values from across ``ForeignKey`` relations by using +double underscores to separate the field names, just as when calling the +``filter()`` command. For example:: + + >>> Entry.objects.values('blog__name').distinct() + [{'name': 'Beatles Blog'}] + +A couple of subtleties that are worth mentioning: + + * The ``values()`` method does not return anything for ``ManyToManyField`` + attributes and will raise an error if you try to pass in this type of + field to it. + * If you have a field called ``foo`` that is a ``ForeignKey``, the default + ``values()`` call will return a dictionary key called ``foo_id``, since + this is the name of the hidden model attribute that stores the actual + value (the ``foo`` attribute refers to the related model). When you are + calling ``values()`` and passing in field names, you can pass in either + ``foo`` or ``foo_id`` and you will get back the same thing (the + dictionary key will match the field name you passed in). + + For example:: + + >>> Entry.objects.values() + [{'blog_id: 1, 'headline': u'First Entry', ...}, ...] + + >>> Entry.objects.values('blog') + [{'blog': 1}, ...] + + >>> Entry.objects.values('blog_id') + [{'blog_id': 1}, ...] + * When using ``values()`` together with ``distinct()``, be aware that + ordering can affect the results. See the note in the `distinct()`_ + section, above, for details. + +**New in Django development version:** Previously, it was not possible to pass +``blog_id`` to ``values()`` in the above example, only ``blog``. + A ``ValuesQuerySet`` is useful when you know you're only going to need values from a small number of the available fields and you won't need the functionality of a model instance object. It's more efficient to select only @@ -586,6 +673,34 @@ followed (optionally) by any output-affecting methods (such as ``values()``), but it doesn't really matter. This is your chance to really flaunt your individualism. +``values_list(*fields)`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +**New in Django development version** + +This is similar to ``values()`` except that instead of returning a list of +dictionaries, it returns a list of tuples. Each tuple contains the value from +the respective field passed into the ``values_list()`` call -- so the first +item is the first field, etc. For example:: + + >>> Entry.objects.values_list('id', 'headling') + [(1, u'First entry'), ...] + +If you only pass in a single field, you can also pass in the ``flat`` +parameter. If ``True``, this will mean the returned results are single values, +rather than one-tuples. An example should make the difference clearer:: + + >>> Entry.objects.values_list('id').order_by('id') + [(1,), (2,), (3,), ...] + + >>> Entry.objects.values_list('id', flat=True).order_by('id') + [1, 2, 3, ...] + +It is an error to pass in ``flat`` when there is more than one field. + +If you don't pass any values to ``values_list()``, it will return all the +fields in the model, in the order they were declared. + ``dates(field, kind, order='ASC')`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -635,6 +750,17 @@ Examples:: >>> Entry.objects.none() [] +``all()`` +~~~~~~~~~~ + +**New in Django development version** + +Returns a ''copy'' of the current ``QuerySet`` (or ``QuerySet`` subclass you +pass in). This can be useful in some situations where you might want to pass +in either a model manager or a ``QuerySet`` and do further filtering on the +result. You can safely call ``all()`` on either object and then you'll +definitely have a ``QuerySet`` to work with. + ``select_related()`` ~~~~~~~~~~~~~~~~~~~~ @@ -687,8 +813,8 @@ related ``Person`` *and* the related ``City``:: p = b.author # Hits the database. c = p.hometown # Hits the database. -Note that ``select_related()`` does not follow foreign keys that have -``null=True``. +Note that, by default, ``select_related()`` does not follow foreign keys that +have ``null=True``. Usually, using ``select_related()`` can vastly improve performance because your app can avoid many database calls. However, in situations with deeply nested @@ -705,8 +831,43 @@ follow:: The ``depth`` argument is new in the Django development version. -``extra(select=None, where=None, params=None, tables=None)`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +**New in Django development version:** Sometimes you only need to access +specific models that are related to your root model, not all of the related +models. In these cases, you can pass the related field names to +``select_related()`` and it will only follow those relations. You can even do +this for models that are more than one relation away by separating the field +names with double underscores, just as for filters. For example, if we have +thise model:: + + class Room(models.Model): + # ... + building = models.ForeignKey(...) + + class Group(models.Model): + # ... + teacher = models.ForeignKey(...) + room = models.ForeignKey(Room) + subject = models.ForeignKey(...) + +...and we only needed to work with the ``room`` and ``subject`` attributes, we +could write this:: + + g = Group.objects.select_related('room', 'subject') + +This is also valid:: + + g = Group.objects.select_related('room__building', 'subject') + +...and would also pull in the ``building`` relation. + +You can only refer to ``ForeignKey`` relations in the list of fields passed to +``select_related``. You *can* refer to foreign keys that have ``null=True`` +(unlike the default ``select_related()`` call). It's an error to use both a +list of fields and the ``depth`` parameter in the same ``select_related()`` +call, since they are conflicting options. + +``extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes, the Django query syntax by itself can't easily express a complex ``WHERE`` clause. For these edge cases, Django provides the ``extra()`` @@ -763,6 +924,21 @@ of the arguments is required, but you should use at least one of them. some database backends, such as some MySQL versions, don't support subqueries. + **New in Django development version** + In some rare cases, you might wish to pass parameters to the SQL fragments + in ``extra(select=...)```. For this purpose, use the ``select_params`` + parameter. Since ``select_params`` is a sequence and the ``select`` + attribute is a dictionary, some care is required so that the parameters + are matched up correctly with the extra select pieces. In this situation, + you should use a ``django.utils.datastructures.SortedDict`` for the + ``select`` value, not just a normal Python dictionary. + + This will work, for example:: + + Blog.objects.extra( + select=SortedDict(('a', '%s'), ('b', '%s')), + select_params=('one', 'two')) + ``where`` / ``tables`` You can define explicit SQL ``WHERE`` clauses -- perhaps to perform non-explicit joins -- by using ``where``. You can manually add tables to @@ -779,20 +955,61 @@ of the arguments is required, but you should use at least one of them. SELECT * FROM blog_entry WHERE id IN (3, 4, 5, 20); + Be careful when using the ``tables`` parameter if you're specifying + tables that are already used in the query. When you add extra tables + via the ``tables`` parameter, Django assumes you want that table included + an extra time, if it is already included. That creates a problem, + since the table name will then be given an alias. If a table appears + multiple times in an SQL statement, the second and subsequent occurrences + must use aliases so the database can tell them apart. If you're + referring to the extra table you added in the extra ``where`` parameter + this is going to cause errors. + + Normally you'll only be adding extra tables that don't already appear in + the query. However, if the case outlined above does occur, there are a few + solutions. First, see if you can get by without including the extra table + and use the one already in the query. If that isn't possible, put your + ``extra()`` call at the front of the queryset construction so that your + table is the first use of that table. Finally, if all else fails, look at + the query produced and rewrite your ``where`` addition to use the alias + given to your extra table. The alias will be the same each time you + construct the queryset in the same way, so you can rely upon the alias + name to not change. + +``order_by`` + If you need to order the resulting queryset using some of the new fields + or tables you have included via ``extra()`` use the ``order_by`` parameter + to ``extra()`` and pass in a sequence of strings. These strings should + either be model fields (as in the normal ``order_by()`` method on + querysets), of the form ``table_name.column_name`` or an alias for a column + that you specified in the ``select`` parameter to ``extra()``. + + For example:: + + q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) + q = q.extra(order_by = ['-is_recent']) + + This would sort all the items for which ``is_recent`` is true to the front + of the result set (``True`` sorts before ``False`` in a descending + ordering). + + This shows, by the way, that you can make multiple calls to + ``extra()`` and it will behave as you expect (adding new constraints each + time). + ``params`` - The ``select`` and ``where`` parameters described above may use standard - Python database string placeholders -- ``'%s'`` to indicate parameters the - database engine should automatically quote. The ``params`` argument is a - list of any extra parameters to be substituted. + The ``where`` parameter described above may use standard Python database + string placeholders -- ``'%s'`` to indicate parameters the database engine + should automatically quote. The ``params`` argument is a list of any extra + parameters to be substituted. Example:: Entry.objects.extra(where=['headline=%s'], params=['Lennon']) - Always use ``params`` instead of embedding values directly into ``select`` - or ``where`` because ``params`` will ensure values are quoted correctly - according to your particular backend. (For example, quotes will be escaped - correctly.) + Always use ``params`` instead of embedding values directly into ``where`` + because ``params`` will ensure values are quoted correctly according to + your particular backend. (For example, quotes will be escaped correctly.) Bad:: @@ -802,8 +1019,9 @@ of the arguments is required, but you should use at least one of them. Entry.objects.extra(where=['headline=%s'], params=['Lennon']) - The combined number of placeholders in the list of strings for ``select`` - or ``where`` should equal the number of values in the ``params`` list. +**New in Django development version** The ``select_params`` argument to +``extra()`` is new. Previously, you could attempt to pass parameters for +``select`` in the ``params`` argument, but it worked very unreliably. QuerySet methods that do not return QuerySets --------------------------------------------- @@ -1031,7 +1249,12 @@ Examples:: SQL equivalents:: SELECT ... WHERE id = 14; - SELECT ... WHERE id = NULL; + SELECT ... WHERE id IS NULL; + +**New in Django development version:** The semantics of ``id__exact=None`` have +changed in the development version. Previously, it was (intentionally) +converted to ``WHERE id = NULL`` at the SQL level, which would never match +anything. It has now been changed to behave the same as ``id__isnull=True``. iexact ~~~~~~ @@ -1261,14 +1484,6 @@ SQL equivalent:: SELECT ... WHERE pub_date IS NULL; -.. admonition:: ``__isnull=True`` vs ``__exact=None`` - - There is an important difference between ``__isnull=True`` and - ``__exact=None``. ``__exact=None`` will *always* return an empty result - set, because SQL requires that no value is equal to ``NULL``. - ``__isnull`` determines if the field is currently holding the value - of ``NULL`` without performing a comparison. - search ~~~~~~ @@ -1368,6 +1583,11 @@ equivalent:: Entry.objects.filter(blog__id=3) # __exact is implied Entry.objects.filter(blog__pk=3) # __pk implies __id__exact +.. note:: + Because of this shortcut, you cannot have a field called ``pk`` that is not + the primary key of the model. It will always be replaced by the name of the + model's primary key in queries. + Lookups that span relationships ------------------------------- @@ -1392,6 +1612,60 @@ whose ``headline`` contains ``'Lennon'``:: Blog.objects.filter(entry__headline__contains='Lennon') +Spanning multi-valued relationships +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**New in Django development version** + +When you are filtering an object based on a ``ManyToManyField`` or a reverse +``ForeignKeyField``, there are two different sorts of filter you may be +interested in. Consider the ``Blog``/``Entry`` relationship (``Blog`` to +``Entry`` is a one-to-many relation). We might be interested in finding blogs +that have an entry which has both *"Lennon"* in the headline and was published +today. Or we might want to find blogs that have an entry with *"Lennon"* in +the headline as well as an entry that was published today. Since there are +multiple entries associated with a single ``Blog``, both of these queries are +possible and make sense in some situations. + +The same type of situation arises with a ``ManyToManyField``. For example, if +an ``Entry`` has a ``ManyToManyField`` called ``tags``, we might want to find +entries linked to tags called *"music"* and *"bands"* or we might want an +entry that contains a tag with a name of *"music"* and a status of *"public"*. + +To handle both of these situations, Django has a consistent way of processing +``filter()`` and ``exclude()`` calls. Everything inside a single ``filter()`` +call is applied simultaneously to filter out items matching all those +requirements. Successive ``filter()`` calls further restrict the set of +objects, but for multi-valued relations, they apply to any object linked to +the primary model, not necessarily those objects that were selected by an +earlier ``filter()`` call. + +That may sound a bit confusing, so hopefully an example will clarify. To +select all blogs that contains entries with *"Lennon"* in the headline and +were published today, we would write:: + + Blog.objects.filter(entry__headline__contains='Lennon', + entry__pub_date=datetime.date.today()) + +To select all blogs that contain an entry with *"Lennon"* in the headline +**as well as** an entry that was published today, we would write:: + + Blog.objects.filter(entry__headline__contains='Lennon').filter( + entry__pub_date=datetime.date.today()) + +In this second example, the first filter restricted the queryset to all those +blogs linked to that particular type of entry. The second filter restricted +the set of blogs *further* to those that are also linked to the second type of +entry. The entries select by the second filter may or may not be the same as +the entries in the first filter. We are filtering the ``Blog`` items with each +filter statement, not the ``Entry`` items. + +All of this behaviour also applies to ``exclude()``: all the conditions in a +single ``exclude()`` statement apply to a single instance (if those conditions +are talking about the same multi-valued relation). Conditions in subsequent +``filter()`` or ``exclude()`` calls that refer to the same relation may end up +filtering on different linked objects. + Escaping percent signs and underscores in LIKE statements --------------------------------------------------------- @@ -1496,6 +1770,12 @@ This is equivalent to the following SQL ``WHERE`` clause:: You can compose statements of arbitrary complexity by combining ``Q`` objects with the ``&`` and ``|`` operators. You can also use parenthetical grouping. +**New in Django development version:** ``Q`` objects can also be negated using +the ``~`` operator, allowing for combined lookups that combine both a normal +query and a negated (``NOT``) query:: + + Q(question__startswith='Who') | ~Q(pub_date__year=2005) + Each lookup function that takes keyword-arguments (e.g. ``filter()``, ``exclude()``, ``get()``) can also be passed one or more ``Q`` objects as positional (not-named) arguments. If you provide multiple ``Q`` object @@ -1815,6 +2095,34 @@ complete query set:: Entry.objects.all().delete() +Updating multiple objects at once +================================= + +**New in Django development version** + +Sometimes you want to set a field to a particular value for all the objects in +a queryset. You can do this with the ``update()`` method. For example:: + + # Update all the headlings to the same value. + Entry.objects.all().update(headline='Everything is the same') + +You can only set non-relation fields and ``ForeignKey`` fields using this +method and the value you set the field to must be a normal Python value (you +can't set a field to be equal to some other field at the moment). + +To update ``ForeignKey`` fields, set the new value to be the new model +instance you want to point to. Example:: + + b = Blog.objects.get(pk=1) + # Make all entries belong to this blog. + Entry.objects.all().update(blog=b) + +The ``update()`` method is applied instantly and doesn't return anything +(similar to ``delete()``). The only restriction on the queryset that is +updated is that it can only access one database table, the model's main +table. So don't try to filter based on related fields or anything like that; +it won't work. + Extra instance methods ====================== diff --git a/docs/model-api.txt b/docs/model-api.txt index f73c5aadf7..a5a2e2932e 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -886,6 +886,10 @@ relationship should work. All are optional: `related objects documentation`_ for a full explanation and example. + If using this in an `abstract base class`_, be + sure to read the `extra notes`_ in that section + about ``related_name``. + ``to_field`` The field on the related object that the relation is to. By default, Django uses the primary key of the related object. @@ -893,6 +897,8 @@ relationship should work. All are optional: .. _`Database API reference`: ../db-api/ .. _related objects documentation: ../db-api/#related-objects +.. _abstract base class: `Abstract base classes`_ +.. _extra notes: `Be careful with related_name`_ Many-to-many relationships ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -988,9 +994,6 @@ the relationship should work. All are optional: One-to-one relationships ~~~~~~~~~~~~~~~~~~~~~~~~ -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, keep reading. - To define a one-to-one relationship, use ``OneToOneField``. You use it just like any other ``Field`` type: by including it as a class attribute of your model. @@ -1012,9 +1015,26 @@ As with ``ForeignKey``, a relationship to self can be defined by using the string ``"self"`` instead of the model name; references to as-yet undefined models can be made by using a string containing the model name. -This ``OneToOneField`` will actually replace the primary key ``id`` field -(since one-to-one relations share the same primary key), and will be displayed -as a read-only field when you edit an object in the admin interface: +Finally, ``OneToOneField`` takes the following extra option: + + ======================= ============================================================ + Argument Description + ======================= ============================================================ + ``parent_link`` When ``True`` and used in a model inherited from + another model, indicates that this field should + be used as the link from the child back to the + parent. See `Model inheritance`_ for more + details. + + **New in Django development version** + + ======================= ============================================================ + +**New in Django development version:** ``OneToOneField`` classes used to +automatically become the primary key on a model. This is no longer true, +although you can manually pass in the ``primary_key`` attribute if you like. +Thus, it's now possible to have multiple fields of type ``OneToOneField`` on a +single model. See the `One-to-one relationship model example`_ for a full example. @@ -1048,6 +1068,14 @@ Model metadata is "anything that's not a field", such as ordering options, etc. Here's a list of all possible ``Meta`` options. No options are required. Adding ``class Meta`` to a model is completely optional. +``abstract`` +------------ + +**New in Django development version** + +When set to ``True``, denotes this model as an abstract base class. See +`Abstract base classes`_ for more details. Defaults to ``False``. + ``db_table`` ------------ @@ -1155,6 +1183,10 @@ together. It's used in the Django admin and is enforced at the database level (i.e., the appropriate ``UNIQUE`` statements are included in the ``CREATE TABLE`` statement). +All the fields specified in ``unique_together`` must be part of the current +model. If you are using `model inheritance`_, you cannot refer to fields from +any parent classes in ``unique_together``. + **New in Django development version** For convenience, unique_together can be a single list when dealing @@ -2041,6 +2073,238 @@ You can also prevent saving:: .. _database API docs: ../db-api/ +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 `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. + +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 ``Meta`` 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 or anything like that. + +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 ``Meta`` inner class +you declared on the base class available as an attribute. If a child class +does not declared its own ``Meta`` class, it will inherit the parent's +``Meta``. If the child wants to extend the parent's ``Meta`` 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 ``Meta`` class of an abstract base +class: before installing the ``Meta`` 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 ``Meta`` 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 ``Meta``) would use +the same database table, which is almost certainly not what you want. + +Be careful with ``related_name`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are using the ``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 ``related_name``) each time. + +To work around this problem, when you are using ``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 ``syncdb``). + +If you don't specify a ``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 ``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 +----------------------- + +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 ``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 ``Meta`` class. All the ``Meta`` 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 ``Meta`` class. However, +there are a few limited cases where the child inherits behaviour from the +parent: if the child does not specify an ``ordering`` attribute or a +``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 ``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 +``related_name`` value for ``ForeignKey`` and ``ManyToManyField`` relations. +If you are putting those type of relations on a subclass of another model, you +**must** specify the ``related_name`` attribute on each such field. If you +forget, Django will raise an error when you run ``manage.py validate`` or try +to syncdb. + +For example, using the above ``Place`` class again, let's create another +subclass with a ``ManyToManyField``:: + + class Supplier(Place): + # Must specify related_name on all relations. + customers = models.ManyToManyField(Restaurant, + related_name='provider') + +For more information about reverse relations, refer to the `Database API +reference`_ . For now, just remember to run ``manage.py validate`` when +you're writing your models and pay attention to the error messages. + +Specifying the parent link field +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As mentioned, Django will automatically create a ``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 +link field and pass it ``parent_link=True``. For example, to explicitly +specify the field that will link ``Supplier`` to ``Place`` in the above +example, you could write:: + + class Supplier(Place): + parent = models.OneToOneField(Place, parent_link=True) + ... + +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. +``Meta``) will be the one that is used. We stop searching once we find the +name once. This means that if multiple parents contain a ``Meta`` class, only +the first one is going to be used. 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. + Models across files =================== |
