| 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 |