Как именно работают типы контента Django?

148

Мне действительно трудно понять концепцию типов контента Django. Это кажется очень хакерским и, в конечном счете, против того, как Python стремится что-то делать. При этом, если я собираюсь использовать Django, то я должен работать в рамках фреймворка.

Поэтому я прихожу сюда с вопросом, может ли кто-нибудь привести практический пример того, как работает тип контента и как вы его реализуете. Почти все учебники (в основном в блогах), которые я рассмотрел, не очень хорошо освещают эту концепцию. Похоже, они начинают с того места, где закончилась документация по Django (что похоже никуда).

Крис Шелтон
источник
5
Я считаю (кто-то поправит меня, если я ошибаюсь), что типы контента - это что-то вроде полиморфизма, он станет инструментом в ваших руках, как только ваш проект начнет иметь модели, которые могут иметь много разных форм. Пример тега в документации довольно прост, вы хотите иметь возможность пометить элементы, но не хотите конкретно указывать, какие они элементы, в конце концов, тег может поддерживать сообщения, страницы, пользователи, товары. Используя типы контента, вы можете создавать отношения к различным реализациям, не зная точно, что представляет собой связанная модель.
Петкостас
1
Хорошо, так что я запутался в том, что они создали класс с именем TaggedItem, который мне не был понятен. Я не был уверен, был ли TaggedItem заполнителем класса "bridge". Моим естественным уклоном было бы что-то вроде «Tag» со свойством «term».
Крис Шелтон

Ответы:

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. Эти поля предназначены для сообщения Django, к какому типу объекта это относится и каков id этого объекта. В действительности это имеет смысл, потому что Django понадобится оба для поиска этих связанных объектов.

Ну, это не очень похоже на 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!"]

Это так просто.

Каковы другие практические последствия этих «общих» отношений?

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

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

Это слишком хорошо, чтобы быть правдой?

Как и во всем в жизни, есть плюсы и минусы. Каждый раз, когда вы добавляете больше кода и больше абстракции, базовые процессы становятся тяжелее и немного медленнее. Добавление общих отношений может немного снизить производительность, несмотря на то, что она попытается кэшировать свои результаты. В общем, все сводится к тому, перевешивает ли чистота и простота небольшие эксплуатационные расходы. Для меня ответ - миллион раз да.

В структуре типов контента есть нечто большее, чем я показал здесь. Существует целый уровень детализации и более подробное использование, но для среднего человека, по моему мнению, вы будете использовать его 9 из 10 раз.

Общие реляционеры (?) Будьте осторожны!

Довольно большое предостережение заключается в том, что при использовании a GenericRelation, если удаляется модель, имеющая GenericRelationapply ( Picture), все связанные Commentобъекты ( ) также будут удалены. Или, по крайней мере, на момент написания этой статьи.

Крис Шелтон
источник
11
Так что, если я использую GenericRelationв Postи Pictureя не нужно использовать object_id, content_typeи content_objectв Comment?
Ави
5
Было бы неплохо иметь такое четкое описание инфраструктуры типов контента где-нибудь в официальной документации Django. Что касается меня, я понял, что делает этот фреймворк только после прочтения этого порта. Спасибо.
Прохер
2
немного поздно ... но я слышал, что при использовании структуры типов контента ваше приложение может не масштабироваться должным образом. Может кто-нибудь сказать мне, если это правда или обман?
Каран Кумар
1
Как и во всем в программировании, Каран, ответ всегда «это зависит». Я бы сказал, использовать типы контента. Это своего рода «компромисс», позволяющий обойти некоторые из жестких основ таблично ориентированной системы SQL. Не преждевременно оптимизируйте свое приложение! Django отлично справляется со своей задачей, поэтому вы можете написать приложение следующего поколения, которое вы всегда хотели: используйте его возможности в своих интересах!
Крис Шелтон
2
Каран, в этом есть доля правды. Я работаю над приложением, которое отслеживает уведомления для пользователей. Каждое уведомление имеет отношение GenericForeignKey к некоторому другому типу контента, который мы храним. Каждый раз, когда пользователь просматривает уведомления, ORM выдает N запросов, чтобы получить весь связанный контент. Вряд ли идеал.
Трэвис Мелингер,
-2

Хорошо, прямой ответ на ваш вопрос: (из исходного кода django): Разбор медиа-типов в соответствии с RFC 2616, раздел 3.7.

Какой способ сказать, что он читает / позволяет вам изменять / передает заголовок httpd 'Content-type' .

Тем не менее, вы просите более практический пример использования. У меня есть 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. Есть два замечательных плагина RESTFul для django. Так что, если вы хотите увидеть, насколько глубоко заходит кролик, вы можете проверить.

Я предлагаю пройти курс обучения django-rest-framework, который будет посвящен «действию в отношении разных типов контента / типов». Примечание. Распространенной практикой является использование заголовка типа содержимого для API-интерфейсов restful «version» .

Джефф Шеффилд
источник
1
Это то, что он имеет в виду? или в структуру типов контента ?: docs.djangoproject.com/en/dev/ref/contrib/contenttypes
petkostas
1
Да, я имел в виду структуру типов контента. Я, возможно, не сделал достаточно хорошую работу, передавая себя. Я ценю ответ независимо. Для чего это стоит, если бы это был мой вопрос, вы бы выбили его из парка =)
Крис Шелтон