Отже, ви хочете використовувати рамки типів вмісту у своїй роботі?
Спочатку задайте собі це питання: "Чи потрібно будь-яку з цих моделей таким же чином пов’язати з іншими моделями та / або я буду повторно використовувати ці відносини небаченими способами пізніше вниз?" Причина, чому ми задаємо це запитання, полягає в тому, що саме це відповідає типі контенту: це створює загальні відносини між моделями. Бла-бла, давайте зануримось у якийсь код і подивимося, що я маю на увазі.
# 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
) об'єкти також будуть видалені. Або принаймні станом на час написання цього тексту.