Як саме працюють типи вмісту Django?


148

Мені справді важко зрозуміти поняття змісту типів Джанго. Це дуже хакерське і, зрештою, проти того, як Python має тенденцію робити справи. Якщо говорити, якщо я буду використовувати Django, то мені доведеться працювати в рамках.

Тож я приїжджаю сюди, цікаво, чи може хтось навести практичний приклад у реальному світі того, як працює тип контенту та як ви його реалізуєте. Майже всі навчальні посібники (в основному в блогах), які я переглядав, не роблять великої роботи, дійсно висвітлюючи концепцію. Вони ніби забирають там, де зупинилася документація Джанго (що здається ніде).


5
Я вважаю (хтось мене виправляє, якщо я помиляюся), що типи вмісту - це щось на зразок поліморфізму, він стане інструментом у ваших руках, як тільки ваш проект почне мати моделі, які можуть мати багато різних форм. Приклад тегів у документації є досить прямим, ви хочете мати можливість тегувати елементи, але ви не хочете конкретизувати, які саме вони є, адже тег може підтримувати, публікації, сторінки, користувачі, продукції. Використовуючи типи контенту, ви можете створювати відносини до різних різних реалізацій, не знаючи, що саме стосується моделі.
petkostas

1
Гаразд, там, де я потрапив, це те, що вони склали клас під назвою "TaggedItem", який мені не був зрозумілий. Тоді я не був впевнений, чи TaggedItem був заповнювачем класу "міст". Моя природна схильність була б чимось на кшталт "Тег" із властивістю під назвою "термін".
Кріс Шелтон

Відповіді:


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моделі та замінити їх на 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, але також і ForeignKeyдо, ContentTypeі PositiveIntegerFieldдля object_id. Ці поля призначені для того, щоб сказати Джанго, з яким типом об'єкта це пов’язано та який ідентифікатор для цього об’єкта. Насправді це має сенс, оскільки Джанго знадобиться обидва для пошуку цих пов'язаних об’єктів.

Ну, це не дуже подобається Python ... його якось потворно!

Ви, напевно, шукаєте герметичний, бездоганний, інтуїтивний код, який би зробив Гвідо ван Россума гордим. Я вас отримую. Давайте подивимось на 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')

Бам! Так само ви можете працювати з коментарями для цих двох моделей. Насправді, давайте продовжимо це робити в нашій оболонці (введіть python manage.py shellу каталозі свого проекту Django).

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

Це так просто.

Які ще практичні наслідки цих "родових" відносин?

Узагальнені зовнішні ключі дозволяють мати менш нав'язливі відносини між різними програмами. Наприклад, скажімо, що ми витягнули модель коментаря у власну програму під назвою 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)

Я сподіваюся, ви, хлопці, знайшли це корисним, бо я б хотів, щоб натрапив на щось, що показало мені більш реалістичне застосування GenericForeignKeyта GenericRelationсфери.

Це занадто добре, щоб бути правдою?

Як і в житті, є плюси і мінуси. Щоразу, коли ви додаєте більше коду та більше абстракцій, основні процеси стають більш важкими та трохи повільнішими. Додавання загальних зв'язків може трохи зменшити ефективність роботи, незважаючи на те, що вона постарається і розумно кешувати свої результати. Загалом, справа зводиться до того, чи чистота та простота переважають малі витрати на продуктивність. Для мене відповідь - це мільйон разів так.

У структурі типів вмісту є більше, ніж я показав тут. Існує цілий рівень деталізації та більш багатослівного використання, але для середньостатистичного індивіда саме так ви будете використовувати його 9 із 10 разів.

Універсальні реляціонізатори (?) Остерігайтесь!

Досить великий застереження полягає в тому, що коли ви використовуєте a GenericRelation, якщо модель, яка має GenericRelationзастосований ( Picture), буде видалена, всі пов'язані ( Comment) об'єкти також будуть видалені. Або принаймні станом на час написання цього тексту.


11
Отже, якщо я використовую GenericRelationв Postі Pictureтоді мені не потрібно використовувати object_id, content_typeі content_objectв Comment?
avi

5
Було б непогано мати такий чистий опис фрейму типу контенту десь в офіційній документації Django. Щодо мене, я зрозумів, що робить ця рамка лише після прочитання цього порту. Дякую.
прохер

2
трохи пізно ... але я чув, що використовуючи рамки типу вмісту, ваша програма може не масштабуватися належним чином. хтось, будь ласка, скаже мені, чи це правда чи підступ?
Каран Кумар

1
Як і в усьому програмуванні, Каран, відповідь завжди "це залежить". Я б сказав, використовуйте типи вмісту. Це "компроміс" різного роду, щоб обійти деякі жорсткі основи системи, орієнтованої на таблицю SQL. Не передчасно оптимізуйте свій додаток! Django найкраще вибиратися з вашого шляху, щоб ви могли написати заявку наступного покоління, яку ви завжди хотіли: використовуйте її на вашу користь!
Кріс Шелтон

2
Каран, в цьому є правда. Я працюю над додатком, який відстежує сповіщення користувачів. Кожне повідомлення має відношення GenericForeignKey до деякого іншого типу вмісту, який ми зберігаємо. Кожен раз, коли користувач переглядає сповіщення, ORM видає N запитів, щоб отримати весь відповідний вміст. Навряд чи ідеально.
Тревіс Мелінгер

-2

Добре, пряма відповідь на ваше запитання: (з вихідного коду django): Аналіз типів медіа відповідно до RFC 2616, розділ 3.7.

Який спосіб сліз говорить про те, що він читає / дозволяє-змінювати / проходить вздовж заголовка 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 - це пітон, і як такий він володіє силою спільноти пітонів. Є два дивовижні плагіни RESTFul до django. Тож якщо ви хочете побачити, наскільки глибоко йде ціле кролик, ви можете перевірити.

Я пропоную ознайомитися з навчальним посібником для django-rest, в якому буде конкретно розглянуто "дію на різний зміст / типи". Примітка. Загальна практика використовувати заголовок типу вмісту для спокійних API 'версії' .


1
Це те, на що він має на увазі? або до системи contenttypes
petkostas

1
Так, я мав на увазі рамки типів вмісту. Я, можливо, не зробив достатньо хорошої роботи, передаючи себе. Я ціную відповідь незалежно. Чого варто, якби це було моїм питанням, ви б вибили його з парку =)
Кріс Шелтон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.