Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finished proofreading docs/model-api.txt

git-svn-id: http://code.djangoproject.com/svn/django/trunk@2833 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a8ccdd0fcd631e8e928ef20547e1fe3e313dc607 1 parent a79cbb5
Adrian Holovaty adrianholovaty authored
Showing with 232 additions and 137 deletions.
  1. +232 −137 docs/model-api.txt
369 docs/model-api.txt
View
@@ -1298,16 +1298,22 @@ user searches for ``john lennon``, Django will do the equivalent of this SQL
Managers
========
-The Manager is the interface through which database query operations
-are provided to Django applications. At least one Manager exists for
-every model in a Django application.
+A ``Manager`` is the interface through which database query operations are
+provided to Django models. At least one ``Manager`` exists for every model in
+a Django application.
-By default, Django will add a Manager with the name of ``objects`` to
-every Django model. However, if you wish to use ``objects`` as a field
-name, or if you wish to use a name other than ``objects`` for the Manager,
-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()
-on that model. For example::
+The way ``Manager`` classes work is documented in the `Retrieving objects`_
+section of the database API docs, but this section specifically touches on
+model options that customize ``Manager`` behavior.
+
+Manager names
+-------------
+
+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
@@ -1315,38 +1321,118 @@ on that model. For example::
#...
people = models.Manager()
-In this example, ``Person.objects.all()`` will generate an error, but
-``Person.people.all()`` will provide a list of all ``Person`` objects.
+Using this example model, ``Person.objects`` will generate an
+``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
-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.
+Custom Managers
+---------------
-To modify the initial Query Set provided by a Manager, override the
-``get_query_set()`` method to return a Query Set with the properties
-you require. For example::
+You can use a custom ``Manager`` in a particular model by extending the base
+``Manager`` class and instantiating your custom ``Manager`` in your model.
+
+There are two reasons you might want to customize a ``Manager``: to add extra
+``Manager`` methods, and/or to modify the initial ``QuerySet`` the ``Manager``
+returns.
+
+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):
+ return super(Manager, self).get_query_set().filter(author='Roald Dahl')
- class PersonManager(models.Manager):
- # Add some custom behavior to the Manager
- def move_house(self):
- # Some logic to help a person move house
+ # Then hook it into the Book model explicitly.
+ class Book(models.Model):
+ title = models.CharField(maxlength=100)
+ author = models.CharField(maxlength=50)
- # Modify the initial Query Set provided by the manager
- def get_query_set(self):
- return super(Manager, self).get_query_set().filter(name__startswith="Fred")
+ objects = models.Manager() # The default manager.
+ dahl_objects = DahlBookManager() # The Dahl-specific manager.
- class Person(models.Model):
- #...
- objects = PersonManager()
+With this sample model, ``Book.objects.all()`` will return all books in the
+database, but ``Book.dahl_objects.all()`` will only return the ones written by
+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::
-In this example, ``Person.objects.all()`` will only return people whose name starts
-with "Fred"; ``Person.objects.move_house()`` will also be available.
+ Book.dahl_objects.all()
+ Book.dahl_objects.filter(title='Matilda')
+ Book.dahl_objects.count()
-If required, you can add multiple Managers to a model. Every Manager attribute
-added to a model can be accessed and used as a manager. This is an easy way
-to define common filters types for your models. For example, the model::
+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):
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')
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()
men = MaleManager()
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.
-If you are going to install a customized Manager, be warned that the first
-Manager that Django encounters in a model definition has special status.
-Django interprets the first Manager defined in a class as the default Manager.
-Certain operations use the default Manager to obtain lists of objects, so it
-is generally a good idea for the first Manager to be relatively unfiltered.
-In the last example, ``people`` is defined first - so the default Manager
-will include everyone.
+If you use custom ``Manager`` objects, take note that the first ``Manager``
+Django encounters (in order by which they're defined in the model) has a
+special status. Django interprets the first ``Manager`` defined in a class as
+the "default" ``Manager``. Certain operations -- such as Django's admin site --
+use the default ``Manager`` to obtain lists of objects, so it's generally a
+good idea for the first ``Manager`` to be relatively unfiltered. In the last
+example, the ``people`` ``Manager`` is defined first -- so it's the default
+``Manager``.
Model methods
=============
-There are a number of methods you can define on model objects to control the
-object's behavior. First, any methods you define will be available as methods
-of object instances. For example::
+Define custom methods on a model to add custom "row-level" functionality to
+your objects. Whereas ``Manager`` methods are intended to do "table-wide"
+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):
- return "anchovies" in [topping.name for topping in self.toppings.all()]
+For example, this model has a few custom methods::
-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')
-A few object methods have special meaning:
+ 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)
-``__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__``.
+The last method in this example is a *property*. `Read more about properties`_.
- Although defining ``__str__()`` isn't required, it's strongly encouraged.
+.. _Read more about properties: http://www.python.org/download/releases/2.2/descrintro/#property
- See `Adding str`_ for a full example.
+A few object methods have special meaning:
- .. _Adding str: http://www.djangoproject.com/documentation/models/repr/
+``__str__``
+-----------
-``get_absolute_url``
- Define a ``get_absolute_url`` method to tell Django how to calculate the
- URL for an object. For example::
+``__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.
- def get_absolute_url(self):
- return "/pizzas/%i/" % self.id
+For example::
- 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.
+ class Person(models.Model):
+ first_name = models.CharField(maxlength=50)
+ last_name = models.CharField(maxlength=50)
- It's good practice to use ``get_absolute_url()`` in templates, instead of
- hard-coding your objects' URLs.
+ def __str__(self):
+ return '%s %s' % (self.first_name, self.last_name)
-Module-level methods
+``get_absolute_url``
--------------------
-If you want to add a method to the Model, rather than instances of the model,
-you can use the Python ``staticmethod`` and ``classmethod`` operators. For
-example::
-
- class Pizza(models.Model):
- # ...
-
- def get_pizzas_to_deliver():
- return get_list(delivered__exact=False)
- get_pizzas_to_deliver = staticmethod(get_pizzas_to_deliver)
-
-Or, using Python 2.4 decorators::
+Define a ``get_absolute_url()`` method to tell Django how to calculate the
+URL for an object. For example::
- # ...
- @staticmethod
- def get_pizzas_to_deliver():
- # ...
+ def get_absolute_url(self):
+ return "/people/%i/" % self.id
-This will make the top-level ``pizzas`` module have a ``get_pizzas_to_deliver()``
-method::
+Django uses this in its admin interface. If an object defines
+``get_absolute_url()``, the object-editing page will have a "View on site"
+link that will jump you directly to the object's public view, according to
+``get_absolute_url()``.
- >>> from pizza_hut.models import Pizza
- >>> Pizza.get_pizzas_to_deliver()
- [ ... ]
+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.
-Manipulator methods
--------------------
+It's good practice to use ``get_absolute_url()`` in templates, instead of
+hard-coding your objects' URLs. For example, this template code is bad::
-(The functionality in this section is going away soon. This documentation is
-provided only for legacy purposes at this point.)
+ <a href="/people/{{ object.id }}/">{{ object.name }}</a>
-Similarly, you can add methods to the object's manipulators by defining methods
-that being with "_manipulator_". This is most useful for providing custom
-validators for certain fields, because manipulators automatically call any
-method that begins with "validate"::
+But this template code is good::
- class Pizza(models.Model):
- # ...
+ <a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
- 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."
+(Yes, we know ``get_absolute_url()`` couples URLs to models, which violates the
+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
+the sake of convenience. With that said, we're working on an even cleaner way
+of specifying URLs in a more DRY fashion.)
Executing custom SQL
--------------------
Feel free to write custom SQL statements in custom model methods and
-module-level methods. The object ``django.db.connection`` object represents
-the current database connection. To use it, call ``connection.cursor()`` to
-get a cursor object. Then, call ``cursor.execute(sql, [params])``
-to execute the SQL and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return
-the resulting rows. Example::
+module-level methods. The object ``django.db.connection`` represents the
+current database connection. To use it, call ``connection.cursor()`` to get a
+cursor object. Then, call ``cursor.execute(sql, [params])`` to execute the SQL
+and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return the resulting
+rows. Example::
def my_custom_sql(self):
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])
connection.commit()
-``connection`` and ``cursor`` simply use the standard `Python DB-API`_. If you're not
-familiar with the Python DB-API, note that the SQL statement in
+``connection`` and ``cursor`` simply use the standard `Python DB-API`_. If
+you're not familiar with the Python DB-API, note that the SQL statement in
``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding parameters
directly within the SQL. If you use this technique, the underlying database
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
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
.. _Other lookup options: http://www.djangoproject.com/documentation/db_api/#extra-params-select-where-tables
+Models across files
+===================
+
+It's perfectly OK to relate a model to one from another app. To do this, just
+import the related model at the top of the model that holds your model. Then,
+just refer to the other model class wherever needed. For example::
+
+ from mysite.geography.models import ZipCode
+
+ class Restaurant(models.Model):
+ # ...
+ zip_code = models.ForeignKey(ZipCode)
+
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.
+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 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::
+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 = (
#...
- project.myapp,
+ 'mysite.myapp',
#...
)
-
-Models across files
-===================
-
-It's perfectly OK to relate a model to one from another module. To do this,
-just import the model module at the top of your model module. Then, just
-refer to the other model class wherever needed. For example::
-
- from myproject.otherapp import Site
-
- class MyModel(models.Model):
- # ...
- sites = models.ManyToManyField(Site)
Please sign in to comment.
Something went wrong with that request. Please try again.