| 1 |
========================== |
|---|
| 2 |
The contenttypes framework |
|---|
| 3 |
========================== |
|---|
| 4 |
|
|---|
| 5 |
Django includes a "contenttypes" application that can track all of |
|---|
| 6 |
the models installed in your Django-powered project, providing a |
|---|
| 7 |
high-level, generic interface for working with your models. |
|---|
| 8 |
|
|---|
| 9 |
Overview |
|---|
| 10 |
======== |
|---|
| 11 |
|
|---|
| 12 |
At the heart of the contenttypes application is the ``ContentType`` |
|---|
| 13 |
model, which lives at |
|---|
| 14 |
``django.contrib.contenttypes.models.ContentType``. Instances of |
|---|
| 15 |
``ContentType`` represent and store information about the models |
|---|
| 16 |
installed in your project, and new instances of ``ContentType`` are |
|---|
| 17 |
automatically created whenever new models are installed. |
|---|
| 18 |
|
|---|
| 19 |
Instances of ``ContentType`` have methods for returning the model |
|---|
| 20 |
classes they represent and for querying objects from those models. |
|---|
| 21 |
``ContentType`` also has a `custom manager`_ that adds methods for |
|---|
| 22 |
working with ``ContentType`` and for obtaining instances of |
|---|
| 23 |
``ContentType`` for a particular model. |
|---|
| 24 |
|
|---|
| 25 |
Relations between your models and ``ContentType`` can also be used to |
|---|
| 26 |
enable "generic" relationships between an instance of one of your |
|---|
| 27 |
models and instances of any model you have installed. |
|---|
| 28 |
|
|---|
| 29 |
.. _custom manager: ../model-api/#custom-managers |
|---|
| 30 |
|
|---|
| 31 |
Installing the contenttypes framework |
|---|
| 32 |
===================================== |
|---|
| 33 |
|
|---|
| 34 |
The contenttypes framework is included in the default |
|---|
| 35 |
``INSTALLED_APPS`` list created by ``django-admin.py startproject``, |
|---|
| 36 |
but if you've removed it or if you manually set up your |
|---|
| 37 |
``INSTALLED_APPS`` list, you can enable it by adding |
|---|
| 38 |
``'django.contrib.contenttypes'`` to your ``INSTALLED_APPS`` setting. |
|---|
| 39 |
|
|---|
| 40 |
It's generally a good idea to have the contenttypes framework |
|---|
| 41 |
installed; several of Django's other bundled applications require it: |
|---|
| 42 |
|
|---|
| 43 |
* The admin application uses it to log the history of each object |
|---|
| 44 |
added or changed through the admin interface. |
|---|
| 45 |
|
|---|
| 46 |
* Django's `authentication framework`_ uses it to tie user permissions |
|---|
| 47 |
to specific models. |
|---|
| 48 |
|
|---|
| 49 |
* Django's comments system (``django.contrib.comments``) uses it to |
|---|
| 50 |
"attach" comments to any installed model. |
|---|
| 51 |
|
|---|
| 52 |
.. _authentication framework: ../authentication/ |
|---|
| 53 |
|
|---|
| 54 |
The ``ContentType`` model |
|---|
| 55 |
========================= |
|---|
| 56 |
|
|---|
| 57 |
Each instance of ``ContentType`` has three fields which, taken |
|---|
| 58 |
together, uniquely describe an installed model: |
|---|
| 59 |
|
|---|
| 60 |
``app_label`` |
|---|
| 61 |
The name of the application the model is part of. This is taken from |
|---|
| 62 |
the ``app_label`` attribute of the model, and includes only the *last* |
|---|
| 63 |
part of the application's Python import path; |
|---|
| 64 |
"django.contrib.contenttypes", for example, becomes an ``app_label`` |
|---|
| 65 |
of "contenttypes". |
|---|
| 66 |
|
|---|
| 67 |
``model`` |
|---|
| 68 |
The name of the model class. |
|---|
| 69 |
|
|---|
| 70 |
``name`` |
|---|
| 71 |
The human-readable name of the model. This is taken from |
|---|
| 72 |
`the verbose_name attribute`_ of the model. |
|---|
| 73 |
|
|---|
| 74 |
Let's look at an example to see how this works. If you already have |
|---|
| 75 |
the contenttypes application installed, and then add `the sites |
|---|
| 76 |
application`_ to your ``INSTALLED_APPS`` setting and run ``manage.py |
|---|
| 77 |
syncdb`` to install it, the model ``django.contrib.sites.models.Site`` |
|---|
| 78 |
will be installed into your database. Along with it a new instance |
|---|
| 79 |
of ``ContentType`` will be created with the following values: |
|---|
| 80 |
|
|---|
| 81 |
* ``app_label`` will be set to ``'sites'`` (the last part of the Python |
|---|
| 82 |
path "django.contrib.sites"). |
|---|
| 83 |
|
|---|
| 84 |
* ``model`` will be set to ``'site'``. |
|---|
| 85 |
|
|---|
| 86 |
* ``name`` will be set to ``'site'``. |
|---|
| 87 |
|
|---|
| 88 |
.. _the verbose_name attribute: ../model-api/#verbose_name |
|---|
| 89 |
.. _the sites application: ../sites/ |
|---|
| 90 |
|
|---|
| 91 |
Methods on ``ContentType`` instances |
|---|
| 92 |
==================================== |
|---|
| 93 |
|
|---|
| 94 |
Each ``ContentType`` instance has methods that allow you to get from a |
|---|
| 95 |
``ContentType`` instance to the model it represents, or to retrieve objects |
|---|
| 96 |
from that model: |
|---|
| 97 |
|
|---|
| 98 |
``get_object_for_this_type(**kwargs)`` |
|---|
| 99 |
Takes a set of valid `lookup arguments`_ for the model the |
|---|
| 100 |
``ContentType`` represents, and does `a get() lookup`_ on that |
|---|
| 101 |
model, returning the corresponding object. |
|---|
| 102 |
|
|---|
| 103 |
``model_class()`` |
|---|
| 104 |
Returns the model class represented by this ``ContentType`` |
|---|
| 105 |
instance. |
|---|
| 106 |
|
|---|
| 107 |
For example, we could look up the ``ContentType`` for the ``User`` model:: |
|---|
| 108 |
|
|---|
| 109 |
>>> from django.contrib.contenttypes.models import ContentType |
|---|
| 110 |
>>> user_type = ContentType.objects.get(app_label="auth", model="user") |
|---|
| 111 |
>>> user_type |
|---|
| 112 |
<ContentType: user> |
|---|
| 113 |
|
|---|
| 114 |
And then use it to query for a particular ``User``, or to get access |
|---|
| 115 |
to the ``User`` model class:: |
|---|
| 116 |
|
|---|
| 117 |
>>> user_type.model_class() |
|---|
| 118 |
<class 'django.contrib.auth.models.User'> |
|---|
| 119 |
>>> user_type.get_object_for_this_type(username='Guido') |
|---|
| 120 |
<User: Guido> |
|---|
| 121 |
|
|---|
| 122 |
Together, ``get_object_for_this_type`` and ``model_class`` enable two |
|---|
| 123 |
extremely important use cases: |
|---|
| 124 |
|
|---|
| 125 |
1. Using these methods, you can write high-level generic code that |
|---|
| 126 |
performs queries on any installed model -- instead of importing and |
|---|
| 127 |
using a single specific model class, you can pass an ``app_label`` |
|---|
| 128 |
and ``model`` into a ``ContentType`` lookup at runtime, and then |
|---|
| 129 |
work with the model class or retrieve objects from it. |
|---|
| 130 |
|
|---|
| 131 |
2. You can relate another model to ``ContentType`` as a way of tying |
|---|
| 132 |
instances of it to particular model classes, and use these methods |
|---|
| 133 |
to get access to those model classes. |
|---|
| 134 |
|
|---|
| 135 |
Several of Django's bundled applications make use of the latter |
|---|
| 136 |
technique. For example, `the permissions system`_ in Django's |
|---|
| 137 |
authentication framework uses a ``Permission`` model with a foreign |
|---|
| 138 |
key to ``ContentType``; this lets ``Permission`` represent concepts |
|---|
| 139 |
like "can add blog entry" or "can delete news story". |
|---|
| 140 |
|
|---|
| 141 |
.. _lookup arguments: ../db-api/#field-lookups |
|---|
| 142 |
.. _a get() lookup: ../db-api/#get-kwargs |
|---|
| 143 |
.. _the permissions system: ../authentication/#permissions |
|---|
| 144 |
|
|---|
| 145 |
The ``ContentTypeManager`` |
|---|
| 146 |
-------------------------- |
|---|
| 147 |
|
|---|
| 148 |
``ContentType`` also has a custom manager, ``ContentTypeManager``, |
|---|
| 149 |
which adds the following methods: |
|---|
| 150 |
|
|---|
| 151 |
``clear_cache()`` |
|---|
| 152 |
Clears an internal cache used by ``ContentType`` to keep track of which |
|---|
| 153 |
models for which it has created ``ContentType`` instances. You probably |
|---|
| 154 |
won't ever need to call this method yourself; Django will call it |
|---|
| 155 |
automatically when it's needed. |
|---|
| 156 |
|
|---|
| 157 |
``get_for_model(model)`` |
|---|
| 158 |
Takes either a model class or an instance of a model, and returns the |
|---|
| 159 |
``ContentType`` instance representing that model. |
|---|
| 160 |
|
|---|
| 161 |
The ``get_for_model`` method is especially useful when you know you |
|---|
| 162 |
need to work with a ``ContentType`` but don't want to go to the |
|---|
| 163 |
trouble of obtaining the model's metadata to perform a manual lookup:: |
|---|
| 164 |
|
|---|
| 165 |
>>> from django.contrib.auth.models import User |
|---|
| 166 |
>>> user_type = ContentType.objects.get_for_model(User) |
|---|
| 167 |
>>> user_type |
|---|
| 168 |
<ContentType: user> |
|---|
| 169 |
|
|---|
| 170 |
Generic relations |
|---|
| 171 |
================= |
|---|
| 172 |
|
|---|
| 173 |
Adding a foreign key from one of your own models to ``ContentType`` |
|---|
| 174 |
allows your model to effectively tie itself to another model class, as |
|---|
| 175 |
in the example of the ``Permission`` model above. But it's possible to |
|---|
| 176 |
go one step further and use ``ContentType`` to enable truly generic |
|---|
| 177 |
(sometimes called "polymorphic") relationships between models. |
|---|
| 178 |
|
|---|
| 179 |
A simple example is a tagging system, which might look like this:: |
|---|
| 180 |
|
|---|
| 181 |
from django.db import models |
|---|
| 182 |
from django.contrib.contenttypes.models import ContentType |
|---|
| 183 |
from django.contrib.contenttypes import generic |
|---|
| 184 |
|
|---|
| 185 |
class TaggedItem(models.Model): |
|---|
| 186 |
tag = models.SlugField() |
|---|
| 187 |
content_type = models.ForeignKey(ContentType) |
|---|
| 188 |
object_id = models.PositiveIntegerField() |
|---|
| 189 |
content_object = generic.GenericForeignKey('content_type', 'object_id') |
|---|
| 190 |
|
|---|
| 191 |
def __unicode__(self): |
|---|
| 192 |
return self.tag |
|---|
| 193 |
|
|---|
| 194 |
A normal ``ForeignKey`` can only "point to" one other model, which |
|---|
| 195 |
means that if the ``TaggedItem`` model used a ``ForeignKey`` it would have to |
|---|
| 196 |
choose one and only one model to store tags for. The contenttypes |
|---|
| 197 |
application provides a special field type -- |
|---|
| 198 |
``django.contrib.contenttypes.generic.GenericForeignKey`` -- which |
|---|
| 199 |
works around this and allows the relationship to be with any |
|---|
| 200 |
model. There are three parts to setting up a ``GenericForeignKey``: |
|---|
| 201 |
|
|---|
| 202 |
1. Give your model a ``ForeignKey`` to ``ContentType``. |
|---|
| 203 |
|
|---|
| 204 |
2. Give your model a field that can store a primary-key value from the |
|---|
| 205 |
models you'll be relating to. (For most models, this means an |
|---|
| 206 |
``IntegerField`` or ``PositiveIntegerField``.) |
|---|
| 207 |
|
|---|
| 208 |
This field must be of the same type as the primary key of the models |
|---|
| 209 |
that will be involved in the generic relation. For example, if you use |
|---|
| 210 |
``IntegerField``, you won't be able to form a generic relation with a |
|---|
| 211 |
model that uses a ``CharField`` as a primary key. |
|---|
| 212 |
|
|---|
| 213 |
3. Give your model a ``GenericForeignKey``, and pass it the names of |
|---|
| 214 |
the two fields described above. If these fields are named |
|---|
| 215 |
"content_type" and "object_id", you can omit this -- those are the |
|---|
| 216 |
default field names ``GenericForeignKey`` will look for. |
|---|
| 217 |
|
|---|
| 218 |
This will enable an API similar to the one used for a normal ``ForeignKey``; |
|---|
| 219 |
each ``TaggedItem`` will have a ``content_object`` field that returns the |
|---|
| 220 |
object it's related to, and you can also assign to that field or use it when |
|---|
| 221 |
creating a ``TaggedItem``:: |
|---|
| 222 |
|
|---|
| 223 |
>>> from django.contrib.auth.models import User |
|---|
| 224 |
>>> guido = User.objects.get(username='Guido') |
|---|
| 225 |
>>> t = TaggedItem(content_object=guido, tag='bdfl') |
|---|
| 226 |
>>> t.save() |
|---|
| 227 |
>>> t.content_object |
|---|
| 228 |
<User: Guido> |
|---|
| 229 |
|
|---|
| 230 |
Reverse generic relations |
|---|
| 231 |
------------------------- |
|---|
| 232 |
|
|---|
| 233 |
If you know which models you'll be using most often, you can also add |
|---|
| 234 |
a "reverse" generic relationship to enable an additional API. For example:: |
|---|
| 235 |
|
|---|
| 236 |
class Bookmark(models.Model): |
|---|
| 237 |
url = models.URLField() |
|---|
| 238 |
tags = generic.GenericRelation(TaggedItem) |
|---|
| 239 |
|
|---|
| 240 |
``Bookmark`` instances will each have a ``tags`` attribute, which can |
|---|
| 241 |
be used to retrieve their associated ``TaggedItems``:: |
|---|
| 242 |
|
|---|
| 243 |
>>> b = Bookmark(url='http://www.djangoproject.com/') |
|---|
| 244 |
>>> b.save() |
|---|
| 245 |
>>> t1 = TaggedItem(content_object=b, tag='django') |
|---|
| 246 |
>>> t1.save() |
|---|
| 247 |
>>> t2 = TaggedItem(content_object=b, tag='python') |
|---|
| 248 |
>>> t2.save() |
|---|
| 249 |
>>> b.tags.all() |
|---|
| 250 |
[<TaggedItem: django>, <TaggedItem: python>] |
|---|
| 251 |
|
|---|
| 252 |
If you don't add the reverse relationship, you can do the lookup manually:: |
|---|
| 253 |
|
|---|
| 254 |
>>> b = Bookmark.objects.get(url='http://www.djangoproject.com/) |
|---|
| 255 |
>>> bookmark_type = ContentType.objects.get_for_model(b) |
|---|
| 256 |
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, |
|---|
| 257 |
... object_id=b.id) |
|---|
| 258 |
[<TaggedItem: django>, <TaggedItem: python>] |
|---|
| 259 |
|
|---|
| 260 |
Note that if you delete an object that has a ``GenericRelation``, any objects |
|---|
| 261 |
which have a ``GenericForeignKey`` pointing at it will be deleted as well. In |
|---|
| 262 |
the example above, this means that if a ``Bookmark`` object were deleted, any |
|---|
| 263 |
``TaggedItem`` objects pointing at it would be deleted at the same time. |
|---|