Django

Code

Ticket #4234: 4234.2.diff

File 4234.2.diff, 10.6 kB (added by ubernostrum, 1 year ago)

New patch with note about cascading delete of generic relations

  • docs/contenttypes.txt

    old new  
     1========================== 
     2The contenttypes framework 
     3========================== 
     4 
     5 
     6Django includes a "contenttypes" application which can track all of 
     7the models installed in your Django-powered project, providing a 
     8high-level, generic interface for working with your models. 
     9 
     10 
     11Overview 
     12======== 
     13 
     14At the heart of the contenttypes application is the ``ContentType`` 
     15model, which lives at 
     16``django.contrib.contenttypes.models.ContentType``; instances of 
     17``ContentType`` represent and store information about the models 
     18installed in your project, and new instances of ``ContentType`` are 
     19automatically created whenever new models are installed. 
     20 
     21Instances of ``ContentType`` have methods for returning the model 
     22classes they represent and for querying objects from those models. 
     23``ContentType`` also has a `custom manager`_ which adds methods for 
     24working with ``ContentType`` and for obtaining instances of 
     25``ContentType`` for a particular model. 
     26 
     27Relations between your models and ``ContentType`` can also be used to 
     28enable "generic" relationships between an instance of one of your 
     29models and instances of any model you have installed. 
     30 
     31.. _custom manager: ../model-api/#custom-managers 
     32 
     33 
     34Installing the contenttypes framework 
     35===================================== 
     36 
     37The contenttypes framework is included in the default 
     38``INSTALLED_APPS`` list created by ``django-admin.py startproject``, 
     39but 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 
     43It's generally a good idea to have the contenttypes framework 
     44installed; 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 
     58The ``ContentType`` model 
     59========================= 
     60 
     61Each instance of ``ContentType`` has three fields which, taken 
     62together, 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 
     78Let's look at an example to see how this works. If you already have 
     79the contenttypes application installed, and then add `the sites 
     80application`_ to your ``INSTALLED_APPS`` setting and run ``manage.py 
     81syncdb`` to install it, the model ``django.contrib.sites.models.Site`` 
     82will be installed into your database. Along with it a new instance 
     83of ``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 
     96Methods on ``ContentType`` instances 
     97==================================== 
     98 
     99Additionally, each ``ContentType`` instance has methods which allow 
     100you to easily get from a ``ContentType`` instance to the model it 
     101represents, 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 
     112So, for example, we could look up the ``ContentType`` for the ``User`` 
     113model:: 
     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 
     120And then use it to query for a particular ``User``, or to get access 
     121to 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 
     128Together, ``get_object_for_this_type`` and ``model_class`` enable two 
     129extremely important use cases: 
     130 
     1311. 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 
     1372. 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 
     141Several of Django's bundled applications make use of the latter 
     142technique. For example, `the permissions system`_ in Django's 
     143authentication framework uses a ``Permission`` model with a foreign 
     144key to ``ContentType``; this lets ``Permission`` represent concepts 
     145like "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 
     151The ``ContentTypeManager`` 
     152-------------------------- 
     153 
     154``ContentType`` also has a custom manager, ``ContentTypeManager``, 
     155which 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 
     168The ``get_for_model`` method is especially useful when you know you 
     169need to work with a ``ContentType`` but don't want to go to the 
     170trouble 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 
     178Generic relations 
     179================= 
     180 
     181Adding a foreign key from one of your own models to ``ContentType`` 
     182allows your model to effectively tie itself to another model class, as 
     183in the example of the ``Permission`` model above, but it's possible to 
     184go one step further and use ``ContentType`` to enable truly generic 
     185(sometimes called "polymorphic") relationships between models. 
     186 
     187A 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 
     202A normal ``ForeignKey`` can only "point to" one other model, which 
     203means that if the ``TaggedItem`` model used a ``ForeignKey`` it would have to 
     204choose one and only one model to store tags for. The contenttypes 
     205application provides a special field type -- 
     206``django.contrib.contenttypes.generic.GenericForeignKey`` -- which 
     207works around this and allows the relationship to be with any 
     208model. There are three parts to setting up a ``GenericForeignKey``: 
     209 
     2101. Give your model a ``ForeignKey`` to ``ContentType``. 
     211 
     2122. 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 
     2163. 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 
     221This will enable an API similar to the one used for a normal 
     222``ForeignKey``; each ``TaggedItem`` will have a ``content_object`` 
     223field which returns the object it's related to, and you can also 
     224assign 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 
     233Reverse generic relations 
     234------------------------- 
     235 
     236If you know which models you'll be using most often, you can also add 
     237a "reverse" generic relationship to enable an additional API. For 
     238example:: 
     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 
     245be 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 
     256If you don't add the reverse relationship, you can simply perform the 
     257lookup 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 
     265Note that if you delete an object which has a ``GenericRelation``, 
     266any objects which have a ``GenericForeignKey`` pointing at it will be 
     267deleted as well; in the example above, this means that if a 
     268``Bookmark`` object were deleted, any ``TaggedItem`` objects pointing 
     269at it would be deleted at the same time.