Djangoコンテンツタイプはどのように正確に機能しますか?


148

Djangoのコンテンツタイプの概念を理解するのに本当に苦労しています。それは非常にハックに感じられ、最終的には、Pythonがどのように物事を行う傾向があるかに反対しています。つまり、Djangoを使用する場合は、フレームワークの範囲内で作業する必要があります。

だから私はここに来て、コンテンツタイプがどのように機能し、どのようにそれを実装するかについての実際的な実例を誰かが提供できるかどうか疑問に思っています。私がレビューしたほとんどすべてのチュートリアル(主にブログ)は、コンセプトを実際にカバーする素晴らしい仕事をしていません。彼らは、Djangoのドキュメントが中断したところ(どこにもないように見えます)を取り上げているようです。


5
Contentタイプは多態性のようなものであると私は信じています(私が間違っていれば誰かが訂正します)。プロジェクトがさまざまな形のモデルを持つようになると、それは手に入るツールになります。ドキュメントのタグの例は非常に簡単です。アイテムにタグを付けることができるようにしたいが、すべてのタグが投稿、ページ、ユーザーをサポートできるようになった後、それらがどのようなアイテムであるかを特定したくはありません。製品。コンテンツタイプを使用すると、関連するモデルが正確に何であるかを知らなくても、さまざまな異なる実装への関係を作成できます。
petkostas 2014年

1
さて、私がつまずいたのは、彼らが「TaggedItem」という名前のクラスを作成したことです。そのとき、TaggedItemがプレースホルダーの「ブリッジ」クラスであるかどうかはわかりませんでした。私の自然な傾向は「term」という名前のプロパティを持つ「Tag」のようなものでした。
Chris Shelton

回答:


307

それで、あなたはあなたの仕事でコンテンツタイプフレームワークを使いたいですか?

「これらのモデルのいずれかを他のモデルと同じように関連付ける必要がありますか、そして/または将来、予期しない方法でこれらの関係を再利用しますか?」私たちがこの質問をする理由は、これがコンテンツタイプフレームワークが最も得意とすることであり、モデル間に一般的な関係を作成します。何とか何とか、いくつかのコードに飛び込んで、私が何を意味するのか見てみましょう。

# ourapp.models
from django.conf import settings
from django.db import models

# Assign the User model in case it has been "swapped"
User = settings.AUTH_USER_MODEL

# Create your models here
class Post(models.Model):
  author = models.ForeignKey(User)
  title = models.CharField(max_length=75)
  slug = models.SlugField(unique=True)
  body = models.TextField(blank=True)

class Picture(models.Model):
  author = models.ForeignKey(User)
  image = models.ImageField()
  caption = models.TextField(blank=True)

class Comment(models.Model):
  author = models.ForeignKey(User)
  body = models.TextField(blank=True)
  post = models.ForeignKey(Post)
  picture = models.ForeignKey(Picture)

さて、私たちは理論的にこの関係を作成する方法を持っています。しかし、Pythonプログラマーとして、あなたの優れた知性は、これはひどいことであり、あなたはもっと上手くできると教えています。ハイタッチ!

コンテンツタイプフレームワークに入りましょう!

それでは、モデルを詳しく見て、より「再利用可能」で直感的になるようにモデルを作り直します。まず、Commentモデルの2つの外部キーを削除して、それらをに置き換えますGenericForeignKey

# ourapp.models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

...

class Comment(models.Model):
  author = models.ForeignKey(User)
  body = models.TextField(blank=True)
  content_type = models.ForeignKey(ContentType)
  object_id = models.PositiveIntegerField()
  content_object = GenericForeignKey()

どうしたの?さて、他のモデルとの一般的な関係を可能にするために必要なコードを追加しました。お知らせちょうどより多くありますどのようにGenericForeignKey、また ForeignKeyContentTypeしてPositiveIntegerFieldのためにobject_id。これらのフィールドは、Djangoに関連するオブジェクトのタイプと、そのオブジェクトのIDを通知するためのものです。Djangoはこれらの関連オブジェクトを検索するために両方が必要になるため、実際にはこれは理にかなっています。

まあ、それはあまりPython風ではありません...ちょっと醜いです!

おそらく、Guido van Rossumを誇りに思う気密性のある、きれいで直感的なコードを探しています。わかった GenericRelation私たちはこれにかなりの弓をつけることができるようにフィールドを見てみましょう。

# ourapp.models
from django.contrib.contenttypes.fields import GenericRelation

...

class Post(models.Model):
  author = models.ForeignKey(User)
  title = models.CharField(max_length=75)
  slug = models.SlugField(unique=True)
  body = models.TextField(blank=True)
  comments = GenericRelation('Comment')

class Picture(models.Model):
  author = models.ForeignKey(User)
  image = models.ImageField()
  caption = models.TextField(blank=True)
  comments = GenericRelation('Comment')

バム!同様に、これら2つのモデルのコメントを操作できます。実際、それをシェルで実行してみましょう(python manage.py shellDjangoプロジェクトディレクトリから入力)。

>>> from django.contrib.auth import get_user_model
>>> from ourapp.models import Picture, Post

# We use get_user_model() since we are referencing directly
User = get_user_model()

# Grab our own User object
>>> me = User.objects.get(username='myusername')

# Grab the first of our own pictures so we can comment on it
>>> pic = Picture.objects.get(author=me)

# Let's start making a comment for our own picture
>>> pic.comments.create(author=me, body="Man, I'm cool!")

# Let's go ahead and retrieve the comments for this picture now
>>> pic.comments.all()
[<Comment: "Man, I'm cool!">]

# Same for Post comments
>>> post = Post.objects.get(author=me)
>>> post.comments.create(author=me, body="So easy to comment now!")
>>> post.comments.all()
[<Comment: "So easy to comment now!"]

とても簡単です。

これらの「一般的な」関係の他の実際的な意味は何ですか?

ジェネリック外部キーを使用すると、さまざまなアプリケーション間の煩わしい関係を減らすことができます。たとえば、Commentモデルをという独自のアプリに引き出したとしましょうchatterly。次にnoise_nimbus、音楽を保存して他の人と共有するという名前の別のアプリケーションを作成します。

それらの曲にコメントを追加したい場合はどうなりますか?まあ、私たちは一般的な関係を描くことができます:

# noise_nimbus.models
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models

from chatterly.models import Comment

# For a third time, we take the time to ensure custom Auth isn't overlooked
User = settings.AUTH_USER_MODEL

# Create your models here
class Song(models.Model):
  '''
  A song which can be commented on.
  '''
  file = models.FileField()
  author = models.ForeignKey(User)
  title = models.CharField(max_length=75)
  slug = models.SlugField(unique=True)
  description = models.TextField(blank=True)
  comments = GenericRelation(Comment)

GenericForeignKeyGenericRelationフィールドのより現実的なアプリケーションを示す何かに出会うのが大好きだったので、皆さんがこれが役に立ったことを願っています。

これは本当であるには余りにも良いですか?

人生のあらゆるものと同様に、長所と短所があります。コードと抽象化を追加するたびに、基礎となるプロセスが重くなり、少し遅くなります。ジェネリックリレーションを追加すると、結果をスマートキャッシュしようとするにもかかわらず、パフォーマンスダンパーが少し追加されます。全体として、クリーンさとシンプルさが小さなパフォーマンスコストを上回っているかどうかにかかっています。私にとっては、答えは100万回そうです。

コンテンツタイプフレームワークには、ここに表示した以上のものがあります。全体的なレベルの粒度とより詳細な使用法がありますが、平均的な個人の場合、これは、私の意見では10回のうち9回使用する方法です。

一般的な関係者(?)は注意してください!

かなり大きな注意点は、を使用するGenericRelation場合、GenericRelation適用された(Picture)を持つモデルが削除されると、関連するすべての(Comment)オブジェクトも削除されることです。または、少なくともこの執筆時点で。


11
私が使用している場合はそうGenericRelationではPostPicture、私は使用する必要はありませんobject_idcontent_typecontent_objectではComment
avi 2015年

5
公式のDjangoドキュメントのどこかにcontenttypeフレームワークのそのような明確な説明があればよいでしょう。私については、この移植版を読んだ後にのみ、このフレームワークが何をするかを理解しました。ありがとうございました。
prokher

2
少し遅れましたが、コンテンツタイプフレームワークを使用すると、アプリケーションが適切にスケーリングされない可能性があると聞きました。これが本当かデマかを誰かに教えてもらえますか?
Karan Kumar

1
プログラミングのすべてと同じように、カラン、答えは常に「依存する」です。コンテンツタイプを使用すると思います。テーブル指向のSQLシステムの厳格な基本の一部をバイパスすることは、一種の「妥協」です。アプリを時期尚早に最適化しないでください!Djangoは、あなたがいつも望んでいた次世代のアプリケーションを作成できるように、あなたの邪魔にならないようにするのに最適です。
Chris Shelton

2
カラン、それにはいくつかの真実があります。ユーザーへの通知を追跡するアプリケーションを開発しています。各通知には、保存する他のタイプのコンテンツとGenericForeignKeyの関係があります。ユーザーが通知を表示するたびに、ORMはN個のクエリを発行してすべての関連コンテンツを取得します。ほとんど理想的ではありません。
Travis Mehlinger 2016

-2

(djangoソースコードからの)質問に対する直接の回答は次のとおりです 。RFC2616のセクション3.7に従ってメディアタイプを解析します。

これは、「Content-type」 httpdヘッダーに沿って読み取り/変更を許可/渡すことを示す涙の方法です。

ただし、より実践的な使用例を求めています。2つの提案があります。

1:このコードを調べる

def index(request):
   media_type='text/html'
   if request.META.has_key('CONTENT_TYPE'):
      media_type = request.META['CONTENT_TYPE'].split(';')[0]

   if media_type.lower() == 'application/json':
      return HttpResponse("""{ "ResponseCode": "Success"}""", content_type="application/json; charset=UTF-8")

   return HttpResponse("<h1>regular old joe</h1>");

2:djangoはPythonであることを忘れないでください。Pythonコミュニティの力を発揮します。djangoには2つの素晴らしいRESTFulプラグインがあります。ですから、ウサギの全体がどれだけ深いかを知りたい場合は、チェックしてください。

特に、「異なるコンテンツ/タイプでのアクション」に対処するdjango-rest-frameworkチュートリアルを実行することをお勧めします。注:content-typeヘッダーを使用してRESTful API「バージョン」するが一般的な方法です。


1
それは彼が言っていることですか?またはcontenttypesフレームワークに?:docs.djangoproject.com/en/dev/ref/contrib/contenttypes
petkostas

1
ええ、私はコンテンツタイプフレームワークを参照していました。私は自分を伝えるのに十分な仕事をしていないかもしれません。とにかく対応に感謝します。その価値については、それが私の質問であれば、あなたはそれを公園から追い出したでしょう=)
Chris Shelton
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.