123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- ==========================
- The contenttypes framework
- ==========================
- .. module:: django.contrib.contenttypes
- :synopsis: Provides generic interface to installed models.
- Django includes a :mod:`contenttypes` application that can track all of
- the models installed in your Django-powered project, providing a
- high-level, generic interface for working with your models.
- Overview
- ========
- At the heart of the contenttypes application is the
- :class:`~django.contrib.contenttypes.models.ContentType` model, which lives at
- ``django.contrib.contenttypes.models.ContentType``. Instances of
- :class:`~django.contrib.contenttypes.models.ContentType` represent and store
- information about the models installed in your project, and new instances of
- :class:`~django.contrib.contenttypes.models.ContentType` are automatically
- created whenever new models are installed.
- Instances of :class:`~django.contrib.contenttypes.models.ContentType` have
- methods for returning the model classes they represent and for querying objects
- from those models. :class:`~django.contrib.contenttypes.models.ContentType`
- also has a :ref:`custom manager <custom-managers>` that adds methods for
- working with :class:`~django.contrib.contenttypes.models.ContentType` and for
- obtaining instances of :class:`~django.contrib.contenttypes.models.ContentType`
- for a particular model.
- Relations between your models and
- :class:`~django.contrib.contenttypes.models.ContentType` can also be used to
- enable "generic" relationships between an instance of one of your
- models and instances of any model you have installed.
- Installing the contenttypes framework
- =====================================
- The contenttypes framework is included in the default
- :setting:`INSTALLED_APPS` list created by ``django-admin.py startproject``,
- but if you've removed it or if you manually set up your
- :setting:`INSTALLED_APPS` list, you can enable it by adding
- ``'django.contrib.contenttypes'`` to your :setting:`INSTALLED_APPS` setting.
- It's generally a good idea to have the contenttypes framework
- installed; several of Django's other bundled applications require it:
- * The admin application uses it to log the history of each object
- added or changed through the admin interface.
- * Django's :mod:`authentication framework <django.contrib.auth>` uses it
- to tie user permissions to specific models.
- * Django's comments system (:mod:`django.contrib.comments`) uses it to
- "attach" comments to any installed model.
- The ``ContentType`` model
- =========================
- .. class:: models.ContentType
- Each instance of :class:`~django.contrib.contenttypes.models.ContentType`
- has three fields which, taken together, uniquely describe an installed model:
- .. attribute:: models.ContentType.app_label
- The name of the application the model is part of. This is taken from
- the :attr:`app_label` attribute of the model, and includes only the *last*
- part of the application's Python import path;
- "django.contrib.contenttypes", for example, becomes an :attr:`app_label`
- of "contenttypes".
- .. attribute:: models.ContentType.model
- The name of the model class.
- .. attribute:: models.ContentType.name
- The human-readable name of the model. This is taken from the
- :attr:`verbose_name <django.db.models.fields.Field.verbose_name>`
- attribute of the model.
- Let's look at an example to see how this works. If you already have
- the contenttypes application installed, and then add
- :mod:`the sites application <django.contrib.sites>` to your
- :setting:`INSTALLED_APPS` setting and run ``manage.py syncdb`` to install it,
- the model :class:`django.contrib.sites.models.Site` will be installed into
- your database. Along with it a new instance of
- :class:`~django.contrib.contenttypes.models.ContentType` will be
- created with the following values:
- * :attr:`app_label` will be set to ``'sites'`` (the last part of the Python
- path "django.contrib.sites").
- * :attr:`model` will be set to ``'site'``.
- * :attr:`name` will be set to ``'site'``.
- .. _the verbose_name attribute: ../model-api/#verbose_name
- Methods on ``ContentType`` instances
- ====================================
- .. class:: models.ContentType
- Each :class:`~django.contrib.contenttypes.models.ContentType` instance has
- methods that allow you to get from a
- :class:`~django.contrib.contenttypes.models.ContentType` instance to the model
- it represents, or to retrieve objects from that model:
- .. method:: models.ContentType.get_object_for_this_type(**kwargs)
- Takes a set of valid :ref:`lookup arguments <field-lookups-intro>` for the
- model the :class:`~django.contrib.contenttypes.models.ContentType`
- represents, and does :ref:`a get() lookup <get-kwargs>` on that model,
- returning the corresponding object.
- .. method:: models.ContentType.model_class()
- Returns the model class represented by this
- :class:`~django.contrib.contenttypes.models.ContentType` instance.
- For example, we could look up the
- :class:`~django.contrib.contenttypes.models.ContentType` for the
- :class:`~django.contrib.auth.models.User` model::
- >>> from django.contrib.contenttypes.models import ContentType
- >>> user_type = ContentType.objects.get(app_label="auth", model="user")
- >>> user_type
- <ContentType: user>
- And then use it to query for a particular ``User``, or to get access
- to the ``User`` model class::
- >>> user_type.model_class()
- <class 'django.contrib.auth.models.User'>
- >>> user_type.get_object_for_this_type(username='Guido')
- <User: Guido>
- Together,
- :meth:`~django.contrib.contenttypes.models.ContentType.get_object_for_this_type`
- and :meth:`~django.contrib.contenttypes.models.ContentType.model_class`
- enable two extremely important use cases:
- 1. Using these methods, you can write high-level generic code that
- performs queries on any installed model -- instead of importing and using
- a single specific model class, you can pass an ``app_label`` and
- ``model`` into a :class:`~django.contrib.contenttypes.models.ContentType`
- lookup at runtime, and then work with the model class or retrieve objects
- from it.
- 2. You can relate another model to
- :class:`~django.contrib.contenttypes.models.ContentType` as a way of
- tying instances of it to particular model classes, and use these methods
- to get access to those model classes.
- Several of Django's bundled applications make use of the latter technique.
- For example,
- :class:`the permissions system <django.contrib.auth.models.Permission` in
- Django's authentication framework uses a
- :class:`~django.contrib.auth.models.Permission` model with a foreign
- key to :class:`~django.contrib.contenttypes.models.ContentType`; this lets
- :class:`~django.contrib.auth.models.Permission` represent concepts like
- "can add blog entry" or "can delete news story".
- The ``ContentTypeManager``
- --------------------------
- .. class:: models.ContentTypeManager
- :class:`~django.contrib.contenttypes.models.ContentType` also has a custom
- manager, :class:`~django.contrib.contenttypes.models.ContentTypeManager`,
- which adds the following methods:
- .. method:: models.ContentTypeManager.clear_cache()
- Clears an internal cache used by
- :class:`~django.contrib.contenttypes.models.ContentType` to keep track
- of which models for which it has created
- :class:`django.contrib.contenttypes.models.ContentType` instances. You
- probably won't ever need to call this method yourself; Django will call
- it automatically when it's needed.
- .. method:: models.ContentTypeManager.get_for_model(model)
- Takes either a model class or an instance of a model, and returns the
- :class:`~django.contrib.contenttypes.models.ContentType` instance
- representing that model.
- The :meth:`~models.ContentTypeManager.get_for_model()` method is especially useful when you know you
- need to work with a :class:`ContentType <django.contrib.contenttypes.models.ContentType>` but don't want to go to the
- trouble of obtaining the model's metadata to perform a manual lookup::
- >>> from django.contrib.auth.models import User
- >>> user_type = ContentType.objects.get_for_model(User)
- >>> user_type
- <ContentType: user>
- .. _generic-relations:
- Generic relations
- =================
- Adding a foreign key from one of your own models to
- :class:`~django.contrib.contenttypes.models.ContentType` allows your model to
- effectively tie itself to another model class, as in the example of the
- :class:`~django.contrib.auth.models.Permission` model above. But it's possible
- to go one step further and use
- :class:`~django.contrib.contenttypes.models.ContentType` to enable truly
- generic (sometimes called "polymorphic") relationships between models.
- A simple example is a tagging system, which might look like this::
- from django.db import models
- from django.contrib.contenttypes.models import ContentType
- from django.contrib.contenttypes import generic
- class TaggedItem(models.Model):
- tag = models.SlugField()
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- def __unicode__(self):
- return self.tag
- A normal :class:`~django.db.models.fields.related.ForeignKey` can only "point
- to" one other model, which means that if the ``TaggedItem`` model used a
- :class:`~django.db.models.fields.related.ForeignKey` it would have to
- choose one and only one model to store tags for. The contenttypes
- application provides a special field type --
- :class:`django.contrib.contenttypes.generic.GenericForeignKey` -- which
- works around this and allows the relationship to be with any
- model. There are three parts to setting up a
- :class:`~django.contrib.contenttypes.generic.GenericForeignKey`:
- 1. Give your model a :class:`~django.db.models.fields.related.ForeignKey`
- to :class:`~django.contrib.contenttypes.models.ContentType`.
- 2. Give your model a field that can store a primary-key value from the
- models you'll be relating to. (For most models, this means an
- :class:`~django.db.models.fields.IntegerField` or
- :class:`~django.db.models.fields.PositiveIntegerField`.)
- This field must be of the same type as the primary key of the models
- that will be involved in the generic relation. For example, if you use
- :class:`~django.db.models.fields.IntegerField`, you won't be able to
- form a generic relation with a model that uses a
- :class:`~django.db.models.fields.CharField` as a primary key.
- 3. Give your model a
- :class:`~django.contrib.contenttypes.generic.GenericForeignKey`, and
- pass it the names of the two fields described above. If these fields
- are named "content_type" and "object_id", you can omit this -- those
- are the default field names
- :class:`~django.contrib.contenttypes.generic.GenericForeignKey` will
- look for.
- This will enable an API similar to the one used for a normal
- :class:`~django.db.models.fields.related.ForeignKey`;
- each ``TaggedItem`` will have a ``content_object`` field that returns the
- object it's related to, and you can also assign to that field or use it when
- creating a ``TaggedItem``::
- >>> from django.contrib.auth.models import User
- >>> guido = User.objects.get(username='Guido')
- >>> t = TaggedItem(content_object=guido, tag='bdfl')
- >>> t.save()
- >>> t.content_object
- <User: Guido>
- Due to the way :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
- is implemented, you cannot use such fields directly with filters (``filter()``
- and ``exclude()``, for example) via the database API. They aren't normal field
- objects. These examples will *not* work::
- # This will fail
- >>> TaggedItem.objects.filter(content_object=guido)
- # This will also fail
- >>> TaggedItem.objects.get(content_object=guido)
- Reverse generic relations
- -------------------------
- If you know which models you'll be using most often, you can also add
- a "reverse" generic relationship to enable an additional API. For example::
- class Bookmark(models.Model):
- url = models.URLField()
- tags = generic.GenericRelation(TaggedItem)
- ``Bookmark`` instances will each have a ``tags`` attribute, which can
- be used to retrieve their associated ``TaggedItems``::
- >>> b = Bookmark(url='http://www.djangoproject.com/')
- >>> b.save()
- >>> t1 = TaggedItem(content_object=b, tag='django')
- >>> t1.save()
- >>> t2 = TaggedItem(content_object=b, tag='python')
- >>> t2.save()
- >>> b.tags.all()
- [<TaggedItem: django>, <TaggedItem: python>]
- Just as :class:`django.contrib.contenttypes.generic.GenericForeignKey`
- accepts the names of the content-type and object-ID fields as
- arguments, so too does ``GenericRelation``; if the model which has the
- generic foreign key is using non-default names for those fields, you
- must pass the names of the fields when setting up a
- ``GenericRelation`` to it. For example, if the ``TaggedItem`` model
- referred to above used fields named ``content_type_fk`` and
- ``object_primary_key`` to create its generic foreign key, then a
- ``GenericRelation`` back to it would need to be defined like so::
- tags = generic.GenericRelation(TaggedItem, content_type_field='content_type_fk', object_id_field='object_primary_key')
- Of course, if you don't add the reverse relationship, you can do the
- same types of lookups manually::
- >>> b = Bookmark.objects.get(url='http://www.djangoproject.com/')
- >>> bookmark_type = ContentType.objects.get_for_model(b)
- >>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id,
- ... object_id=b.id)
- [<TaggedItem: django>, <TaggedItem: python>]
- Note that if the model in a
- :class:`~django.contrib.contenttypes.generic.GenericRelation` uses a
- non-default value for ``ct_field`` or ``fk_field`` in its
- :class:`~django.contrib.contenttypes.generic.GenericForeignKey` (e.g. the
- :mod:`django.contrib.comments` app uses ``ct_field="object_pk"``),
- you'll need to set ``content_type_field`` and/or ``object_id_field`` in
- the :class:`~django.contrib.contenttypes.generic.GenericRelation` to
- match the ``ct_field`` and ``fk_field``, respectively, in the
- :class:`~django.contrib.contenttypes.generic.GenericForeignKey`::
- comments = generic.GenericRelation(Comment, object_id_field="object_pk")
- Note also, that if you delete an object that has a
- :class:`~django.contrib.contenttypes.generic.GenericRelation`, any objects
- which have a :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
- pointing at it will be deleted as well. In the example above, this means that
- if a ``Bookmark`` object were deleted, any ``TaggedItem`` objects pointing at
- it would be deleted at the same time.
- Generic relations and aggregation
- ---------------------------------
- :doc:`Django's database aggregation API </topics/db/aggregation>`
- doesn't work with a
- :class:`~django.contrib.contenttypes.generic.GenericRelation`. For example, you
- might be tempted to try something like::
- Bookmark.objects.aggregate(Count('tags'))
- This will not work correctly, however. The generic relation adds extra filters
- to the queryset to ensure the correct content type, but the ``aggregate`` method
- doesn't take them into account. For now, if you need aggregates on generic
- relations, you'll need to calculate them without using the aggregation API.
- Generic relations in forms and admin
- ------------------------------------
- :mod:`django.contrib.contenttypes.generic` provides both a
- :class:`~django.contrib.contenttypes.generic.GenericInlineFormSet`
- and :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`.
- This enables the use of generic relations in forms and the admin. See the
- :doc:`model formset </topics/forms/modelforms>` and
- :doc:`admin </ref/contrib/admin/index>` documentation for more information.
- .. class:: generic.GenericInlineModelAdmin
- The :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`
- class inherits all properties from an
- :class:`~django.contrib.admin.options.InlineModelAdmin` class. However,
- it adds a couple of its own for working with the generic relation:
- .. attribute:: generic.GenericInlineModelAdmin.ct_field
- The name of the
- :class:`~django.contrib.contenttypes.models.ContentType` foreign key
- field on the model. Defaults to ``content_type``.
- .. attribute:: generic.GenericInlineModelAdmin.ct_fk_field
- The name of the integer field that represents the ID of the related
- object. Defaults to ``object_id``.
|