Начало работы, создание модели статей

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

Новый проект

В последних двух модулях мы построим второй большой проект — сайт статей с древовидными категориями, профилями, добавлением статей пользователями, древовидными комментариями на AJAX, тегами, лайками, RSS и кешированием. Многое будет знакомо по блогу, но мы пойдём дальше и сделаем акцент на представлениях-классах и миксинах.

Создание

mkdir articles && cd articles
python -m venv venv
source venv/bin/activate
pip install django pillow
django-admin startproject backend .
mkdir modules
python manage.py startapp blog modules/blog

Приложения будем хранить в папке modules/ — так проще ориентироваться, когда их много. В modules/blog/apps.py поправьте имя:

class BlogConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "modules.blog"
    verbose_name = "Статьи"

Создайте пустой modules/__init__.py.

Настройки

backend/settings.py:

INSTALLED_APPS = [
    ...
    "modules.blog.apps.BlogConfig",
]

TEMPLATES[0]["DIRS"] = [BASE_DIR / "templates"]

LANGUAGE_CODE = "ru"
TIME_ZONE = "Europe/Moscow"

STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "templates" / "src"]
STATIC_ROOT = BASE_DIR / "static"

MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"

backend/urls.py:

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("modules.blog.urls")),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Модель статьи

modules/blog/models.py:

from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse

User = get_user_model()


class Article(models.Model):
    class ArticleManager(models.Manager):
        def all(self):
            return self.get_queryset().filter(status="published")

    STATUS_OPTIONS = (
        ("published", "Опубликовано"),
        ("draft", "Черновик"),
    )

    title = models.CharField(verbose_name="Заголовок", max_length=255)
    slug = models.SlugField(verbose_name="URL", max_length=255, blank=True)
    short_description = models.TextField(verbose_name="Краткое описание", max_length=500)
    full_description = models.TextField(verbose_name="Полное описание")
    thumbnail = models.ImageField(
        verbose_name="Превью поста",
        blank=True,
        upload_to="images/thumbnails/%Y/%m/%d/",
    )
    status = models.CharField(choices=STATUS_OPTIONS, default="published", verbose_name="Статус", max_length=10)
    time_create = models.DateTimeField(auto_now_add=True, verbose_name="Время добавления")
    time_update = models.DateTimeField(auto_now=True, verbose_name="Время обновления")
    author = models.ForeignKey(
        to=User,
        verbose_name="Автор",
        on_delete=models.SET_DEFAULT,
        related_name="author_posts",
        default=1,
    )
    updater = models.ForeignKey(
        to=User,
        verbose_name="Обновил",
        on_delete=models.SET_NULL,
        null=True,
        related_name="updater_posts",
        blank=True,
    )
    fixed = models.BooleanField(verbose_name="Закреплено", default=False)

    objects = models.Manager()
    custom = ArticleManager()

    class Meta:
        db_table = "app_articles"
        ordering = ["-fixed", "-time_create"]
        indexes = [models.Index(fields=["-fixed", "-time_create", "status"])]
        verbose_name = "Статья"
        verbose_name_plural = "Статьи"

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse("articles_detail", kwargs={"slug": self.slug})

Что здесь нового по сравнению с блогом:

  • get_user_model() — получаем модель пользователя, какой бы она ни была (стандартной или своей).
  • on_delete=models.SET_DEFAULT, default=1 — при удалении автора статьи «переходят» администратору с id=1, а не удаляются.
  • updater — кто последним редактировал статью (SET_NULL, поле может быть пустым).
  • fixed — закреплённые статьи показываются первыми (сортировка -fixed).
  • Менеджер объявлен прямо внутри модели; метод all() переопределён, чтобы Article.custom.all() возвращал только опубликованные.
  • db_table = "app_articles" — своё имя таблицы.
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

Админка

from django.contrib import admin

from .models import Article


@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ["title", "author", "status", "fixed", "time_create"]
    list_filter = ["status", "fixed"]
    search_fields = ["title", "short_description"]
    prepopulated_fields = {"slug": ("title",)}

Итог

  • Новый проект backend с приложениями в папке modules/.
  • Article: заголовок, слаг, описания, превью, статус, даты, автор и редактор, закрепление.
  • get_user_model() для ссылки на пользователя, SET_DEFAULT для сохранения статей при удалении автора.
  • Собственный менеджер для опубликованных статей.

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

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

Расставьте шаги старта нового проекта «Сайт статей».

  1. Описать модель Article и выполнить миграции
  2. python manage.py startapp blog (в папке modules)
  3. django-admin startproject backend .
  4. Подключить приложение и настроить язык, часовой пояс, статику и медиа
  5. Создать venv и установить Django
  6. Зарегистрировать модель в админке
Задание 2 Написать код

Опишите модель Article с полями: title (CharField 255), slug (SlugField 255, blank=True), short_description (TextField 500), full_description (TextField), thumbnail (ImageField, upload_to="images/thumbnails/", blank=True), status (CharField 10 с вариантами STATUS_OPTIONS, по умолчанию "published").

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

Добавьте в модель Article поля дат: time_create (заполняется при создании) и time_update (при каждом сохранении), а также автора: author — ForeignKey на User с on_delete=models.SET_DEFAULT, default=1 и related_name="author_posts".

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

Что произойдёт со статьями при удалении автора, если on_delete=models.SET_DEFAULT, default=1?

Задание 5 Несколько вариантов

Какие настройки нужно добавить для работы с загружаемыми картинками (превью статей)?

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