Создайте блог с нуля с помощью Django

Оглавление

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

В этом руководстве вы узнаете, как:

  • Создать новый проект Django
  • Создание и редактирование записей в блоге
  • Показывать сообщения пользователю
  • Присвойте сообщениям категории
  • Разрешить пользователям комментировать сообщения

Настройка среды разработки

Всякий раз, когда вы начинаете новый проект веб-разработки, рекомендуется сначала настроить среду разработки. Создайте новый каталог, в котором будет находиться ваш проект, и cd в него:

$ mkdir django-blog
$ cd django-blog

Как только вы окажетесь внутри каталога django-blog/, рекомендуется создать виртуальную среду для управления зависимостями. Выберите свою операционную систему ниже и используйте команду, зависящую от вашей платформы, для настройки виртуальной среды:

PS> python -m venv venv
$ python -m venv venv

Эта команда создаст папку venv/ в вашем рабочем каталоге. Внутри этого каталога вы найдете несколько файлов, в том числе копию стандартной библиотеки Python. Позже, когда вы установите новые зависимости, они тоже будут храниться в этом каталоге. Далее вам необходимо активировать виртуальную среду, выполнив следующую команду:

PS> .\venv\Scripts\activate
(venv) PS>
$ source venv/bin/activate
(venv) $

С помощью приведенных выше команд вы создаете и активируете виртуальную среду с именем venv, используя встроенный в Python модуль venv. Надпись в скобках (venv) перед приглашением означает, что вы успешно активировали виртуальную среду.

Теперь, когда вы создали и активировали виртуальную среду, пришло время установить Django. Вы можете сделать это с помощью pip:

(venv) $ python -m pip install Django

После того как вы настроили виртуальную среду и установили Django, теперь вы можете приступить к созданию своего проекта на Django.

Запустите свой проект на Django

Веб-приложение Django состоит из проекта и приложений. Проект Django содержит некоторые конфигурации, которые применяются к веб-приложению в целом, такие как настройки проекта, URL-адреса, общие шаблоны и статические файлы. Каждое приложение Django может иметь свои собственные URL-адреса, а также свои собственные HTML-шаблоны и статические файлы, такие как JavaScript и CSS.

Чтобы создать свой проект на Django, убедитесь, что вы находитесь в каталоге django-blog/ с активированной виртуальной средой. Затем выполните следующую команду для создания проекта personal_blog:

(venv) $ django-admin startproject personal_blog .

Не забудьте добавить точку (.) в конце приведенной выше команды. Точка не позволяет Django создавать вложенный каталог проектов для вашего портфолио-проекта. В противном случае у вас получится папка personal_blog/, содержащая подкаталог personal_blog/.

Выполнив команду startproject, как показано выше, вы указали Django создать одну папку personal_blog/ в каталоге django-blog/. Ваша структура каталогов должна выглядеть примерно так:

django-blog/
│
├── personal_blog/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
│
├── venv/
│
└── manage.py

Как только ваша файловая структура настроена, вы можете запустить сервер разработки Django и убедиться, что настройка прошла успешно. В консоли выполните следующую команду:

(venv) $ python manage.py runserver

Затем в вашем браузере перейдите на страницу http://localhost:8000, и вы должны увидеть следующее:

Django Start Page

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

Добавьте приложение для блога, которое вы создадите с нуля

Прежде чем приступить к созданию функциональности этой части вашего сайта, создайте новое приложение Django с именем blog:

(venv) $ python manage.py startapp blog

После того, как вы создали приложение, вам необходимо установить его в свой проект. В django-blog/personal_blog/settings.py добавьте следующую строку кода под INSTALLED_APPS:

# personal_blog/settings.py

# ...

INSTALLED_APPS = [
    "blog.apps.BlogConfig",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

# ...

Чтобы включить приложение в проект Django, вам необходимо добавить ссылку на его класс конфигурации в начале INSTALLED_APPS списка в settings.py.

Добавляя blog.apps.BlogConfig, вы даете blog понять Django, что приложение, которое вы только что создали, существует. Вы на один большой шаг приблизились к успешному созданию блога с нуля! Следующим шагом будет создание моделей, определяющих логику хранения контента вашего блога в базе данных.

Определение моделей для представления таблиц базы данных

Если вы хотите хранить данные для отображения на веб-сайте, вам понадобится база данных. Как правило, если вы хотите создать базу данных с таблицами и столбцами внутри этих таблиц, вам нужно будет использовать SQL для управления базой данных. Но когда вы используете Django, вам не нужно изучать новый язык, потому что в него встроен объектно-реляционный картограф (ORM).

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

Когда вы используете ORM, модели - это классы, которые вы создаете и которые представляют таблицы базы данных. В Django они находятся в модуле models.py каждого приложения Django.

Вам понадобятся три отдельные модели для блога:

  1. Post
  2. Category
  3. Comment

Начните с кода для моделей Category и Post в файле models.py вашего приложения blog:

 1# blog/models.py
 2
 3from django.db import models
 4
 5class Category(models.Model):
 6    name = models.CharField(max_length=30)
 7
 8class Post(models.Model):
 9    title = models.CharField(max_length=255)
10    body = models.TextField()
11    created_on = models.DateTimeField(auto_now_add=True)
12    last_modified = models.DateTimeField(auto_now=True)
13    categories = models.ManyToManyField("Category", related_name="posts")

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

Поля title и body в модели Post также содержат текст. Вам нужно CharField для title, чтобы сохранить короткую строку для заголовка записи. Основная часть сообщения должна представлять собой длинный фрагмент текста, поэтому вы используете TextField.

Следующие два поля, created_on и last_modified, являются Django DateTimeFields. В них хранится datetime объект, содержащий дату и время создания и изменения записи соответственно.

Для created_on параметр DateTimeField принимает аргумент auto_now_add=True. При этом текущая дата и время присваиваются этому полю всякий раз, когда вы создаете экземпляр этого класса.

Для last_modified параметр DateTimeField принимает аргумент auto_now=True. При этом текущая дата и время присваиваются этому полю всякий раз, когда сохраняется экземпляр этого класса. Это означает, что всякий раз, когда вы редактируете экземпляр этого класса, date_modified обновляется.

Последнее поле в модели Post создает связь между публикацией и категориями. Здесь вы связываете свои модели для категорий и постов таким образом, чтобы вы могли назначить многим категориям много постов. Django предоставляет ManytoManyField тип поля для такого рода отношений.

Параметр ManyToManyField принимает два аргумента. Первый — это модель, к которой он применяется, в данном случае Category. Второй позволяет вам получить доступ к связи из объекта Category, даже если вы не добавили туда поле. Добавив related_name из posts, вы можете перейти к category.posts, чтобы получить список записей с этой категорией. Далее в руководстве вы увидите, как это работает.

Третья и последняя модель, которую вам нужно добавить, называется Comment:

 1# blog/models.py
 2
 3# ...
 4
 5class Comment(models.Model):
 6    author = models.CharField(max_length=60)
 7    body = models.TextField()
 8    created_on = models.DateTimeField(auto_now_add=True)
 9    post = models.ForeignKey("Post", on_delete=models.CASCADE)

Первые три поля в этой модели должны выглядеть знакомо. Там есть поле author, в котором пользователи могут добавлять имя или псевдоним, поле body для основного текста комментария и поле created_on, идентичное полю created_on на модель Post.

В строке 9 вы используете другое относительное поле, поле ForeignKey. Это аналогично ManyToManyField, но вместо этого определяет отношение "многие к одному". Причина этого в том, что многим комментариям может быть присвоено одному сообщению. Но у вас не может быть комментария, который соответствовал бы многим сообщениям.

Поле ForeignKey принимает два аргумента. Первый — это другая модель связи, в данном случае Post. Второй указывает Django, что делать, когда запись удалена. Если публикация удалена, вы не хотите, чтобы комментарии, связанные с ней, оставались в сети. Вместо этого вы также удаляете комментарии. Для этого и существует on_delete=models.CASCADE.

Теперь, когда вы создали свой класс Project, вам нужен Django для создания базы данных. По умолчанию ORM Django создает базы данных в SQLite, но вы можете использовать другие базы данных, использующие язык SQL, такие как PostgreSQL или MySQL, с помощью Django ОРМ.

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

Чтобы выполнить перенос, введите в консоли следующую команду, убедившись, что вы находитесь в каталоге django-blog/:

(venv) $ python manage.py makemigrations blog
Migrations for 'blog':
  blog/migrations/0001_initial.py
    - Create model Category
    - Create model Post
    - Create model Comment

Вы должны увидеть, что blog/ теперь содержит migrations/, включая файл с именем 0001_initial.py. Этот файл содержит инструкции, которые Django должен выполнить для базы данных.

Теперь, когда вы создали файл миграции, вам нужно применить процедуры миграции, указанные в этом файле, и создать свою базу данных, используя команду migrate:

(venv) $ python manage.py migrate blog
Operations to perform:
  Apply all migrations: blog
Running migrations:
  Applying blog.0001_initial... OK

Теперь, когда вы создали модели, вы можете начать добавлять некоторые посты и категории в свой блог. Для этого вы узнаете, как использовать сайт администратора Django, который позволит вам создавать экземпляры ваших классов моделей в удобном веб-интерфейсе.

Используйте возможности сайта администратора Django

Сайт администратора Django - это фантастический инструмент и одно из самых больших преимуществ использования Django. Сайт администратора Django позволяет вам, как администратору блога, создавать, обновлять и удалять экземпляры ваших классов моделей, не выходя из удобного веб-интерфейса.

Чтобы получить доступ к сайту администратора Django, вам необходимо зарегистрироваться в качестве суперпользователя. Django поставляется со встроенными пользовательскими моделями и системой управления пользователями, которая позволит вам войти в систему от имени администратора.

Прежде чем вы сможете создать суперпользователя, вам необходимо применить некоторые существующие миграции, которые Django уже подготовил для вас:

(venv) $ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, blog, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

Если посмотреть на список примененных миграций, то можно получить представление о функциях, которыми уже оснащен Django. Например, Django предоставляет авторизацию пользователей и администрирование сайта.

После применения всех изменений вы можете добавить себя в качестве суперпользователя, используя следующую команду:

(venv) $ python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password: RealPyth0n
Password (again): RealPyth0n
Superuser created successfully.

Когда вы запускаете команду управления createsuperuser, Django предлагает вам выбрать имя пользователя, указать адрес электронной почты и установить пароль. Используйте свои собственные данные для этих полей и обязательно запомните их.

Перейдите на страницу http://localhost:8000/admin и войдите в систему, используя учетные данные, которые вы только что использовали для создания статуса суперпользователя. Вы увидите страницу, аналогичную приведенной ниже:

Django default admin site

Должны появиться модели User и Groups, но вы заметите, что в них нет ссылок на модели Category, Post, или Comment, которые вы создали самостоятельно. Это потому, что сначала вам нужно зарегистрировать свои модели на сайте администратора Django.

В каталоге blog откройте файл admin.py и добавьте следующие строки кода:

 1# blog/admin.py
 2
 3from django.contrib import admin
 4from blog.models import Category, Comment, Post
 5
 6class CategoryAdmin(admin.ModelAdmin):
 7    pass
 8
 9class PostAdmin(admin.ModelAdmin):
10    pass
11
12class CommentAdmin(admin.ModelAdmin):
13    pass
14
15admin.site.register(Category, CategoryAdmin)
16admin.site.register(Post, PostAdmin)
17admin.site.register(Comment, CommentAdmin)

В режиме онлайн 4 вы импортируете модели, которые хотите зарегистрировать на странице администратора.

Со строки 6 по строку 13 вы определяете пустые классы CategoryAdmin, PostAdmin, и CommentAdmin. Для целей данного руководства вам не нужно добавлять какие-либо атрибуты или методы к этим классам. Их назначение - настроить отображение на страницах администратора. Для этого руководства достаточно конфигурации по умолчанию.

В последних трех строках вы регистрируете модели в классах администратора. Если вы зайдете на страницу http://localhost:8000/admin, то увидите, что теперь видны модели Post и Category:

Django Admin site with models displayed

Если вы нажмете на Сообщения, то сможете добавлять новые записи для блога, который вы создаете с нуля. Если вы не хотите придумывать заголовки и текст прямо сейчас, вы можете создавать посты-заполнители, используя некоторый lorem ipsum фиктивный текст:

Создайте пару поддельных записей и назначьте им поддельные категории, прежде чем переходить к следующему разделу. Таким образом, у вас будут записи и категории, с которыми вы сможете работать в следующем разделе.

Управление представлением модели

В настоящее время на сайте администратора Django есть два недостатка, которые вы рассмотрите в этом разделе. Оба они связаны с представлением ваших моделей. Перейдите на страницу http://localhost:8000/admin и посмотрите, сможете ли вы их найти. Кроме того, вы можете нажать на кнопку сворачивания ниже:

Первый недостаток - это ссылка на вашу модель Category. На данный момент в тексте написано “Категории” вместо правильного “Categories”:

Django model with wrong plural

Другим недостатком является то, что записи в блоге и категории отображают их объектное представление вместо заголовков:

Django Admin site raw model representation

 

Чтобы улучшить представление ваших моделей, откройте models.py и добавьте выделенный код ниже:

 1# blog/models.py
 2
 3from django.db import models
 4
 5class Category(models.Model):
 6    name = models.CharField(max_length=30)
 7
 8    class Meta:
 9        verbose_name_plural = "categories"
10
11    def __str__(self):
12        return self.name
13
14class Post(models.Model):
15    title = models.CharField(max_length=255)
16    body = models.TextField()
17    created_on = models.DateTimeField(auto_now_add=True)
18    last_modified = models.DateTimeField(auto_now=True)
19    categories = models.ManyToManyField("Category", related_name="posts")
20
21    def __str__(self):
22        return self.title
23
24class Comment(models.Model):
25    author = models.CharField(max_length=60)
26    body = models.TextField()
27    created_on = models.DateTimeField(auto_now_add=True)
28    post = models.ForeignKey("Post", on_delete=models.CASCADE)
29
30    def __str__(self):
31        return f"{self.author} on '{self.post}'"

Как и в случае с обычными классами Python, вы можете добавить метод .__str()__ в классы моделей, чтобы обеспечить лучшее строковое представление ваших объектов. Для категорий вы хотите отобразить имя. Для постов вы хотите заголовок. Для комментариев покажите имя комментатора и пост, который он комментирует.

Чтобы исправить неправильную форму множественного числа в вашем классе Category, вы добавляете Meta class для управления именем класса во множественном числе. По умолчанию Django просто добавляет строчную букву s в конце названия модели. Для множественного числа от post это работает идеально. Для категорий вам необходимо явно указать verbose_name_plural с правильным написанием.

Чтобы убедиться, что внесенные изменения работают, посетите http://localhost:8000/admin еще раз:

Django Admin site with the correct representation of models

Молодец! Вы исправили оба недостатка, и модели представлены в удобочитаемом виде. Далее вы перейдете из области администрирования в ту часть блога, которая предназначена для пользователей.

Обрабатывать логику с помощью Представлений

Представление в Django - это набор функций или классов внутри файла views.py в каталоге приложения. Каждая функция или класс обрабатывает логику, которая обрабатывается каждый раз, когда пользователь переходит по другому URL-адресу.

Вам нужно создать три функции просмотра для вашего блога в файле views.py в каталоге blog/:

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

Начните с добавления необходимого импорта и функции blog_index() в views.py:

 1# blog/views.py
 2
 3from django.shortcuts import render
 4from blog.models import Post, Comment
 5
 6def blog_index(request):
 7    posts = Post.objects.all().order_by("-created_on")
 8    context = {
 9        "posts": posts,
10    }
11    return render(request, "blog/index.html", context)

В строке 4 вы импортируете модели Post и Comment. В строке 7, внутри функции просмотра, вы получаете Набор запросов, содержащий все записи в базе данных. Набор запросов - это набор всех объектов в базе данных, которые соответствуют запросу.

При добавлении метода .order_by() в Queryset объекты упорядочиваются в соответствии с заданным аргументом. Знак минус (-) указывает Django на то, что следует начинать с наибольшего значения, а не с наименьшего. Таким образом, вы сначала получите недавно созданные записи.

Наконец, вы определяете context словарь и создаете шаблон с именем index.html. Пока не беспокойтесь о шаблонах Django. Вы познакомитесь с их созданием в следующем разделе.

Далее вы можете создать представление blog_category(). Эта функция просмотра должна будет принять название категории в качестве аргумента и запросить в базе данных Post все записи в данной категории:

 1# blog/views.py
 2
 3# ...
 4
 5def blog_category(request, category):
 6    posts = Post.objects.filter(
 7        categories__name__contains=category
 8    ).order_by("-created_on")
 9    context = {
10        "category": category,
11        "posts": posts,
12    }
13    return render(request, "blog/category.html", context)

В строке 6 вы используете Фильтр набора запросов Django. Аргумент фильтра сообщает Django, какие условия должны быть выполнены для извлечения объекта. В этом случае вам нужны только те публикации, категории которых содержат категорию с названием, соответствующим тому, что указано в аргументе функции просмотра. Опять же, вы используете .order_by() в строке 8, чтобы упорядочить публикации, начиная с самых последних.

Затем вы добавляете эти записи и категорию в словарь context и создаете шаблон category.html.

Последней добавляемой функцией просмотра является blog_detail():

 1# blog/views.py
 2
 3# ...
 4
 5def blog_detail(request, pk):
 6    post = Post.objects.get(pk=pk)
 7    comments = Comment.objects.filter(post=post)
 8    context = {
 9        "post": post,
10        "comments": comments,
11    }
12
13    return render(request, "blog/detail.html", context)

Функция просмотра blog_detail() принимает значение первичного ключа pk в качестве аргумента и в строке 6 извлекает объект с заданным значением pk. Первичный ключ - это уникальный идентификатор записи в базе данных. Это означает, что вы запрашиваете одну запись с определенным первичным ключом, который вы предоставляете.

В строке 7 вы извлекаете все комментарии, присвоенные данному сообщению, снова используя фильтры Django. Если вы не создавали никаких комментариев на сайте администратора Django, то набор запросов пуст. Пока все в порядке.

Наконец, вы добавляете оба post и comments в словарь context и создаете шаблон detail.html. Как и другие шаблоны, на которые вы ссылаетесь в своих представлениях, этот шаблон еще не существует. В следующем разделе вы создадите недостающие шаблоны.

Создайте шаблоны

Шаблоны - это HTML-файлы с возможностью рендеринга динамического контента, передаваемого из ваших представлений Django. Существуют популярные шаблонизаторы, такие как Jinja. Но если вы не планируете делать что-то необычное в своих шаблонах, то можете использовать встроенный в Django язык шаблонов .

Функция render() ваших представлений выполняет поиск HTML-шаблонов в каталоге с именем templates/ внутри каталога вашего приложения. Поскольку шаблоны разных приложений могут иметь одинаковые имена, рекомендуется также добавить подкаталог с названием приложения в каталог templates/.

Создайте каталог template/, а также подкаталог с именем blog/, а затем файлы шаблонов внутри него:

(venv) $ mkdir -p blog/templates/blog
(venv) $ touch blog/templates/blog/index.html
(venv) $ touch blog/templates/blog/category.html
(venv) $ touch blog/templates/blog/detail.html

Первый шаблон, с которым вы будете работать, - это index.html. Вы будете использовать цикл for для перебора всех записей. Для каждого сообщения вы будете отображать заголовок и фрагмент текста:

 1<!-- blog/templates/blog/index.html -->
 2
 3{% block page_title %}
 4    <h2>Blog Posts</h2>
 5{% endblock page_title %}
 6
 7{% block page_content %}
 8    {% block posts %}
 9        {% for post in posts %}
10            <h3><a href="{% url 'blog_detail' post.pk %}">{{ post.title }}</a></h3>
11            <small>
12                {{ post.created_on.date }} | Categories:
13                {% for category in post.categories.all %}
14                    <a href="{% url 'blog_category' category.name %}">
15                        {{ category.name }}
16                    </a>
17                {% endfor %}
18            </small>
19            <p>{{ post.body | slice:":400" }}...</p>
20        {% endfor %}
21    {% endblock posts %}
22{% endblock page_content %}

Вы можете заметить, что шаблон не отображался как допустимая HTML-страница. Для этого отсутствуют необходимые HTML-элементы, такие как <html> и <head>. Вы позаботитесь об этом позже. А пока сосредоточьтесь на том, как вы работаете со словарем context в шаблоне Django.

В строке 9 вы просматриваете значения posts. Это означает, что вы можете напрямую обращаться к ключам context в шаблоне Django, поскольку этот словарь уже распакован для вас.

Внутри цикла for затем вы можете получить доступ к атрибутам post, таким как .title в строке 10. Вы помещаете заголовок сообщения в гиперссылку, которая указывает на URL-адрес с именем blog_detail, который принимает целое число в качестве аргумента. Это целое число является уникальным значением первичного ключа pk записи.

Под заголовком отображается .created_on атрибут публикации, а также ее категории. В строке 13 вы используете другой цикл for для перебора всех категорий, присвоенных сообщению.

В строке 19 вы используете фильтр шаблона slice чтобы сократить текст сообщения до четырехсот символов , чтобы сделать индекс блога более читабельным. Чтобы узнать больше о фильтрах шаблонов, ознакомьтесь с руководством по встроенным тегам и фильтрам.

Еще одним интересным элементом в шаблоне index.html является использование тегов шаблона {% block %} . С помощью этого тега шаблона вы можете определить блоки содержимого, которые можно использовать или переопределять в дочерних шаблонах, расширяющих родительский шаблон.

Дочерним шаблоном для index.html является category.html. Этот шаблон должен выглядеть почти так же. Все записи, которые шаблон получает из представления, должны быть перечислены. Это означает, что единственное отличие в category.html - это заголовок:

<!-- blog/templates/blog/category.html -->

{% extends "blog/index.html" %}

{% block page_title %}
<h2>{{ category }}</h2>
{% endblock page_title %}

Чтобы расширить родительский шаблон, вы должны использовать тег {% extends %} в начале дочернего шаблона. Затем внутри тега {% extends %} вы определяете шаблон, который хотите расширить.

После того, как вы указали ссылку на родительский шаблон, вы можете решить, какие блоки вы хотите унаследовать или переопределить. Если вы не ссылаетесь на блок в дочернем шаблоне, вы наследуете родительский блок таким, какой он есть. В качестве альтернативы вы можете ссылаться на родительский блок с новым содержимым.

Здесь вы переопределяете только блок title, но не блок posts из index.html. Поскольку вы не ссылаетесь на блок posts родительского шаблона, вы наследуете содержимое из index.html для этого блока. Это именно то, что вы хотите, чтобы отображались все записи, представленные в представлении blog_category().

Последним шаблоном является шаблон detail.html. В этом шаблоне вы будете отображать заголовок и весь текст сообщения.

Между заголовком и текстом сообщения вы увидите дату создания сообщения и все категории:

 1<!--  blog/templates/blog/detail.html -->
 2
 3{% block page_title %}
 4    <h2>{{ post.title }}</h2>
 5{% endblock page_title %}
 6
 7{% block page_content %}
 8    <small>
 9        {{ post.created_on.date }} | Categories:
10        {% for category in post.categories.all %}
11            <a href="{% url 'blog_category' category.name %}">
12                {{ category.name }}
13            </a>
14        {% endfor %}
15    </small>
16    <p>{{ post.body | linebreaks }}</p>
17{% endblock page_content %}

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

Примечание: Вы еще не работаете с комментариями в этом шаблоне. Вы представите их позже в шаблоне. Но сейчас полезно знать, что вам не нужно использовать элементы в шаблоне только потому, что вы добавили их в словарь context.

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

Включить маршруты для URL-адресов

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

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

Для этого вам нужно создать файл urls.py внутри blog/ и добавить URL-адреса для трех представлений:

# blog/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path("", views.blog_index, name="blog_index"),
    path("post/<int:pk>/", views.blog_detail, name="blog_detail"),
    path("category/<category>/", views.blog_category, name="blog_category"),
]

Как только URL-адреса для блога будут готовы, вам нужно добавить их в конфигурацию URL-адресов проекта в personal_blog/urls.py с помощью include():

# personal_blog/urls.py

from django.contrib import admin
from django.urls import path, include

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

При такой настройке у вас будет следующая логика маршрута:

Route pattern Example URL Description
"" http://localhost:8000/ Blog index
"post/<int:pk>/" http://localhost:8000/post/1 Blog detail view of a post with pk=1
"category/<category>/" http://localhost:8000/category/python Blog index view of all posts with category python

Перейдите на страницу http://localhost:8000/. Затем щелкните по ссылке и обратите внимание на различные URL-адреса в адресной строке и загружаемые шаблоны:

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

Сделайте так, Чтобы Ваш Блог выглядел красиво

Прежде чем приступить к стилизации вашего проекта, вы создадите базовый шаблон, который вы будете расширять в своих дочерних шаблонах, созданных ранее. Таким образом, вы можете структурировать свои HTML-шаблоны в одном месте и позволить другим шаблонам наследовать содержимое.

Начните с создания каталога с именем templates/ в папке django-blog/ и файла с именем base.html внутри нового каталога:

(venv) $ mkdir templates/
(venv) $ touch templates/base.html

Как вы видели ранее, каждый проект Django может состоять из нескольких приложений, которые обрабатывают отдельную логику, и каждое приложение содержит свой собственный каталог templates/ для хранения HTML-шаблонов, связанных с приложением. Для шаблонов, которые используются совместно во всем проекте, рекомендуется создать каталог templates/ в корневом каталоге.

Внутри base.html добавьте следующие строки кода:

 1<!-- templates/base.html -->
 2
 3<!DOCTYPE html>
 4<html lang="en">
 5<head>
 6    <meta charset="utf-8">
 7    <title>My Personal Blog</title>
 8</head>
 9<body>
10<h1>My Personal Blog</h1>
11<a href="{% url "blog_index" %}">Home</a>
12<hr>
13{% block page_title %}{% endblock page_title %}
14{% block page_content %}{% endblock page_content %}
15</body>
16</html>

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

Как вы узнали ранее, вам нужно добавить тег {% extends %} в начало дочернего шаблона. Откройте index.html и добавьте выделенную строку ниже:

<!-- blog/templates/blog/index.html -->

{% extends "base.html" %}

{% block page_title %}
    <h2>Blog Posts</h2>
{% endblock page_title %}

{% block posts %}
    <!-- ... -->
{% endblock posts %}

При добавлении {% extends "base.html" %} к index.html шаблон унаследует структуру base.html.

Перейдите к detail.html и сделайте его дочерним шаблоном base.html:

<!--  blog/templates/blog/detail.html -->

{% extends "base.html" %}

{% block page_title %}
    <h2>{{ post.title }}</h2>
{% endblock page_title %}

{% block page_content %}
    <!-- ... -->
{% endblock page_content %}

С помощью наследования шаблонов вам не нужно повторять разметку в своих шаблонах. Вместо этого вы расширяете свои дочерние шаблоны. Затем Django автоматически объединяет их вместе при отображении в представлении.

Помните, что categories.html уже расширяет index.html. Поскольку шаблоны передаются по наследству, вам не нужно добавлять какие-либо дополнительные теги в этот шаблон.

Прежде чем вы сможете увидеть базовый шаблон в действии, вам нужно сообщить вашему проекту-примеру Django, что base.html существует. Настройки по умолчанию регистрируют templates/ каталога в каждом приложении, но не в самом корневом каталоге. В personal_blog/settings.py обновите TEMPLATES:

# personal_blog/settings.py

# ...

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            BASE_DIR / "templates/",
        ],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ]
        },
    }
]

# ...

Вы уже определили константу BASE_DIR в settings.py, и она указывает на корневой каталог вашего проекта. Затем вы соединяете путь с помощью оператора прямой косой черты (/) от pathlib, чтобы указать на templates/ каталог и добавьте его в список "DIRS".

Перейдите в свой браузер и посетите http://localhost:8000/:

Django blog with no styling

Когда вы посещаете свой блог, на каждой странице отображается основной заголовок. Это означает, что наследование работает. Но без какого-либо оформления ваш блог по-прежнему выглядит очень просто.

В этом руководстве вы не будете углубляться в CSS-стилистику, а добавите в свой проект внешний CSS-фреймворк. Использование внешних CSS-фреймворков может значительно сэкономить вам время при веб-разработке. Тем не менее, если вы не знакомы с CSS, стоит изучить основы, если вы занимаетесь веб-разработкой.

Снова откройте base.html и добавьте ссылку на фреймворк Simple.css:

 1<!-- templates/base.html -->
 2
 3<!DOCTYPE html>
 4<html lang="en">
 5<head>
 6    <meta charset="utf-8">
 7    <title>My Personal Blog</title>
 8    <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
 9</head>
10<body>
11<!-- ... -->
12</body>
13</html>

Аналогично методу импорта Python, вы можете импортировать внешнюю библиотеку CSS на свой веб-сайт. В строке 8 вы загружаете внешний файл CSS. Этот внешний CSS-файл обеспечивает стилизацию без необходимости добавления каких-либо классов к вашим HTML-элементам.

Откройте http://localhost:8000/ и перейдите на страницу категорий, чтобы убедиться, что ваши дочерние шаблоны наследуют стиль:

Django blog with style inheritance

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

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

Работа с формами для комментариев пользователей

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

Создайте новый файл с именем forms.py в каталоге blog/:

(venv) $ touch blog/forms.py

Внутри forms.py добавьте класс CommentForm с двумя полями author и body:

# blog/forms.py

from django import forms

class CommentForm(forms.Form):
    author = forms.CharField(
        max_length=60,
        widget=forms.TextInput(
            attrs={"class": "form-control", "placeholder": "Your Name"}
        ),
    )
    body = forms.CharField(
        widget=forms.Textarea(
            attrs={"class": "form-control", "placeholder": "Leave a comment!"}
        )
    )

Как для author, так и для body используется класс CharField. Чтобы управлять тем, как элемент формы должен отображаться на странице, вы передаете аргумент widget.

Поле author содержит виджет forms.TextInput. Это указывает Django на необходимость загрузки этого поля в качестве элемента ввода HTML-текста в шаблоны. Вместо поля body используется виджет forms.TextArea, поэтому поле отображается как элемент текстовой области HTML.

Эти виджеты также принимают аргумент attrs, который представляет собой словарь, позволяющий указать некоторые классы CSS. Это поможет с форматированием шаблона для этого представления позже. Это также позволяет вам добавить некоторый текст-заполнитель.

После того, как вы создали форму для комментариев в Django, посмотрите, как форма проходит через запросы:

  1. Когда пользователь посещает страницу, содержащую форму, он отправляет на сервер запрос GET. В этом случае в форму не вводятся данные, поэтому вы просто хотите отрисовать форму и отобразить ее.
  2. Когда пользователь вводит информацию и нажимает кнопку Отправить, он отправляет сообщение POST запрос, содержащий следующее данные, отправленные вместе с формой на сервер. На этом этапе данные отправляются на обработку, и могут произойти две вещи:
    • Форма действительна, и пользователь перенаправляется на следующую страницу.
    • Форма недействительна, и снова отображается пустая форма. Пользователь возвращается к шагу 1, и процесс повторяется.

Функция просмотра, которая должна соответствовать такому поведению, - это функция просмотра blog_detail(). Обновите blog_detail() выделенным кодом ниже:

 1# blog/views.py
 2
 3from django.http import HttpResponseRedirect
 4from django.shortcuts import render
 5from blog.models import Post, Comment
 6from blog.forms import CommentForm
 7
 8# ...
 9
10def blog_detail(request, pk):
11    post = Post.objects.get(pk=pk)
12    form = CommentForm()
13    if request.method == "POST":
14        form = CommentForm(request.POST)
15        if form.is_valid():
16            comment = Comment(
17                author=form.cleaned_data["author"],
18                body=form.cleaned_data["body"],
19                post=post,
20            )
21            comment.save()
22            return HttpResponseRedirect(request.path_info)
23
24    comments = Comment.objects.filter(post=post)
25    context = {
26        "post": post,
27        "comments": comments,
28        "form": CommentForm(),
29    }
30    return render(request, "blog/detail.html", context)

В строке 3 вы импортируете HttpResponseRedirect, что поможет вам перенаправить запрос в строке 22. Чуть позже вы более подробно ознакомитесь со строкой 22. Во-первых, выполните запрос в теле blog_detail().

Независимо от типа вашего запроса, вы берете CommentForm(), который вы импортировали в строке 6, и создаете его экземпляр в строке 12. Таким образом, вы гарантируете, что в вашем представлении всегда будет присутствовать пустая форма.

Затем в строке 13 вы проверяете, получен ли вами запрос POST. Если это так, вы обновляете form данными запроса POST в строке 14. Это данные, которые пользователь ввел в форму.

После этого вы подтверждаете правильность заполнения формы, используя .is_valid() в строке 15, чтобы убедиться, что пользователь правильно ввел все поля.

Примечание: Если форма неверна, вы можете вывести ошибки пользователю. Это выходит за рамки данного руководства, но вы можете прочитать больше о сообщениях об ошибках отображения формы в документации по Django.

Если форма верна, то вы создаете новый экземпляр Comment в строках с 16 по 20. Вы можете получить доступ к данным из формы, используя form.cleaned_data, который является словарем. Перед передачей пользовательских данных в запросы к базе данных рекомендуется очистить данные формы. Таким образом, вы убедитесь, что все вводимые данные являются согласованными и безопасными.

Ключи form.cleaned_data соответствуют полям формы, поэтому вы можете получить доступ к автору, используя form.cleaned_data["author"] в строке 17, а к тексту комментария - form.cleaned_data["body"] в строке 18.

Чтобы правильно создать объект Comment в вашей базе данных, вы должны подключить его к существующему объекту Post в строке 19. Вы берете соответствующую запись с первичным ключом представления в строке 11.

После того, как вы создали комментарий в форме, вы сохраняете его, используя .save() в строке 21, и перенаправляете пользователя на URL-адрес, который request.path_info содержится в строке 22. В вашем случае это будет URL-адрес записи в блоге.

Другими словами, это означает, что когда вы отправляете действительную форму в blog_detail() с запросом POST, Django снова вызовет blog_detail() после сохранения вашего комментария. На этот раз Django вызовет функцию просмотра с запросом GET, и запись в блоге загрузится с пустой формой и вашим комментарием в списке комментариев.

Для этих GET запросов или когда форма недействительна, остальные blog_detail() будут выполнять следующее:

  • Строка 24 запрашивает в базе данных все существующие комментарии к вашему сообщению.
  • Строки с 25 по 29 создают context, включая данные для публикации, отфильтрованные комментарии и форму.
  • Строка 30 отображает шаблон detail.html с context.

Теперь, когда context также содержит comments и form данные, вы можете обновить свой шаблон detail.html:

 1<!-- blog/templates/blog/detail.html -->
 2
 3{% block page_title %}
 4    <h2>{{ post.title }}</h2>
 5{% endblock page_title %}
 6
 7{% block page_content %}
 8    <small>
 9        <!-- ... -->
10    </small>
11    <p>{{ post.body | linebreaks }}</p>
12
13    <h3>Leave a comment:</h3>
14    <form method="post">
15        {% csrf_token %}
16        <div>
17            {{ form.author }}
18        </div>
19        <div>
20            {{ form.body }}
21        </div>
22        <button type="submit" class="btn btn-primary">Submit</button>
23    </form>
24
25    <h3>Comments:</h3>
26    {% for comment in comments %}
27        <p>
28            On {{ comment.created_on.date }} <b>{{ comment.author }}</b> wrote:
29        </p>
30        <p>
31            {{ comment.body | linebreaks }}
32        </p>
33    {% endfor %}
34{% endblock page_content %}

Под публикацией отображается ваша форма. Если вы не задаете атрибут action для формы, то действие с формой указывает на страницу, на которой вы находитесь в данный момент. Затем вы добавляете csrf_token, который обеспечивает безопасность и отображает поля "Текст" и "автор" формы, а затем кнопку "Отправить".

Наконец, вы просматриваете все комментарии к данному сообщению. Вместо отображения полной временной метки отображается только дата для атрибута .created_on комментария.

Перейдите на страницу http://localhost:8000/ и посмотрите свои обновления в действии:

Благодаря этим изменениям вы сделали свой блог по-настоящему интерактивным. Теперь ваши читатели могут делиться своими отзывами непосредственно под каждым постом.

Внедрение комментариев с помощью функции Django form в вашем приложении для блога было последним шагом в этом руководстве. Если вы хотите сравнить свой код с окончательным кодом этого руководства, вы можете перейти по ссылке ниже:

Получите свой код: Нажмите здесь, чтобы бесплатно загрузить исходный код для создания блога с нуля с помощью Django.

Заключение

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

В этом руководстве вы узнали, как:

  • Создать новый проект Django
  • Создание и редактирование записей в блоге
  • Показывать сообщения пользователю
  • Присвойте сообщениям категории
  • Разрешить пользователям комментировать сообщения

Кроме того, вы узнали о сайте администратора Django и использовали формы Django для взаимодействия с пользователями.

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

<статус завершения статьи-slug="создание блога с нуля на django" class="btn-group mb-0" url-адрес закладки для данных api-статьи="/api/v1/articles/создание блога с нуля на django"/закладка/" data-api-article-completion-status-url="/api/v1/articles/build-a-blog-from-scratch-django/completion_status/"> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0 Как создать блог с нуля с помощью Django" email-subject="Статья о Python для вас" twitter-text="Интересная статья о Python от @realpython:" url="https://realpython.com/build-a-blog-from-scratch-django /" url-title="Создайте блог с нуля с помощью Django">

Вернуться на верх