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

91

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

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

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

2 ответа

214
Лучший ответ

Итак, вы хотите использовать структуру Content Types для своей работы?

Начните с того, что задайте себе этот вопрос: "Следует ли каким-либо из этих моделей одинаково относиться к другим моделям и/или буду ли я повторно использовать эти отношения непредвиденными способами позже по дороге?" Причина, по которой мы задаем этот вопрос, состоит в том, что именно это делает структура Content Types: создает общие отношения между моделями. Бла-бла, пусть погрузится в какой-то код и посмотрит, что я имею в виду.

# 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, ваш превосходный интеллект говорит вам, что это отстой, и вы можете сделать лучше. Высокие пять!

Введите структуру Content Types!

Итак, теперь мы внимательно рассмотрим наши модели и переработаем их, чтобы быть более "многоразовыми" и интуитивно понятными. Начните с избавления от двух внешних ключей нашей модели 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()

Итак, что случилось? Ну, мы вошли и добавили необходимый код, чтобы обеспечить общее отношение к другим моделям. Обратите внимание, что для object_id больше, чем просто GenericForeignKey, но также от ForeignKey до ContentType и a PositiveIntegerField. Эти поля предназначены для того, чтобы сообщать Django, к какому типу объекта это относится, и к какому идентификатору относится этот объект. На самом деле это имеет смысл, потому что 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')

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

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

# 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 start making a comment for our own picture
>>> pic.comments.create(author=me, body="Man, I'm cool!")

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

Это просто.

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

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

Общие отношения (?) остерегайтесь!

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

  • 8
    Так что, если я использую GenericRelation в Post и Picture тогда мне не нужно использовать object_id , content_type и content_object в Comment ?
  • 2
    Было бы неплохо иметь такое чистое описание инфраструктуры типов контента где-нибудь в официальной документации Django. Что касается меня, я понял, что делает этот фреймворк только после прочтения этого порта. Спасибо.
Показать ещё 6 комментариев
0

Хорошо, что прямой ответ на ваш вопрос: (из исходного кода django): Разбор носителей в соответствии с RFC 2616, раздел 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, в котором будет рассмотрено "действующее действие на разные контенты/типы". Примечание. Обычно используется заголовок типа контента для 'версии', поддерживающего непринужденный API.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню