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