From e90eb41a4a1814ce3524fd46d97b1c32e537b9ea Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sat, 15 Sep 2007 18:29:09 +0000 Subject: [PATCH] Fixed #4234 -- Added docs/contenttypes. Thanks, ubernostrum git-svn-id: http://code.djangoproject.com/svn/django/trunk@6302 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/contenttypes.txt | 258 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 docs/contenttypes.txt diff --git a/docs/contenttypes.txt b/docs/contenttypes.txt new file mode 100644 index 0000000000..3ef83f2066 --- /dev/null +++ b/docs/contenttypes.txt @@ -0,0 +1,258 @@ +========================== +The contenttypes framework +========================== + +Django includes a "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 ``ContentType`` +model, which lives at +``django.contrib.contenttypes.models.ContentType``. Instances of +``ContentType`` represent and store information about the models +installed in your project, and new instances of ``ContentType`` are +automatically created whenever new models are installed. + +Instances of ``ContentType`` have methods for returning the model +classes they represent and for querying objects from those models. +``ContentType`` also has a `custom manager`_ that adds methods for +working with ``ContentType`` and for obtaining instances of +``ContentType`` for a particular model. + +Relations between your models and ``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. + +.. _custom manager: ../model-api/#custom-managers + +Installing the contenttypes framework +===================================== + +The contenttypes framework is included in the default +``INSTALLED_APPS`` list created by ``django-admin.py startproject``, +but if you've removed it or if you manually set up your +``INSTALLED_APPS`` list, you can enable it by adding +``'django.contrib.contenttypes'`` to your ``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 `authentication framework`_ uses it to tie user permissions + to specific models. + + * Django's comments system (``django.contrib.comments``) uses it to + "attach" comments to any installed model. + +.. _authentication framework: ../authentication/ + +The ``ContentType`` model +========================= + +Each instance of ``ContentType`` has three fields which, taken +together, uniquely describe an installed model: + + ``app_label`` + The name of the application the model is part of. This is taken from + the ``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 ``app_label`` + of "contenttypes". + + ``model`` + The name of the model class. + + ``name`` + The human-readable name of the model. This is taken from + `the 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 `the sites +application`_ to your ``INSTALLED_APPS`` setting and run ``manage.py +syncdb`` to install it, the model ``django.contrib.sites.models.Site`` +will be installed into your database. Along with it a new instance +of ``ContentType`` will be created with the following values: + + * ``app_label`` will be set to ``'sites'`` (the last part of the Python + path "django.contrib.sites"). + + * ``model`` will be set to ``'site'``. + + * ``name`` will be set to ``'site'``. + +.. _the verbose_name attribute: ../model-api/#verbose_name +.. _the sites application: ../sites/ + +Methods on ``ContentType`` instances +==================================== + +Each ``ContentType`` instance has methods that allow you to get from a +``ContentType`` instance to the model it represents, or to retrieve objects +from that model: + + ``get_object_for_this_type(**kwargs)`` + Takes a set of valid `lookup arguments`_ for the model the + ``ContentType`` represents, and does `a get() lookup`_ on that + model, returning the corresponding object. + + ``model_class()`` + Returns the model class represented by this ``ContentType`` + instance. + +For example, we could look up the ``ContentType`` for the ``User`` model:: + + >>> from django.contrib.contenttypes.models import ContentType + >>> user_type = ContentType.objects.get(app_label="auth", model="user") + >>> user_type + + +And then use it to query for a particular ``User``, or to get access +to the ``User`` model class:: + + >>> user_type.model_class() + + >>> user_type.get_object_for_this_type(username='Guido') + + +Together, ``get_object_for_this_type`` and ``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 ``ContentType`` lookup at runtime, and then + work with the model class or retrieve objects from it. + + 2. You can relate another model to ``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, `the permissions system`_ in Django's +authentication framework uses a ``Permission`` model with a foreign +key to ``ContentType``; this lets ``Permission`` represent concepts +like "can add blog entry" or "can delete news story". + +.. _lookup arguments: ../db-api/#field-lookups +.. _a get() lookup: ../db-api/#get-kwargs +.. _the permissions system: ../authentication/#permissions + +The ``ContentTypeManager`` +-------------------------- + +``ContentType`` also has a custom manager, ``ContentTypeManager``, +which adds the following methods: + + ``clear_cache()`` + Clears an internal cache used by ``ContentType`` to keep track of which + models for which it has created ``ContentType`` instances. You probably + won't ever need to call this method yourself; Django will call it + automatically when it's needed. + + ``get_for_model(model)`` + Takes either a model class or an instance of a model, and returns the + ``ContentType`` instance representing that model. + +The ``get_for_model`` method is especially useful when you know you +need to work with a ``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 + + +Generic relations +================= + +Adding a foreign key from one of your own models to ``ContentType`` +allows your model to effectively tie itself to another model class, as +in the example of the ``Permission`` model above. But it's possible to +go one step further and use ``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 ``ForeignKey`` can only "point to" one other model, which +means that if the ``TaggedItem`` model used a ``ForeignKey`` it would have to +choose one and only one model to store tags for. The contenttypes +application provides a special field type -- +``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 ``GenericForeignKey``: + + 1. Give your model a ``ForeignKey`` to ``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 + ``IntegerField`` or ``PositiveIntegerField``.) + + 3. Give your model a ``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 ``GenericForeignKey`` will look for. + +This will enable an API similar to the one used for a normal ``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.models.auth import User + >>> guido = User.objects.get(username='Guido') + >>> t = TaggedItem(content_object=guido, tag='bdfl') + >>> t.save() + >>> t.content_object + + +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('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() + [, ] + +If you don't add the reverse relationship, you can do the lookup 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) + [, ] + +Note that if you delete an object that has a ``GenericRelation``, any objects +which have a ``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.