Finished proofreading docs/model-api.txt

git-svn-id: http://code.djangoproject.com/svn/django/trunk@2833 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2006-05-05 01:56:29 +00:00
parent a79cbb5fe8
commit a8ccdd0fcd
1 changed files with 235 additions and 140 deletions

View File

@ -1298,16 +1298,22 @@ user searches for ``john lennon``, Django will do the equivalent of this SQL
Managers Managers
======== ========
The Manager is the interface through which database query operations A ``Manager`` is the interface through which database query operations are
are provided to Django applications. At least one Manager exists for provided to Django models. At least one ``Manager`` exists for every model in
every model in a Django application. a Django application.
By default, Django will add a Manager with the name of ``objects`` to The way ``Manager`` classes work is documented in the `Retrieving objects`_
every Django model. However, if you wish to use ``objects`` as a field section of the database API docs, but this section specifically touches on
name, or if you wish to use a name other than ``objects`` for the Manager, model options that customize ``Manager`` behavior.
you can rename the Manager on a per-model basis. To rename the Manager
for a given class, define a class attribute of type models.Manager() Manager names
on that model. For example:: -------------
By default, Django adds a ``Manager`` with the name ``objects`` to every Django
model class. However, if you want to use ``objects`` as a field name, or if you
want to use a name other than ``objects`` for the ``Manager``, you can rename
it on a per-model basis. To rename the ``Manager`` for a given class, define a
class attribute of type ``models.Manager()`` on that model. For example::
from django.db import models from django.db import models
@ -1315,38 +1321,118 @@ on that model. For example::
#... #...
people = models.Manager() people = models.Manager()
In this example, ``Person.objects.all()`` will generate an error, but Using this example model, ``Person.objects`` will generate an
``Person.people.all()`` will provide a list of all ``Person`` objects. ``AttributeError`` exception, but ``Person.people.all()`` will provide a list
of all ``Person`` objects.
Managers can also be customized. This is achieved by extending the Custom Managers
base Manager class, and instantiating the new Manager on your model. ---------------
There are two reasons that you may want to customize a Manager: firstly,
to add utility methods to the Manager, and secondly, to modify the
initial Query Set provided by the Manager.
To modify the initial Query Set provided by a Manager, override the You can use a custom ``Manager`` in a particular model by extending the base
``get_query_set()`` method to return a Query Set with the properties ``Manager`` class and instantiating your custom ``Manager`` in your model.
you require. For example::
class PersonManager(models.Manager): There are two reasons you might want to customize a ``Manager``: to add extra
# Add some custom behavior to the Manager ``Manager`` methods, and/or to modify the initial ``QuerySet`` the ``Manager``
def move_house(self): returns.
# Some logic to help a person move house
# Modify the initial Query Set provided by the manager Adding extra Manager methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adding extra ``Manager`` methods is the preferred way to add "table-level"
functionality to your models. (For "row-level" functionality -- i.e., functions
that act on a single instance of a model object -- use _`Model methods`, not
custom ``Manager`` methods.)
A custom ``Manager`` method can return anything you want. It doesn't have to
return a ``QuerySet``.
For example, this custom ``Manager`` offers a method ``with_counts()``, which
returns a list of all ``OpinionPoll`` objects, each with an extra
``num_responses`` attribute that is the result of an aggregate query::
class PollManager(models.Manager):
def with_counts(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("""
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
GROUP BY 1, 2, 3
ORDER BY 3 DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
p.num_responses = row[3]
result_list.append(p)
return result_list
class OpinionPoll(models.Model):
question = models.CharField(maxlength=200)
poll_date = models.DateField()
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(Poll)
person_name = models.CharField(maxlength=50)
response = models.TextField()
With this example, you'd use ``OpinionPoll.objects.with_counts()`` to return
that list of ``OpinionPoll`` objects with ``num_responses`` attributes.
Another thing to note about this example is that ``Manager`` methods can
access ``self.model`` to get the model class to which they're attached.
Modifying initial Manager QuerySets
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A ``Manager``'s base ``QuerySet`` returns all objects in the system. For
example, using this model::
class Book(models.Model):
title = models.CharField(maxlength=100)
author = models.CharField(maxlength=50)
...the statement ``Book.objects.all()`` will return all books in the database.
You can override a ``Manager``'s base ``QuerySet`` by overriding the
``Manager.get_query_set()`` method. ``get_query_set()`` should return a
``QuerySet`` with the properties you require.
For example, the following model has *two* ``Manager``s -- one that returns
all objects, and one that returns only the books by Roald Dahl::
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_query_set(self): def get_query_set(self):
return super(Manager, self).get_query_set().filter(name__startswith="Fred") return super(Manager, self).get_query_set().filter(author='Roald Dahl')
class Person(models.Model): # Then hook it into the Book model explicitly.
#... class Book(models.Model):
objects = PersonManager() title = models.CharField(maxlength=100)
author = models.CharField(maxlength=50)
In this example, ``Person.objects.all()`` will only return people whose name starts objects = models.Manager() # The default manager.
with "Fred"; ``Person.objects.move_house()`` will also be available. dahl_objects = DahlBookManager() # The Dahl-specific manager.
If required, you can add multiple Managers to a model. Every Manager attribute With this sample model, ``Book.objects.all()`` will return all books in the
added to a model can be accessed and used as a manager. This is an easy way database, but ``Book.dahl_objects.all()`` will only return the ones written by
to define common filters types for your models. For example, the model:: Roald Dahl.
Of course, because ``get_query_set()`` returns a ``QuerySet`` object, you can
use ``filter()``, ``exclude()`` and all the other ``QuerySet`` methods on it.
So these statements are all legal::
Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()
This example also pointed out another interesting technique: using multiple
managers on the same model. You can attach as many ``Manager()`` instances to
a model as you'd like. This is an easy way to define common "filters" for your
models.
For example::
class MaleManager(models.Manager): class MaleManager(models.Manager):
def get_query_set(self): def get_query_set(self):
@ -1357,126 +1443,130 @@ to define common filters types for your models. For example, the model::
return super(Manager, self).get_query_set().filter(sex='F') return super(Manager, self).get_query_set().filter(sex='F')
class Person(models.Model): class Person(models.Model):
#... first_name = models.CharField(maxlength=50)
last_name = models.CharField(maxlength=50)
sex = models.CharField(maxlength=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager() people = models.Manager()
men = MaleManager() men = MaleManager()
women = FemaleManager() women = FemaleManager()
... will allow end users to request ``Person.men.all()``, ``Person.women.all()``, This example allows you to request ``Person.men.all()``, ``Person.women.all()``,
and ``Person.people.all()``, yielding predictable results. and ``Person.people.all()``, yielding predictable results.
If you are going to install a customized Manager, be warned that the first If you use custom ``Manager`` objects, take note that the first ``Manager``
Manager that Django encounters in a model definition has special status. Django encounters (in order by which they're defined in the model) has a
Django interprets the first Manager defined in a class as the default Manager. special status. Django interprets the first ``Manager`` defined in a class as
Certain operations use the default Manager to obtain lists of objects, so it the "default" ``Manager``. Certain operations -- such as Django's admin site --
is generally a good idea for the first Manager to be relatively unfiltered. use the default ``Manager`` to obtain lists of objects, so it's generally a
In the last example, ``people`` is defined first - so the default Manager good idea for the first ``Manager`` to be relatively unfiltered. In the last
will include everyone. example, the ``people`` ``Manager`` is defined first -- so it's the default
``Manager``.
Model methods Model methods
============= =============
There are a number of methods you can define on model objects to control the Define custom methods on a model to add custom "row-level" functionality to
object's behavior. First, any methods you define will be available as methods your objects. Whereas ``Manager`` methods are intended to do "table-wide"
of object instances. For example:: things, model methods should act on a particular model instance.
class Pizza(models.Model): This is a valuable technique for keeping business logic in one place -- the
# ... model.
def is_disgusting(self): For example, this model has a few custom methods::
return "anchovies" in [topping.name for topping in self.toppings.all()]
Now, every ``Pizza`` object will have a ``is_disgusting()`` method. class Person(models.Model):
first_name = models.CharField(maxlength=50)
last_name = models.CharField(maxlength=50)
birth_date = models.DateField()
address = models.CharField(maxlength=100)
city = models.CharField(maxlength=50)
state = models.USStateField() # Yes, this is America-centric...
See `Giving models custom methods`_ for a full example. def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):
return "Baby boomer"
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
return "Post-boomer"
.. _Giving models custom methods: http://www.djangoproject.com/documentation/models/custom_methods/ def is_midwestern(self):
"Returns True if this person is from the Midwest."
return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
The last method in this example is a *property*. `Read more about properties`_.
.. _Read more about properties: http://www.python.org/download/releases/2.2/descrintro/#property
A few object methods have special meaning: A few object methods have special meaning:
``__str__`` ``__str__``
Django uses ``str(obj)`` in a number of places, most notably as the value -----------
inserted into a template when it displays an object. Thus, you should always
return a nice, human-readable string for the object's ``__str__``.
Although defining ``__str__()`` isn't required, it's strongly encouraged. ``__str__()`` is a Python "magic method" that defines what should be returned
if you call ``str()`` on the object. Django uses ``str(obj)`` in a number of
places, most notably as the value displayed to render an object in the Django
admin site and as the value inserted into a template when it displays an
object. Thus, you should always return a nice, human-readable string for the
object's ``__str__``. Although this isn't required, it's strongly encouraged.
See `Adding str`_ for a full example. For example::
.. _Adding str: http://www.djangoproject.com/documentation/models/repr/ class Person(models.Model):
first_name = models.CharField(maxlength=50)
last_name = models.CharField(maxlength=50)
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
``get_absolute_url`` ``get_absolute_url``
Define a ``get_absolute_url`` method to tell Django how to calculate the
URL for an object. For example::
def get_absolute_url(self):
return "/pizzas/%i/" % self.id
Django uses this in its admin interface. If an object defines
``get_absolute_url``, the object detail page will have a "View on site"
link that will jump you directly to the object's public view.
It's good practice to use ``get_absolute_url()`` in templates, instead of
hard-coding your objects' URLs.
Module-level methods
-------------------- --------------------
If you want to add a method to the Model, rather than instances of the model, Define a ``get_absolute_url()`` method to tell Django how to calculate the
you can use the Python ``staticmethod`` and ``classmethod`` operators. For URL for an object. For example::
example::
class Pizza(models.Model): def get_absolute_url(self):
# ... return "/people/%i/" % self.id
def get_pizzas_to_deliver(): Django uses this in its admin interface. If an object defines
return get_list(delivered__exact=False) ``get_absolute_url()``, the object-editing page will have a "View on site"
get_pizzas_to_deliver = staticmethod(get_pizzas_to_deliver) link that will jump you directly to the object's public view, according to
``get_absolute_url()``.
Or, using Python 2.4 decorators:: Also, a couple of other bits of Django, such as the syndication-feed framework,
use ``get_absolute_url()`` as a convenience to reward people who've defined the
method.
# ... It's good practice to use ``get_absolute_url()`` in templates, instead of
@staticmethod hard-coding your objects' URLs. For example, this template code is bad::
def get_pizzas_to_deliver():
# ...
This will make the top-level ``pizzas`` module have a ``get_pizzas_to_deliver()`` <a href="/people/{{ object.id }}/">{{ object.name }}</a>
method::
>>> from pizza_hut.models import Pizza But this template code is good::
>>> Pizza.get_pizzas_to_deliver()
[ ... ]
Manipulator methods <a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
-------------------
(The functionality in this section is going away soon. This documentation is (Yes, we know ``get_absolute_url()`` couples URLs to models, which violates the
provided only for legacy purposes at this point.) DRY principle, because URLs are defined both in a URLconf and in the model.
This is a rare case in which we've intentionally violated that principle for
Similarly, you can add methods to the object's manipulators by defining methods the sake of convenience. With that said, we're working on an even cleaner way
that being with "_manipulator_". This is most useful for providing custom of specifying URLs in a more DRY fashion.)
validators for certain fields, because manipulators automatically call any
method that begins with "validate"::
class Pizza(models.Model):
# ...
def _manipulator_validate_customer_id(self, field_data, all_data):
from django.core import validators
from django.conf.settings import BAD_CUSTOMER_IDS
if int(field_data) in BAD_CUSTOMER_IDS:
raise validators.ValidationError, "We don't deliver to this customer."
Executing custom SQL Executing custom SQL
-------------------- --------------------
Feel free to write custom SQL statements in custom model methods and Feel free to write custom SQL statements in custom model methods and
module-level methods. The object ``django.db.connection`` object represents module-level methods. The object ``django.db.connection`` represents the
the current database connection. To use it, call ``connection.cursor()`` to current database connection. To use it, call ``connection.cursor()`` to get a
get a cursor object. Then, call ``cursor.execute(sql, [params])`` cursor object. Then, call ``cursor.execute(sql, [params])`` to execute the SQL
to execute the SQL and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return the resulting
the resulting rows. Example:: rows. Example::
def my_custom_sql(self): def my_custom_sql(self):
from django.db import connection from django.db import connection
@ -1494,12 +1584,14 @@ via a ``DELETE`` or ``UPDATE`` -- you'll need to call ``db.commit()``. Example::
cursor.execute("DELETE FROM bar WHERE baz = %s", [self.baz]) cursor.execute("DELETE FROM bar WHERE baz = %s", [self.baz])
connection.commit() connection.commit()
``connection`` and ``cursor`` simply use the standard `Python DB-API`_. If you're not ``connection`` and ``cursor`` simply use the standard `Python DB-API`_. If
familiar with the Python DB-API, note that the SQL statement in you're not familiar with the Python DB-API, note that the SQL statement in
``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding parameters ``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding parameters
directly within the SQL. If you use this technique, the underlying database directly within the SQL. If you use this technique, the underlying database
library will automatically add quotes and escaping to your parameter(s) as library will automatically add quotes and escaping to your parameter(s) as
necessary. necessary. (Also note that Django expects the ``"%s"`` placeholder, *not* the
``"?"`` placeholder, which is used by the SQLite Python 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 just A final note: If all you want to do is a custom ``WHERE`` clause, you can just
just the ``where``, ``tables`` and ``params`` arguments to the standard lookup just the ``where``, ``tables`` and ``params`` arguments to the standard lookup
@ -1508,32 +1600,35 @@ API. See `Other lookup options`_.
.. _Python DB-API: http://www.python.org/peps/pep-0249.html .. _Python DB-API: http://www.python.org/peps/pep-0249.html
.. _Other lookup options: http://www.djangoproject.com/documentation/db_api/#extra-params-select-where-tables .. _Other lookup options: http://www.djangoproject.com/documentation/db_api/#extra-params-select-where-tables
Using models
============
Once you have created your model, you have to tell Django about your new application.
This is done by editing your settings file and adding the name of the module that
contains your models module to the ``INSTALLED_APPS`` tuple.
For example, if the models for your application are contained in the module
``project.myapp.models`` (the package structure that is created for an application
by the ``django-admin.py startapp`` script), ``INSTALLED_APPS`` should read, in part::
INSTALLED_APPS = (
#...
project.myapp,
#...
)
Models across files Models across files
=================== ===================
It's perfectly OK to relate a model to one from another module. To do this, It's perfectly OK to relate a model to one from another app. To do this, just
just import the model module at the top of your model module. Then, just import the related model at the top of the model that holds your model. Then,
refer to the other model class wherever needed. For example:: just refer to the other model class wherever needed. For example::
from myproject.otherapp import Site from mysite.geography.models import ZipCode
class MyModel(models.Model): class Restaurant(models.Model):
# ... # ...
sites = models.ManyToManyField(Site) zip_code = models.ForeignKey(ZipCode)
Using models
============
Once you have created your models, the final step is to tell Django you're
going to *use* those models.
Do this by editing your settings file and changing the ``INSTALLED_APPS``
setting to add the name of the module that contains your ``models.py``.
For example, if the models for your application live in the module
``mysite.myapp.models`` (the package structure that is created for an
application by the ``manage.py startapp`` script), ``INSTALLED_APPS`` should
read, in part::
INSTALLED_APPS = (
#...
'mysite.myapp',
#...
)