Bagaimana tepatnya tipe konten Django bekerja?

148

Saya benar-benar mengalami kesulitan memahami konsep tipe konten Django. Rasanya sangat retas dan, pada akhirnya, menentang bagaimana Python cenderung melakukan sesuatu. Yang sedang berkata, jika saya akan menggunakan Django maka saya harus bekerja dalam batas-batas kerangka kerja.

Jadi saya datang ke sini bertanya-tanya apakah ada yang bisa memberikan contoh dunia nyata yang praktis tentang bagaimana jenis konten bekerja dan bagaimana Anda akan mengimplementasikannya. Hampir semua tutorial (kebanyakan di blog) yang saya ulas tidak melakukan pekerjaan dengan baik yang benar-benar membahas konsep tersebut. Mereka tampaknya mengambil di mana dokumentasi Django tinggalkan (apa yang tampaknya seperti mana-mana).

Chris Shelton
sumber
5
Saya percaya (seseorang mengoreksi saya jika saya salah) bahwa tipe konten adalah sesuatu seperti polimorfisme, itu akan menjadi alat di tangan Anda begitu proyek Anda mulai memiliki model yang dapat memiliki berbagai bentuk. Contoh tag dalam dokumentasi ini cukup mudah, Anda ingin dapat menandai item, tetapi Anda tidak ingin secara spesifik untuk jenis barang apa itu, setelah semua tag dapat mendukung, posting, halaman, pengguna, produk. Dengan menggunakan tipe Konten Anda dapat membuat hubungan ke berbagai implementasi yang berbeda tanpa harus tahu apa sebenarnya model yang terkait.
petkostas
1
Oke, jadi tempat saya tersandung adalah mereka membuat kelas bernama "TaggedItem" yang tidak jelas bagi saya. Saya tidak yakin kalau itu TaggedItem adalah kelas "jembatan" placeholder. Kecenderungan alami saya akan menjadi sesuatu seperti "Tag" dengan properti bernama "istilah".
Chris Shelton

Jawaban:

307

Jadi, Anda ingin menggunakan kerangka Jenis Konten pada pekerjaan Anda?

Mulailah dengan bertanya pada diri sendiri pertanyaan ini: "Apakah ada dari model ini yang perlu dikaitkan dengan cara yang sama dengan model lain dan / atau akankah saya menggunakan kembali hubungan ini dengan cara yang tidak terduga nanti di ujung jalan?" Alasan mengapa kami mengajukan pertanyaan ini adalah karena inilah yang paling baik dilakukan oleh kerangka Jenis Konten: ini menciptakan hubungan generik antar model. Blah blah, mari selami beberapa kode dan lihat apa yang saya maksud.

# 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)

Oke, jadi kami punya cara untuk membuat hubungan ini secara teoritis. Namun, sebagai programmer Python, kecerdasan atasan Anda memberi tahu Anda ini menyebalkan dan Anda bisa berbuat lebih baik. Tinggi lima!

Masukkan kerangka Jenis Konten!

Nah, sekarang kita akan melihat lebih dekat pada model kita dan mengolahnya menjadi lebih "dapat digunakan kembali" dan intuitif. Mari kita mulai dengan menyingkirkan dua kunci asing pada Commentmodel kita dan menggantinya dengan a 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()

Jadi apa yang terjadi? Yah, kami masuk dan menambahkan kode yang diperlukan untuk memungkinkan hubungan generik dengan model lain. Perhatikan bagaimana ada lebih dari sekedar GenericForeignKey, tetapi juga ForeignKeyuntuk ContentTypedan PositiveIntegerFielduntuk object_id. Bidang-bidang ini untuk memberi tahu Django jenis objek yang terkait dengan ini dan apa id untuk objek itu. Pada kenyataannya, ini masuk akal karena Django akan membutuhkan keduanya untuk mencari objek terkait ini.

Yah, itu tidak seperti Python ... itu agak jelek!

Anda mungkin mencari kode yang ketat, bersih, dan intuitif yang membuat Guido van Rossum bangga. Aku mengerti kamu Mari kita lihat GenericRelationlapangan sehingga kita bisa membungkuk pada ini.

# 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')

Bam! Sama seperti itu Anda dapat bekerja dengan Komentar untuk dua model ini. Bahkan, mari kita lakukan dan lakukan itu di shell kita (ketik python manage.py shelldari direktori proyek Django Anda).

>>> 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!"]

Sesederhana itu.

Apa implikasi praktis lain dari hubungan "generik" ini?

Kunci asing generik memungkinkan hubungan yang kurang mengganggu antara berbagai aplikasi. Sebagai contoh, katakanlah kita menarik model Komentar keluar ke aplikasi itu sendiri bernama chatterly. Sekarang kami ingin membuat aplikasi lain bernamanoise_nimbus mana orang menyimpan musik mereka untuk dibagikan kepada orang lain.

Bagaimana jika kita ingin menambahkan komentar ke lagu-lagu itu? Kita bisa menggambar relasi generik:

# 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)

Saya harap kalian menemukan ini membantu karena saya akan senang menemukan sesuatu yang menunjukkan kepada saya aplikasi GenericForeignKeydan GenericRelationbidang yang lebih realistis .

Apakah ini terlalu bagus untuk menjadi kenyataan?

Seperti apa pun dalam hidup, ada pro dan kontra. Setiap kali Anda menambahkan lebih banyak kode dan abstraksi lebih banyak, proses yang mendasarinya menjadi lebih berat dan sedikit lebih lambat. Menambahkan hubungan umum dapat menambahkan sedikit peredam kinerja meskipun faktanya itu akan mencoba dan smart cache hasilnya. Secara keseluruhan, ini tergantung pada apakah kebersihan dan kesederhanaan melebihi biaya kinerja yang kecil. Bagi saya, jawabannya adalah sejuta kali ya.

Ada lebih banyak kerangka kerja Jenis Konten daripada yang saya tampilkan di sini. Ada tingkat granularitas dan penggunaan verbose yang lebih banyak, tetapi untuk rata-rata individu, ini adalah bagaimana Anda akan menggunakannya 9 dari 10 kali menurut saya.

Berhubungan dengan generik (?) Berhati-hatilah!

Peringatan yang agak besar adalah bahwa ketika Anda menggunakan GenericRelation, jika model yang telah GenericRelationditerapkan ( Picture) dihapus, semua objek terkait ( Comment) juga akan dihapus. Atau setidaknya pada saat penulisan ini.

Chris Shelton
sumber
11
Jadi jika saya menggunakan GenericRelationdi Postdan Picturekemudian aku tidak perlu digunakan object_id, content_typedan content_objectdi Comment?
avi
5
Akan menyenangkan untuk memiliki deskripsi yang rapi tentang kerangka kontentype di suatu tempat dalam dokumentasi resmi Django. Sedangkan saya, saya menyadari apa yang dilakukan kerangka kerja ini hanya setelah membaca port ini. Terima kasih.
prokher
2
sedikit terlambat ... tetapi saya telah mendengar bahwa menggunakan kerangka jenis konten, aplikasi Anda mungkin tidak skala dengan benar. dapatkah seseorang memberi tahu saya apakah ini benar atau bohong?
Karan Kumar
1
Seperti semua hal dalam pemrograman, Karan, jawabannya selalu "itu tergantung." Saya akan mengatakan menggunakan tipe konten. Ini adalah "kompromi" macam untuk memotong beberapa dasar-dasar kaku dari sistem SQL yang berorientasi tabel. Jangan terlalu dini mengoptimalkan aplikasi Anda! Django adalah cara terbaik untuk keluar dari jalan Anda sehingga Anda dapat menulis aplikasi generasi berikutnya yang selalu Anda inginkan: gunakan fitur itu untuk keuntungan Anda!
Chris Shelton
2
Karan, ada kebenarannya. Saya sedang mengerjakan aplikasi yang melacak notifikasi untuk pengguna. Setiap pemberitahuan memiliki hubungan GenericForeignKey dengan beberapa jenis konten lain yang kami simpan. Setiap kali pengguna melihat pemberitahuan, ORM mengeluarkan N meminta untuk mendapatkan semua konten terkait. Sangat tidak ideal.
Travis Mehlinger
-2

Baiklah jawaban langsung untuk pertanyaan Anda: (dari kode sumber Django) adalah: Jenis Media yang diuraikan menurut RFC 2616, bagian 3.7.

Yang merupakan cara air mata untuk mengatakan bahwa ia membaca / mengizinkan-Anda-memodifikasi / melewati 'tipe-konten' header httpd .

Namun, Anda meminta contoh penggunaan lebih banyak latihan. Saya punya 2 saran untuk Anda:

1: periksa kode ini

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: ingat Django adalah python, dan karena itu memegang kekuatan komunitas python. Ada 2 plugin RESTFul yang mengagumkan untuk Django. Jadi, jika Anda ingin melihat seberapa dalam keseluruhan kelinci Anda bisa melihat.

Saya sarankan melalui tutorial django-rest-framework yang akan membahas 'bertindak pada konten / tipe yang berbeda' secara khusus. Catatan: Ini adalah praktik umum untuk menggunakan header tipe konten ke 'versi' API yang tenang .

Jeff Sheffield
sumber
1
Apakah itu yang dia maksudkan? atau ke kerangka kontentype
petkostas
1
Ya, saya mengacu pada kerangka jenis konten. Saya mungkin tidak melakukan pekerjaan yang cukup baik untuk menyampaikan sendiri. Saya menghargai responnya. Untuk apa nilainya, jika itu pertanyaan saya, Anda akan menjatuhkannya dari taman =)
Chris Shelton