Создание моделей данных блога

⏱ 25 мин · 🧩 заданий: 6

Модель Post

Сердце блога — пост. Опишем его в blog/models.py:

from django.conf import settings
from django.db import models
from django.utils import timezone


class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = "DF", "Черновик"
        PUBLISHED = "PB", "Опубликован"

    title = models.CharField("Заголовок", max_length=250)
    slug = models.SlugField("Слаг", max_length=250)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="blog_posts",
        verbose_name="Автор",
    )
    body = models.TextField("Текст")
    publish = models.DateTimeField("Дата публикации", default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(
        "Статус", max_length=2, choices=Status.choices, default=Status.DRAFT
    )

    class Meta:
        ordering = ["-publish"]
        indexes = [
            models.Index(fields=["-publish"]),
        ]
        verbose_name = "пост"
        verbose_name_plural = "посты"

    def __str__(self):
        return self.title

Разберём каждое решение.

Заголовок и слаг

  • title — CharField, в базе VARCHAR(250).
  • slug — короткая метка для URL из латиницы, цифр, дефисов: kak-ustroen-django. Делает адреса читаемыми и полезными для SEO: /blog/2026/10/25/kak-ustroen-django/ вместо /blog/17/.

Автор

Связь «многие к одному» с пользователем. Ссылаемся через settings.AUTH_USER_MODEL (по умолчанию "auth.User"), а не импортируем User напрямую: если проект перейдёт на собственную модель пользователя, код блога менять не придётся.

related_name="blog_posts" позволит писать user.blog_posts.all().

Даты

Поле Значение
publish Когда пост опубликован. default=timezone.now — по умолчанию «сейчас», но можно изменить
created Когда создан. auto_now_add — заполняется один раз
updated Когда изменён. auto_now — обновляется при каждом save()

Используйте django.utils.timezone.now, а не datetime.now: при USE_TZ = True Django ожидает даты с часовым поясом.

Статус

TextChoices — перечисление вариантов. В базе хранится "DF" или "PB", людям показывается «Черновик» / «Опубликован».

post.status == Post.Status.PUBLISHED   # сравнение
post.get_status_display()              # "Опубликован"
Post.Status.choices                    # [("DF", "Черновик"), ("PB", "Опубликован")]
Post.Status.labels                     # ["Черновик", "Опубликован"]

Meta: сортировка и индекс

  • ordering = ["-publish"] — по умолчанию свежие посты первыми.
  • Индекс по -publish ускоряет именно такую сортировку и фильтрацию по дате. Большинство запросов блога будут использовать это поле.

Миграции

python manage.py makemigrations blog
Migrations for 'blog':
  blog/migrations/0001_initial.py
    + Create model Post
    + Create index blog_post_publish_bb7600_idx on field(s) -publish of model post

Посмотрим SQL:

python manage.py sqlmigrate blog 0001
CREATE TABLE "blog_post" (
  "id" bigint NOT NULL PRIMARY KEY AUTOINCREMENT,
  "title" varchar(250) NOT NULL,
  "slug" varchar(250) NOT NULL,
  "body" text NOT NULL,
  "publish" datetime NOT NULL,
  ...
  "author_id" integer NOT NULL REFERENCES "auth_user" ("id")
);
CREATE INDEX "blog_post_slug_..." ON "blog_post" ("slug");
CREATE INDEX "blog_post_publish_..." ON "blog_post" ("publish" DESC);

Обратите внимание: SlugField по умолчанию создаёт индекс, а ForeignKey — столбец author_id с индексом.

python manage.py migrate

Пробуем в shell

>>> from django.contrib.auth.models import User
>>> from blog.models import Post
>>> user = User.objects.create_superuser("admin", password="admin")   # или через createsuperuser
>>> post = Post.objects.create(title="Первый пост", slug="pervyj-post", body="Привет!", author=user)
>>> post.status
'DF'
>>> post.get_status_display()
'Черновик'

Итог

  • Post: заголовок, слаг, автор (через AUTH_USER_MODEL), текст, три даты, статус.
  • TextChoices описывает варианты статуса.
  • Meta.ordering и Meta.indexes — сортировка и индекс по дате публикации.
  • makemigrations → sqlmigrate (посмотреть) → migrate.

Практика решено 0 из 6

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Добавьте в модель Post поле author — связь с моделью пользователя через settings.AUTH_USER_MODEL, удаление каскадом, обратная связь — blog_posts.

Задание 2 Один вариант ответа

Почему для даты публикации используют default=timezone.now, а не auto_now_add=True?

Задание 3 Написать код

Опишите вложенный класс Status (models.TextChoices) с вариантами DRAFT = "DF", "Черновик" и PUBLISHED = "PB", "Опубликован", и поле status (CharField, длина 2, варианты из Status, по умолчанию черновик).

Задание 4 Написать код

Добавьте в Meta модели Post сортировку по убыванию publish и индекс по этому же полю (по убыванию).

Задание 5 Один вариант ответа

Зачем нужен индекс по полю publish?

Задание 6 Ввод ответа

Как называется метод, который для поля с choices возвращает человекочитаемую подпись? Для поля status — post.???()

Войдите, чтобы отмечать пройденные уроки.