Kerangka contenttypes

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.

Ikhtisar

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.

Memasang kerangka contenttypes

The contenttypes framework is included in the default INSTALLED_APPS list created by django-admin 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.

Model ContentType

class ContentType

Each instance of ContentType has two 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

Nama dari kelas model.

Additionally, the following property is available:

name

The human-readable name of the content type. 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 migrate 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 akan disetel menjadi 'sites' (bagian terakhir dari jalur Python django.contrib.sites).

  • model akan disetel menjadi 'site'.

Metode pada instance ContentType

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:

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

ContentType.model_class()

Mengembalikan kelas model diwakilkan oleh instance ContentType ini.

For example, we could look up the ContentType for the User model:

>>> from django.contrib.contenttypes.models import ContentType
>>> ContentType.objects.get(app_label="auth", model="user")
<ContentType: user>

Dan kemudian gunakan itu pada permintaan untuk User tertentu, atau untuk mendapatkan akses ke kelas model User:

>>> user_type.model_class()
<class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username='Guido')
<User: Guido>

Bersama-sama, meth:~django.contrib.contenttypes.models.ContentType.get_object_for_this_type dan model_class() mengadakan dua sangat penting penggunaan kasus:

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

ContentTypeManager

class ContentTypeManager

ContentType juga mempunyai pengelola penyesuaian, ContentTypeManager, yang menambahkan metode berikut:

clear_cache()

Bersihkan cache internal digunakan oleh ContentType untuk menjaga lintasan dari model-model untuk yang itu telah membuat instance ContentType. Anda mungkin tidak pernah butuh memanggil metode ini anda sendiri; Django akan memanggil itu secara otomatis ketika itu dibutuhkan.

get_for_id(id)

Lookup a ContentType by ID. Since this method uses the same shared cache as get_for_model(), it’s preferred to use this method over the usual ContentType.objects.get(pk=id)

get_for_model(model, for_concrete_model=True)

Takes either a model class or an instance of a model, and returns the ContentType instance representing that model. for_concrete_model=False allows fetching the ContentType of a proxy model.

get_for_models(*models, for_concrete_models=True)

Takes a variadic number of model classes, and returns a dictionary mapping the model classes to the ContentType instances representing them. for_concrete_models=False allows fetching the ContentType of proxy models.

get_by_natural_key(app_label, model)

Returns the ContentType instance uniquely identified by the given application label and model name. The primary purpose of this method is to allow ContentType objects to be referenced via a natural key during deserialization.

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
>>> ContentType.objects.get_for_model(User)
<ContentType: user>

Hubungan umum

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.

Contoh sederhana adalah sistem etiket, yang mungkin kelihatan seperti ini:

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class TaggedItem(models.Model):
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):              # __unicode__ on Python 2
        return self.tag

Sebuah ForeignKey biasa dapat hanya “menunjuk” satu model lain, yang berarti bahwa jika model TaggedItem menggunakan sebuah ForeignKey itu akan harus memilih satu dan hanya satu model untuk menyimpan etiket-etiket. Aplikasi contenttype menyediakan jenis bidang khusus (GenericForeignKey) yang memecahkan ini dan mengizinkan hubungan dengan model apapun.

class GenericForeignKey

Terdapat tiga bagian untuk mengatur GenericForeignKey:

  1. Give your model a ForeignKey to ContentType. The usual name for this field is “content_type”.
  2. Give your model a field that can store primary key values from the models you’ll be relating to. For most models, this means a PositiveIntegerField. The usual name for this field is “object_id”.
  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.
for_concrete_model

If False, the field will be able to reference proxy models. Default is True. This mirrors the for_concrete_model argument to get_for_model().

Kesesuaian jenis primary key

The “object_id” field doesn’t have to be the same type as the primary key fields on the related models, but their primary key values must be coercible to the same type as the “object_id” field by its get_db_prep_value() method.

For example, if you want to allow generic relations to models with either IntegerField or CharField primary key fields, you can use CharField for the “object_id” field on your model since integers can be coerced to strings by get_db_prep_value().

For maximum flexibility you can use a TextField which doesn’t have a maximum length defined, however this may incur significant performance penalties depending on your database backend.

There is no one-size-fits-all solution for which field type is best. You should evaluate the models you expect to be pointing to and determine which solution will be most effective for your use case.

Menserialisasikan acuan pada obyek ContentType

If you’re serializing data (for example, when generating fixtures) from a model that implements generic relations, you should probably be using a natural key to uniquely identify related ContentType objects. See natural keys and dumpdata --natural-foreign for more information.

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.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 GenericForeignKey is implemented, you cannot use such fields directly with filters (filter() and exclude(), for example) via the database API. Because a GenericForeignKey isn’t a normal field object, these examples will not work:

# This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)

Likewise, GenericForeignKeys does not appear in ModelForms.

Membalikkan hubungan umum

class GenericRelation
related_query_name

The relation on the related object back to this object doesn’t exist by default. Setting related_query_name creates a relation from the related object back to this one. This allows querying and filtering from the related object.

Jika anda mengetahui model-model mana anda akan menggunakan paling sering, anda dapat juga menambahkan hubungan umum “reverse” untuk mengadakan sebuah tambahan API. Sebagai contoh:

from django.db import models
from django.contrib.contenttypes.fields import GenericRelation

class Bookmark(models.Model):
    url = models.URLField()
    tags = GenericRelation(TaggedItem)

Bookmark instances will each have a tags attribute, which can be used to retrieve their associated TaggedItems:

>>> b = Bookmark(url='https://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()
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>

Defining GenericRelation with related_query_name set allows querying from the related object:

tags = GenericRelation(TaggedItem, related_query_name='bookmarks')

Ini mengadakan penyaring, oengurutan, dan tindakan permintaan lain pada Bookmark dari TaggedItem:

>>> # Get all tags belonging to bookmarks containing `django` in the url
>>> TaggedItem.objects.filter(bookmarks__url__contains='django')
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>

Tentu saja, jika anda tidak menambah membalikkan hubungan, anda dapat melakukan jenis-jenis sama dari pencarian secara manual:

>>> b = Bookmark.objects.get(url='https://www.djangoproject.com/')
>>> bookmark_type = ContentType.objects.get_for_model(b)
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, object_id=b.id)
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>

Just as 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 = GenericRelation(
    TaggedItem,
    content_type_field='content_type_fk',
    object_id_field='object_primary_key',
)

Note also, 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.

Unlike ForeignKey, GenericForeignKey does not accept an on_delete argument to customize this behavior; if desired, you can avoid the cascade-deletion simply by not using GenericRelation, and alternate behavior can be provided via the pre_delete signal.

Hubungan umum dan pengumpulan

Django’s database aggregation API bekerja dengan GenericRelation. Sebagai contoh, anda dapat menemukan berapa banyak etiket semua bookmark miliki:

>>> Bookmark.objects.aggregate(Count('tags'))
{'tags__count': 3}

Hubungan umum di formulir

Modul django.contrib.contenttypes.forms menyediakan:

class BaseGenericInlineFormSet
generic_inlineformset_factory(model, form=ModelForm, formset=BaseGenericInlineFormSet, ct_field="content_type", fk_field="object_id", fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, validate_max=False, for_concrete_model=True, min_num=None, validate_min=False)

Mengembalikan GenericInlineFormSet menggunakan modelformset_factory().

Anda harus menyediakan ct_field dan fk_field jika mereka berbeda dari awalan, content_type dan object_id masing-masing. Parameter lain adalah mirip ke yang didokumentasi di modelformset_factory() dan inlineformset_factory().

The for_concrete_model argument corresponds to the for_concrete_model argument on GenericForeignKey.

Hubungan umum di admin

Modul django.contrib.contenttypes.admin menyediakan GenericTabularInline dan GenericStackedInline (subkelas-subkelas dari GenericInlineModelAdmin)

Kelas-kelas dan fungsi-fungsi ini mengadakan penggunaan hubungan umum di formulir dan admin. Lihat dokumentasi model formset dan admin untuk informasi lebih.

class GenericInlineModelAdmin

Kelas GenericInlineModelAdmin mewarisi semua sifat-sifat dari sebuah kelas InlineModelAdmin. Bagaimanapun, itu menambahkan sebuah pasang dari itu sendiri untuk bekerja dengan hubungan umum:

ct_field

The name of the ContentType foreign key field on the model. Defaults to content_type.

ct_fk_field

Nama dari bidang integer yang mewakili ID dari obyek terkait. Awalan pada object_id.

class GenericTabularInline
class GenericStackedInline

Subkelas-subkelas dari GenericInlineModelAdmin dengan tata letak bertumpuk dan datar, masing-masing.