Вы находитесь на странице: 1из 310

Django для начинающих

Научитесь веб-разработки с Django 2.0

William S. Vincent
© 2018 William S. Vincent

Группа в ВК "Django_Python"
Содержание

Введение 1
Почему Django 1
Почему эта книга 3
Структура книги 3
Макет книги 5
Вывод 7
Глава 1: Первоначальная Настройка 8
Командная строка 8
Установка Python 3 на Mac OS X 11
Установка Python 3 на Windows 13
Установка Python 3 на Linux 14
Установка Виртуальных Среды 15
Джанго 16
Установить Git 20
Текстовой редактор 21
Вывод 21
Глава 2: Hello World приложение 22
Начальная настройка 22
Создание приложения 25
Views и URL конфигурация 27
Привет мир! 30
Git 31
Bitbucket 33
Вывод 37
Глава 3: Страницы приложения 39
Начальная настройка 39
Шаблоны 41
Основа Классов 44
Views URLs 45
Добавление страницы 47

Группа в ВК "Django_Python"
ОГЛАВЛЕНИЕ

Расширение Шаблонов 49
Тесты 52
Git и Bitbucket 54
Local vs Production 56
Heroku(облачная PaaS-платформа) 57
Дополнительный файл 58
Deploy(развертывание) 61
Вывод 64
Глава 4: Приложение Доска Объявлений 65
Начальная настройка 65
Создание модели базы данных 68
Активация моделей 69
Джанго Админ 70
Views/Templates/URLs 77
Добавление новых записей 81
Тесты 85
Bitbucket 90
Heroku конфигурация 91
Heroku развертывание 93
Выводы
95
Глава 5: Приложение Блог 96
Начальная настройка 96
Модель базы данных 98
Админ 100
URLs 105
Views 107
Templates 108
Static files 111
Отдельные страницы блога 116
Тесты 122
Git 125
Выводы 125
Глава 6: Формы 127
Формы 127
Обновление форм 138
Удаление View 145
Тесты 151
Выводы 155
Глава 7: Учетные Записи Пользователей 156

Группа в ВК "Django_Python"
ОГЛАВЛЕНИЕ

Вход 156
Обновление Домашней страницы 159
Ссылка "выход" 161
Регистрация 164
Bitbucket 170
Конфигурация Heroku 171
Развертывание на Heroku 173
Выводы 177
Глава 8: Пользовательская Модель 178
Установка 178
Пользовательская User Model 180
Формы 182
Суперпользователь 185
Выводы 187
Глава 9: Аутентификация Пользователя 188
Templates 188
URLs 192
Admin 197
Выводы 203
Глава 10: Bootstrap 204
Приложение Страницы 204
Тесты 208
Bootstrap 211
Форма Регистрации 219
Дальнейшие действия 224
Глава 11: смена и сброс пароля 226
Изменение пароля 226
Настройка изменения пароля 228
Сброс пароля 231
Пользовательские шаблоны 235
Вывод 240
Глава 12: Электронная Почта 241
SendGrid 241
Пользовательская эл.почта 246
Вывод 250
Глава 13: Приложение газета 251
Приложение Статьи 251

Группа в ВК "Django_Python"
ОГЛАВЛНИЕ

URLs и Views 257


Редактирование/Удаление 262
Создание страницы 268
Выводы 276
Глава 14: Права доступа и авторизация 277
Улучшаем CreateView 277
Авторизация 279
Mixins 281
Обновление views 283
Вывод 285
Глава 15: Комментарии 286
Model 286
Admin 288
Template 295
Вывод 299
Вывод 301
Ресурсы Джанго 302
Питон книги 302
Блоги 303
Обратная связь 303

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

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

Django-это бесплатный веб-фреймворк с открытым исходным кодом, написанный на языке


программирования Python и используемый миллионами программистов каждый год. Его
популярность обусловлена дружелюбием как к начинающим, так и продвинутым
программистам: Django достаточно надежен для использования крупнейшими веб–сайтами
в мире – Instagram, Pinterest, Bitbucket, Disqus, но также достаточно гибок, чтобы быть
хорошим выбором для стартапов на ранней стадии и прототипирования личных проектов.

Эта книга регулярно обновляется и содержит последние версии Django (2.0) и Python (3.6 x).
Он также использует Pipenv, который теперь официально Рекомендуемый менеджер
пакетов Python.org для управления пакетами Python и виртуальными средами. На всем
протяжении мы будем использовать лучшие современные практики сообщества Django,
Python и веб-разработки, особенно тщательное использование тестирования.

Почему Django

Веб-фреймворк - это набор модульных инструментов, которые абстрагируют большую часть


трудностей и повторений, присущих веб-разработке. Например, большинству веб-сайтов

Группа в ВК "Django_Python"
Introduction 2

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


URL маршрутов, отображение контента на странице, корректная защита и так далее. Вместо
того, чтобы воссоздавать все это с нуля, программисты на протяжении многих лет создавали
веб-фреймворки на всех основных языках программирования: Django и Flask в Python, Rails в
Ruby и Express в JavaScript среди многих, многих других.
Django унаследовал подход Python “batteries-included” и включает в себя поддержку из
коробки для общих задач в веб-разработке:

• Аутентификация пользователя
• Шаблоны, маршруты и представления
• Интерфейс администратора
• Надежная безопасность
• Поддержка нескольких серверных баз данных
• И многое другое

Такой подход значительно упрощает нашу работу как веб-разработчиков. Мы можем


сосредоточиться на этом, что делает наше веб-приложение уникальным, а не изобретать
колесо, когда дело доходит до стандартной функциональности веб-приложения.
Напротив, несколько популярных фреймворков–в первую очередь Flask на Python и Express
на JavaScript-используют подход “микропрограммирования”. Они предоставляют только
минимум, необходимый для простой веб-страницы, и оставляют на усмотрение
разработчика установку и настройку сторонних пакетов для репликации базовой
функциональности веб-сайта. Такой подход обеспечивает большую гибкость для
разработчика, но также дает больше возможностей для ошибок.
По состоянию на 2018 Год Django находится в активной разработке более 13 лет, что делает
его седым ветераном в программных годах. Миллионы программистов уже использовали
Django для создания своих сайтов. И это, несомненно, хорошо. Веб-разработка трудна. Не
имеет смысла повторять один и тот же код–и ошибки–когда, большое сообщество
блестящих разработчиков уже решило эти проблемы для нас.

Группа в ВК "Django_Python"
Introduction 3

В то же время, Django остается в стадии активной разработки и имеет годовой график


выпуска. Сообщество Django постоянно добавляет новые функции и улучшения
безопасности. Если вы создаете сайт с нуля Джанго это отличный выбор.

Почему эта книга

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

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


трудным выбором: они могли бы подчеркнуть простоту использования Django или его
глубину, но не то и другое одновременно. Они выбирают последнее, и как
профессиональный Разработчик я ценю этот выбор, но как новичок я нашел это
как...разочарование!

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

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

Структура Книги

Мы начнем с правильного описания того, как настроить локальную среду разработки


в главе 1. В этой книге мы используем кропотливые инструменты: самую последнюю версию

Группа в ВК "Django_Python"
Introduction 4

Django (2.0), Python (3.6) и Pipenv для управления нашими виртуальными средами. Мы также
пройдем введение в командную строку и обсудим как работать с современным текстовым
редактором.
В Главе 2 мы создадим наш первый проект, минимальное Hello, World приложение, которое
демонстрирует, как настроить новые проекты Django. Так как создание хорошего
программного обеспечения важно, мы также сохраним нашу работу в git и загрузим копию в
удаленный репозиторий кода на Bitbucket.
В Главе 3 мы создадим, протестируем и развернем Приложение Pages, которое
представляет шаблоны и view на основе классов. Шаблоны это то, как Django позволяет
создавать (Не Повторяйтесь - принцип разработки программного обеспечения, нацеленный
на снижение повторения информации различного рода) разработку с помощью HTML и CSS,
в то время как view на основе классов требуют минимального количества кода для
использования и расширения основной функциональности в Django. Они потрясающие, вы
это скоро увидите. Мы также добавим наши первые тесты и развернем в Heroku, который
имеет свободный уровень и который мы будем использовать на протяжении всей этой
книги. Использование таких поставщиков услуг, как Heroku, превращает разработку из
болезненного, трудоемкого процесса во что-то, что занимает всего несколько щелчков
мыши.
В Главе 4 мы создадим наш первый проект с базой данных, приложение для доски
объявлений. Django предоставляет мощный ORM, который позволяет нам писать сжатый
Python для наших таблиц базы данных. Мы рассмотрим встроенное приложение
администратора, которое обеспечивает графический способ взаимодействия с нашими
данными и может быть даже использовано в качестве системы управления контентом (CMS),
аналогичной Wordpress. Также мы напишем тесты для всего нашего кода, сохраним
удаленную копию на Bitbucket и развернем в Heroku. Наконец, в Главах 5-7 мы готовы к
нашему финальному проекту: надежному приложению для блога, которое демонстрирует,
как выполнять функциональность CRUD (Create-Read-Update-Delete)(Создание-Чтение-
Обновление-Удаление) в Django. Мы обнаружим, что общие view на основе классов Django
означают, что для этого нам нужно написать лишь небольшое количество реального кода!
Затем мы добавим формы и интегрируем встроенную систему аутентификации
пользователей Django (login, logout, signup).

В Главе 8 мы начнем сайт газеты и введем понятие пользовательских моделей, Django


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

Группа в ВК "Django_Python"
Introduction 5

проекты должны использовать пользовательскую модель, и в этой главе вы узнаете, как это
сделать. Глава 9 охватывает аутентификацию пользователей, Глава 10 добавляет Bootstrap
для стилизации, а главы 11-12 реализуют сброс пароля и изменение по электронной почте.
В главах 13-15 мы добавляем статьи и комментарии к нашему проекту, а также
соответствующие разрешения и полномочия. Мы даже узнаем некоторые приемы для
настройки администратора, чтобы отобразить наши растущие данные.

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


ресурсов для дальнейшего обучения.
Хотя вы можете выбирать главы для чтения, структура книги очень продумана. Каждое
приложение / глава вводит новую концепцию и усиливает прошлое обучение. Я
настоятельно рекомендую прочитать его по порядку, даже если вы хотите пропустить
перейдя вперед. Более поздние главы не будут охватывать предыдущий материал в той же
глубине, что и предыдущие главы.
К концу этой книги вы будете иметь представление о том, как работает Django, возможность
создавать приложения самостоятельно, и основу необходимую, чтобы в полной мере
воспользоваться дополнительными ресурсами для изучения промежуточных и передовых
методов Django.

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

Code

# This is Python code


print(Hello, World)

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

Группа в ВК "Django_Python"
Introduction 6

Code

def make_my_website:

...

print("All done!")

Мы также часто будем использовать консоль командной строки (начиная с главы 1:


Начальная настройка для выполнения команд, которые принимают форму префикса $ в
традиционном стиле Unix.

Command Line

$ echo "hello, world"

Результатом этой конкретной команды является следующая строка:

Command Line

"hello, world"

Обычно мы объединяем команду и ее вывод. Команда всегда будет после символа $, а


выходные данные-нет. Например, команда и результат выше будут представлены
следующим образом:

Command Line

$ echo "hello, world"

hello, world

Полный исходный код всех примеров можно найти в официальном репозитории github.

Группа в ВК "Django_Python"
Introduction 7

Заключение

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


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

Группа в ВК "Django_Python"
Глава 1 : Начальная настройка
В этой главе описывается, как правильно настроить компьютер для работы с Django
проектом. Мы начнем с обзора командной строки и используем ее для установки последних
версий Django (2.0) и Python (3.6 x). Затем мы обсудим виртуальные среды, git и работу с
текстовым редактором.
К концу этой главы вы будете готовы создавать и изменять новые проекты Джанго за
несколько кликов.

Командная строка

Командная строка-это мощное текстовое представление компьютера. Как разработчики мы


будем использовать его широко на протяжении всей этой книги, чтобы установить и
настроить каждый проект Django.
На Mac командная строка находится в программе Terminal, расположенной в каталоге /
Applications / Utilities. Чтобы найти его, откройте новое окно Finder, откройте папку
Applications, прокрутите вниз, чтобы открыть папку Utilities, и дважды щелкните приложение
под названием Terminal.
В Windows есть встроенная программа командной строки, но это трудно использовать. Я
рекомендую вместо этого использовать Babun, бесплатную программу командной строки с
открытым исходным кодом. На главной странице сайта Babun нажмите кнопку "Загрузить
сейчас", дважды щелкните загруженный файл, чтобы установить Babun, и по завершении
перетащите установщик в корзину. Чтобы использовать Babungo в меню Пуск, выберите
программы, и нажмите на Babun.
Забегая вперед, когда книга ссылается на” командную строку", это означает открыть новую
консоль на вашем компьютере с помощью терминала или Babun.

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 9

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

• cd (изменить каталог)
• cd .. (перейти в верх)
• ls (список файлов в текущем каталоге)
• pwd ( вывести текущий каталог)
• mkdir (создать каталог)
• touch (создать новый файл)

Откройте командную строку и попробуйте их. Знак $ dollar-это наша командная строка: все
команды в этой книге предназначены для ввода после $.
Например, предположим, что вы находитесь на Mac, давайте перейдем в каталог рабочего
стола.

Command Line

$ cd ~/Desktop

Обратите внимание, что наше текущее местоположение / Desktop автоматически


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

Command Line

~/Desktop $ pwd

/Users/wsv/desktop

На моем компьютере Mac это показывает, что я использую пользователя wsv и нахожусь на
рабочем столе для этой учетной записи.
Давайте создадим новую папку каталога с mkdir, cd и добавим в нее новый файл index.html.

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 10

Command Line

~/Desktop $ mkdir new_folder

~/Desktop $ cd new_folder

~/Desktop/new_folder $ touch index.html

Теперь используйте ls для вывода списка всех текущих файлов в нашем каталоге. Вы увидите
только что созданный index.html.

Command Line

~/Desktop/new_folder $ ls

index.html

В качестве последнего шага вернитесь в каталог рабочего стола cd .. и используйте pwd для
подтверждения местоположения.

Command Line

~/Desktop/new_folder $ cd ..

~/Desktop $ pwd

/Users/wsv/desktop

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


для навигации по компьютеру ; с практикой этот подход станет намного быстрее, чем с
помощью мыши.
В этой книге я дам вам точные инструкции для выполнения, вам не нужно быть экспертом в
командной строке и со временем это будет хороший навык для любого профессионального
разработчика программного обеспечения. Два хороших бесплатных ресурса для
дальнейшего изучения-это Command Line Crash Course и CodeCademy’s Course on the
Command Line.

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 11

Ниже приведены инструкции для компьютеров Mac, Windows и Linux.

Установка Python 3 на Mac OS X

Хотя Python 2 установлен по умолчанию на компьютерах Mac, Python3 в нем нет. Вы можете
подтвердить это, набрав python --version в консоли командной строки и нажав Enter:

Command Line

$ python --version

Python . .

Чтобы проверить, установлен ли Python 3, попробуйте выполнить ту же команду, используя


python3 вместо python.

Command Line

$ python --version

Если ваш компьютер выводит �.�.x ((любая версия 3.6 или выше), то у вас все в порядке,
однако, скорее всего, вы увидите сообщение об ошибке, так как нам нужно установить
Python 3 непосредственно.
Наш первый шаг-установить пакет Xcode от Apple, поэтому выполните следующую
команду, чтобы установить его:

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 12

Command Line

$ xcode-select --install

Щелкните по всем командам подтверждения (Xcode - это большая программа, поэтому для
установки может потребоваться некоторое время, в зависимости от вашего интернет-
соединения).

Затем установите менеджер пакетов Homebrew через длинную команду ниже:

Command Line

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/inst\

all/master/install)"

Чтобы проверить правильность установки Homebrew, выполните следующую команду:

Command Line

$ brew doctor

Your system is ready to brew.

И что бы установить последнюю версию Python, выполните следующую команду:

Command Line

$ brew install python

Сейчас мы проверим, какая версия была установлена:

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 13

Command Line

$ python --version

Python . .

Чтобы открыть интерактивную оболочку Python 3 (это позволит нам запускать команды
Python прямо на нашем компьютере )просто введите python3 :

Command Line

$ python

Python . . (default, Jan , : : )

[GCC . . Compatible Apple LLVM . . (clang- . . . )] on darwin

Type "help", "copyright", "credits" or "license" for more information.

>>>

Для выхода из интерактивной оболочки Python 3 в любое время нажмите Control-


d (клавиши” Control “и” d " одновременно).
Вы все еще можете запускать оболочки Python с Python 2, просто набрав python:

Command Line

$ python

Python . . (default, Dec , : : )

[GCC . . Compatible Apple LLVM . . (clang- . . . )] on darwin

Type "help", "copyright", "credits" or "license" for more information.

Установка Python 3 на Windows

Python не включен по умолчанию в Windows, однако мы можем проверить, существует ли


какая-либо версия в системе. Откройте консоль командной строки, введя cmd в меню Пуск
(Поиск Windows))

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 14

Или вы можете удерживать клавишу SHIFT и щелкнуть правой кнопкой мыши на рабочем
столе, а затем выбрать «Открыть командное окно» здесь или - "Открыть окно PowerShell
здесь" в windows 10.

Введите следующую команду и нажмите Enter:

Command Line

python --version

Python . .

Если вы видите такой вывод, Python уже установлен. Но скорее всего, этого не будет!
Чтобы загрузить Python 3, перейдите в раздел загрузки официального сайта Python.
Загрузите установщик и убедитесь, что вы выбрали опцию Add Python to PATH , которая
позволит использовать python прямо из командной строки. В противном случае мы должны
были бы ввести полный путь нашей системы и изменить наши переменные среды вручную.
После установки Python выполните следующую команду в новой консоли командной
строки:

Command Line

python --version

Python . .

Если это сработало, вы закончили!

Установка Python 3 на Linux

Добавление Python 3 в дистрибутив Linux требует немного больше работы. Здесь


рекомендуются последние руководства для Centos и Debian. Если вам нужна
Дополнительная помощь в добавлении Python к вашему пути, обратитесь к этому ответу
StackOverflow.

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 15

Виртуальная среда

Виртуальные среды являются неотъемлемой частью программирования Python. Они


представляют собой изолированный контейнер, содержащий все зависимости
программного обеспечения для данного проекта. Это важно, потому что по умолчанию
программное обеспечение, такое как Python и Django, установлено в одном каталоге. Это
приводит к проблеме, когда требуется работать над несколькими проектами на одном
компьютере. Что делать, если проект использует Django 2.0, но ProjectB с прошлого года все
еще находится на Django 1.10? Без виртуальных сред это становится очень трудно; с
виртуальными средами это вообще не проблема.

Есть много областей разработки программного обеспечения, которые горячо обсуждаются,


но использование виртуальных сред для разработки Python не является одним. Для каждого
нового проекта Python следует использовать выделенную виртуальную среду.

Исторически разработчики Python использовали virtualenv или pyenv для настройки


виртуальных сред. Но в 2017 известных разработчиков Python KennethReitz Выпущено Pipenv
который теперь официально Рекомендуемый инструмент упаковки питона.

Pipenv похож на npm и yarn из экосистемы узлов: он создает файл канала, содержащий
зависимости программного обеспечения и Pipfile. Блокировка для обеспечения
детерминированных построений. “Детерминизм” означает, что каждый раз, когда вы
загружаете программное обеспечение в новой виртуальной среде, у вас будет точно такой
же конфигурации. Себастьян Маккензи, создатель Yarn, который впервые представил эту
концепцию для упаковки JavaScript, имеет краткое сообщение в блоге, объясняющее, что
такое детерминизм и почему это имеет значение.
Конечным результатом является то, что мы создадим новую виртуальную среду с pipenv
для каждого нового проекта Django.
Для установки Pipenv мы можем использовать pip , который автоматически установился
для нас вместе с Python 3.

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 16

Command Line

$ pip install pipenv

Установка Django

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

Command Line

$ cd ~/Desktop

$ mkdir django

$ cd django

Теперь используйте Pip env для установки Django.

Command Line

$ pipenv install django

Если вы посмотрите в наш каталог, есть два новых файла: Popfile и Pipe-file.lock. У нас есть
информация, необходимая для новой виртуальной среды, но мы ее еще не активировали.
Давайте сделаем это с помощью оболочки pip env shell.

Command Line

$ pipenv shell

Если вы находитесь на Mac, вы должны увидеть круглые скобки в командной строке с


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

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 17

Обратите внимание, что из-за открытой ошибки пользователи Windows не будут видеть
визуальную обратную связь виртуальной среды в это время. Но если вы сможете запустить
Django-admin startproject в следующем разделе, то вы узнаете, что в вашей виртуальной
среде установлен Django.

Command Line

(django-JmZ NTQw) $

Это означает, что все работает! Создайте новый проект Django с именем test с помощью
следующей команды. Не забудьте поставить точку в конце через пробел.

Command Line

(django-JmZ NTQw) $ django-admin startproject test_project .

Стоит остановиться здесь, чтобы объяснить, почему вы должны добавить точку через
пробел к команде. Если вы просто запустите django-admin startproject test_project, то по
умолчанию Django создаст эту структуру каталогов:

└── test_project

├── manage.py

└── test_project

├── __init__.py

├── settings.py

├── urls.py

└── wsgi.py

Посмотрите, как он создает новый каталог test_project, а затем в нем manage.py файл и
каталог test_project? Это кажется мне излишним, так как мы уже создали и перешли в папку
django на нашем рабочем столе. Запустив django-admin startproject test_project . с точкой в
конце-который говорит, установите в текущем каталоге Результат вместо этого будет:

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 18

├── manage.py

└── test_project

├── __init__.py

├── settings.py

├── urls.py

└── wsgi.py

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

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

Command Line

(django-JmZ NTQw) $ python manage.py runserver

Если вы перейдете в http://127.0.0.1:8000/ вы должны увидеть следующее изображение:

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 19

Страница приветствия Django

Чтобы остановить наш локальный сервер, введите Control-c. Затем выйдите из нашей
виртуальной среды с помощью команды exit.

Command Line

(django-JmZ NTQw) $ exit

Мы всегда можем снова активировать виртуальную среду с помощью pipenv shell в любое
время.

Мы получим много практики с виртуальными средами в этой книге, так что не волнуйтесь,
если это

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 20

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

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

Установка Git
Git является неотъемлемой частью современной разработки программного обеспечения.
Это система контроля версий, которая может рассматриваться как чрезвычайно мощная
версия отслеживания изменений в Microsoft Word или Google Docs. С помощью git вы
можете сотрудничать с другими разработчиками, отслеживать всю вашу работу с помощью
фиксаций и возвращаться к любой предыдущей версии вашего кода, даже если вы случайно
удалили что-то важное!
На Mac, поскольку Homebrew уже установлен, мы можем просто ввести brew install git в
командной строке:

Command Line

$ brew install git

В Windows вы должны скачать Git из Git for Windows. Нажмите кнопку «Загрузить» и
следуйте инструкциям по установке. Нажмите «Далее» на всех шагах, кроме пятого,
“Adjusting your PATH environment.” Вместо этого выберите нижнюю опцию: “Use Git and
optional Unix tools from the Windows Command Prompt.”
После установки нам нужно выполнить одноразовую настройку системы, чтобы настроить
ее, объявив имя и адрес электронной почты, которые вы хотите связать со всеми вашими
обязательствами Git (подробнее об этом в ближайшее время).
В консоли командной строки введите следующие две строки. Не забудьте обновить их имя и
адрес электронной почты.

Группа в ВК "Django_Python"
Chapter 1: Initial Setup 21

Command Line

$ git config --global user.name "Your Name"

$ git config --global user.email "yourname@email.com"

Вы всегда можете изменить эти настройки, если пожелаете, введя те же команды с новым
именем или адресом электронной почты.

Текстовой редактор

Последний шаг это ваш текстовый редактор. В то время как командная строка, где мы
выполняем команды для наших программ по сути текстовый редактор. Компьютеру все
равно, какой текстовый редактор вы используете конечный результат просто код, но
хороший текстовый редактор может предоставить полезные советы и поймать опечатки для
вас.
Опытные разработчики часто предпочитают использовать либо Vim, либо Emacs, оба
десятилетних текстовых редактора с лояльными последователями. Однако каждый из них
имеет крутую кривую обучения и требует запоминания множества различных комбинаций
клавиш. Я не рекомендую их новичкам.
Современные текстовые редакторы сочетают в себе те же мощные функции с
привлекательным визуальным интерфейсом. Мой текущий фаворит это Visual Studio Code,
который является бесплатным, простым в установке и пользуется широкой популярностью.
Если вы еще не используете текстовый редактор, загрузите и установите Visual Studio Code.

Вывод

УФ! Никто на самом деле не любит настраивать локальную среду разработки, но, к счастью,
это одноразовая боль. Теперь мы научились работать с виртуальными средами и
установили последнюю версию Python и git. Все готово для вашего первого приложения на
Django.

Группа в ВК "Django_Python"
Глава 2 : Hello World приложение
В этой главе мы будем строить проект Django, который просто говорит: “Привет, мир” на
главной странице. Это традиционный способ начать новый язык программирования или
фреймворк. Мы также впервые будем работать с git и развертывать наш код в Bitbucket.

Начальная настройка

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

Command Line

$ cd ~/Desktop

$ mkdir helloworld

$ cd helloworld

Убедитесь, что вы еще не находитесь в существующей виртуальной среде и у вас


отображается текст в скобках () перед знаком доллара $. Чтобы выйти из него, введите exit и
нажмите ENTER. Скобки должны исчезнуть, что означает, что виртуальная среда больше не
активна. Мы будем использовать pipenv для создания новой виртуальной среды, установить
Django и затем активировать его.

Группа в ВК "Django_Python"
Chapter 2: Hello World app 23

Command Line

$ pipenv install django

$ pipenv shell

Если Вы находитесь на Mac, вы должны увидеть скобки в начале командной строки в форме
(helloworld-XXX) где XXX представляет случайные символы. На моем компьютере я вижу

(helloworld-���ivvZC). Я покажу (helloworld) здесь в тексте, но вы увидите что-то немного


другое на своем компьютере. Если Вы находитесь в Windows, Вы не увидите визуальную подсказку в это
время.
Создайте новый проект Django под названием helloworld_project включая пробел с точкой в конце

команды, так что бы он установился в нашем текущем каталоге.

Command Line

(helloworld) $ django-admin startproject helloworld_project .

Если сейчас вы используете команду tree вы можете увидеть, что наша структура проекта
Django теперь выглядит так. (Примечание: если tree у вас не работает установите его с
Homebrew:: brew install tree.)

Command Line

(helloworld) $ tree

├── helloworld_project

│ ├── __init__.py

│ ├── settings.py

│ ├── urls.py

│ └── wsgi.py

└── manage.py

Группа в ВК "Django_Python"
Chapter 2: Hello World app 24

directory, files

Файл settings.py управляет настройками нашего проекта, urls.py сообщает Django какие
страницы для сборки выдать в ответ на запрос браузера или url, и wsgi.py который
обозначает интерфейс шлюза веб-сервера и помогает Django обслуживать наши возможные
веб-страницы. Последний файл manage.py используется для выполнения различных команд
Django, таких как запуск локального веб-сервера или создание нового приложения.

Django поставляется со встроенным веб-сервером для локальных целей разработки. Мы


можем запустить его с помощью команды runerver.

Command Line

(helloworld) $ python manage.py runserver

Если вы посетите http://127.0.0.1:8000/ вы должны увидеть следующее изображение:

Группа в ВК "Django_Python"
Chapter 2: Hello World app 25

Django страница приветствия

Создание приложения

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


читаемым. Один проект Django содержит одно или несколько приложений, которые все
вместе обеспечивают работу всего веб-приложения. Вот почему команда для нового
проекта Django-startproject! Например, на реальном сайте электронной коммерции Django
может быть одно приложение для проверки подлинности пользователя, другое
приложение для платежей и третье приложение для получения сведений о списке
элементов. Каждое фокусируется на изолированной части функциональности.

Группа в ВК "Django_Python"
Chapter 2: Hello World app 26

Нам нужно создать наше первое приложение, которое мы назовем pages. Из командной
строки закройте сервер с помощью Control+c. Затем используйте команду startup.

Command Line

(helloworld) $ python manage.py startapp pages

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

Command Line

├── pages

│ ├── __init__.py

│ ├── admin.py

│ ├── apps.py

│ ├── migrations

│ │ └── __init__.py

│ ├── models.py

│ ├── tests.py

│ └── views.py

Давайте рассмотрим, что делает каждый новый файл приложения pages:

• admin.py файл конфигурации для встроенного приложения администратора Django


• apps.py является конфигурационным файлом для самого приложения
• migrations/ отслеживает любые изменения в файле models.py чтобы синхронизировать
нашу базу данных с models.py
• models.py тут мы определяем наши модели базы данных, которые Django
автоматически переводит в таблицы базы данных

• tests.py предназначен для тестирования приложений

Группа в ВК "Django_Python"
Chapter 2: Hello World app 27

• views.py тут мы обрабатываем логику запроса / ответа для нашего веб-


приложения
Несмотря на то, что Наше новое приложение существует в проекте Django, Django не “знает”
об этом, пока мы явно не добавим его. В текстовом редакторе откройте settings.py файл и
прокрутите вниз до INSTALLED_APPS, где вы увидите шесть уже встроенных приложений
Django. Добавьте наше новое приложение внизу:

Code

# helloworld_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'pages', # new

Views и URLConfs

В Django, Views определяет, какой контент выводится на данной странице, URLConfs


определяет, где этот контент будет.

Когда пользователь запрашивает определенную страницу, например домашнюю страницу,


URLConf использует регулярное выражение для сопоставления этого запроса с
соответствующей Views функцией , которая затем возвращает правильные данные.

Другими словами, наша функция из Views выведет текст «Hello, World», в то время как наш
URL будет гарантировать, что когда пользователь посещает главную страницу, он будет
перенаправляется на правильную функцию Views.

Начнем с обновления файла views.py в нашем pages приложении выглядит это следующим
образом:
Группа в ВК "Django_Python"
Chapter 2: Hello World app 28

Code

# pages/views.py

from django.http import HttpResponse

def homePageView(request):

return HttpResponse('Hello, World!')

В принципе, мы указываем, что всякий раз, когда вызывается функция homePageView


вернется текст “Hello, World!” если более конкретно то мы импортировали встроенный
HttpResponse метод, чтобы мы могли вернуть ответ объекта пользователю. Для этого мы
создали функцию с именем homePageView которая принимает запрос объекта и возвращает
ответ со строкой Hello, World!.

Теперь нам нужно настроить наш URL. В приложении pages создайте новый urls.py файл.

Command Line

(helloworld) $ touch pages/urls.py

Затем обновите его с помощью следующего кода:

Группа в ВК "Django_Python"
Chapter 2: Hello World app 29

Code

# pages/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.homePageView, name='home')

В верхней строке мы импортируем path из Django, чтобы использовать наш шаблон url, и на
следующей строке мы импортируем наш файл views. Использованный пробел с точкой
from . import views означает ссылку на текущий каталог, который является нашим
приложением pages, содержащим как views.py, так и urls.py.
Наш шаблон URL состоит из трех частей:

• регулярное выражение Python для пустой строки ''


• указывает функцию homePageView из файла views
• добавляет не обязательное url имя'home'

Другими словами, если пользователь запрашивает домашнюю страницу, представленную


пустой строкой " , то используется функция файла views с именем homePageView.

Мы уже почти закончили. Последний шаг это настройка на уровне проекта файла urls.py.
Помните, что обычно в одном проекте Django есть несколько приложений, поэтому
каждому из них нужен свой собственный маршрут.

Обновите файл helloworld_project/urls.py следующим образом:

Группа в ВК "Django_Python"
Chapter 2: Hello World app 30

Code

# helloworld_project/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('', include('pages.urls')),

Мы импортировали include во второй строке рядом с path, а затем создали новый шаблон
url-адреса для нашего приложения pages. Теперь, когда пользователь посещает главную
страницу ' ', он сначала будет перенаправлен на приложение pages, а затем на views
домашней страницы.

Это часто путает начинающих, то что нам здесь не нужно импортировать приложение pages,
но мы ссылаемся на него в нашем шаблоне url как pages.urls. Причина, по которой мы
делаем это, заключается в том, что метод django.urls.include () ожидает, что мы перейдем в
модуль или приложение в качестве первого аргумента. Поэтому без использования include
нам нужно будет импортировать наше приложение pages, но поскольку мы используем
include, мы не должны это делать на уровне проекта!

Hello, world!

У нас есть весь код, который нам сейчас нужен ! Чтобы подтвердить, что все работает так,
как и ожидалось, перезагрузите наш сервер Django:

Группа в ВК "Django_Python"
Chapter 2: Hello World app 31

Command Line

(helloworld) $ python manage.py runserver

Если вы обновите браузер для http://127.0.0.1:8000/ теперь он отображает текст “Hello,


world!”

Hello world homepage

Git
В предыдущей главе мы также установили git, который является системой контроля версий.
Давайте используем его здесь. Первый шаг - инициализировать (или добавить) git в наш
репозиторий.

Command Line

(helloworld) $ git init

Если вы введете git status то увидите список изменений с момента последней


фиксации(git commit). Поскольку это наша первая фиксация, это список всех наших
изменений до сих пор.

Группа в ВК "Django_Python"
Chapter 2: Hello World app 32

Command Line

(helloworld) $ git status

On branch master

No commits yet

Untracked files:

(use "git add <file>..." to include in what will be committed)

db.sqlite

helloworld_project/

manage.py

pages/

Pipfile

Pipfile.lock

ничего не добавлено к фиксации, но присутствуют неотслеживаемые файлы (используйте" git add " для отслеживания)

Теперь мы хотим добавить все изменения с помощью команды add-A, а затем


зафиксировать изменения вместе с сообщением, описывающим, что изменилось.

Command Line

(helloworld) $ git add -A

(helloworld) $ git commit -m 'initial commit'

Обратите внимание, что пользователи Windows могут получить сообщение об ошибке git
commit error: pathspec ‘commit’ did not match any file(s) known to которая, по-
видимому, связана с использованием одинарных кавычек ' ' и отличается от двойных
кавычек" ". Если вы видите эту ошибку, используйте двойные кавычки для всех сообщений
фиксации в будущем.

Группа в ВК "Django_Python"
Chapter 2: Hello World app 33

Bitbucket

Хорошей привычкой является создание удаленного хранилища кода для каждого проекта.
Таким образом, у вас есть резервная копия на случай, если что-то случится с вашим
компьютером, и, что более важно, она позволяет сотрудничать с другими разработчиками
программного обеспечения. Два самых популярных варианта Bitbucket и Github.

В этой книге мы будем использовать Bitbucket, потому что он позволяет частные


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

Чтобы начать работу с Bitbucket, зарегистрируйте бесплатную учетную запись. После


подтверждения аккаунта по электронной почте вы будете перенаправлены на страницу
приветствия. Нажмите на ссылку “создать хранилище”.

Группа в ВК "Django_Python"
Chapter 2: Hello World app 34

Bitbucket страница приветствия

Затем на странице” CreateRepo “введите имя вашего репозитория:”hello-world". Затем


нажмите синюю кнопку “Create repository button”:

Группа в ВК "Django_Python"
Chapter 2: Hello World app 35

Bitbucket create repo

На следующей странице внизу, нажмите на ссылку “ I have an existing project” в


которой откроется выпадающий список:

Группа в ВК "Django_Python"
Chapter 2: Hello World app 36

Bitbucket существующий проект

Мы уже в каталоге нашего repo, пропустим Шаг 1 (Step 1). В шаге 2(Step 2) мы используем
две команды для добавления проекта в Bitbucket. Обратите внимание, что ваша команда
будет отличаться от моей, так как у вас другое имя пользователя. Пример общего формата
расположен ниже, где <USER> - Ваше имя пользователя Bitbucket.

Command Line

(helloworld) $ git remote add origin https://<USER>@bitbucket.org/<USER>/hello-w\

orld.git

После выполнения этой команды для настройки git с этим репозиторием Bitbucket мы
должны «вставить» наш код в него.

Группа в ВК "Django_Python"
Chapter 2: Hello World app 37

Command Line
(helloworld) $ git push -u origin master

Сейчас, если вы вернетесь на свою страницу Bitbucket и обновите ее, вы увидите, что код
теперь находится в онлайн! Нажмите на вкладку “Source” слева, чтобы увидеть все это.

Bitbucket обзор

После этого выйдите из виртуальной среды с помощью команды exit.

Command Line

(helloworld) $ exit

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

Вывод

Поздравляю! Мы охватили много фундаментальных понятий в этой главе. Мы создали


наше первое Приложение Django и узнали о структуре project/app (проект/приложение) в
Django.

Группа в ВК "Django_Python"
Chapter 2: Hello World app 38

Мы начали изучать views, urls и внутренний веб-сервер. А так же мы работали с git, чтобы
отслеживать наши изменения, и поместили наш код в частный репозиторий Bitbucket.
Перейдем к главе 3: Простое приложение, где мы создадим и развернем более сложное
приложение Django с использованием шаблонов(templates) и представлений(views) на
основе классов.

Группа в ВК "Django_Python"
Глава �: Pages приложение
В этой главе мы создадим, протестируем и развернем Приложение Pages с главной
страницей home и страницей about. Мы также узнаем о классовых views и templates Django,
которые являются строительными блоками для более сложных веб-приложений,
построенных в книге позже .

Начальная настройка

Как и в главе 2: Hello World приложение, наша первоначальная настройка включает в себя
следующие шаги:
• создание нового каталога для нашего кода
• установить Django в новой виртуальной среде
• создание нового проекта Django
• создание нового приложения pages
• обновление(настройка) settings.py

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


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

В новой консоли командной строки введите следующие команды:

Группа в ВК "Django_Python"
Chapter 3: Pages app 40

Command Line

$ cd ~/Desktop

$ mkdir pages

$ cd pages

$ pipenv install django

$ pipenv shell

(pages) $ django-admin startproject pages_project .

(pages) $ python manage.py startapp pages

Я использую здесь(pages) для представления виртуальной среды, но в реальности у меня


выглядит так (pages-unOYeQ�e). Имя же вашей виртуальной среды будет уникальным и
выглядеть будет примерно в таком формате (pages-XXX).

Откройте текстовый редактор и перейдите к файлу settings.py. Добавьте приложение pages


в наш проект в разделе INSTALLED_APPS:

Code

# pages_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'pages', # new

Запустите локальный веб-сервер с помощью команды runserver.

Группа в ВК "Django_Python"
Chapter 3: Pages app 41

Command Line

(pages) $ python manage.py runserver

И затем перейдите на http://127.0.0.1:8000/.

Django страница приветствия

Templates(Шаблоны)
Каждый веб-фреймворк нуждается в удобном способе создания HTML файлов. В Django,
подход заключается в использовании шаблонов(templates), так что бы отдельные HTML
файлы могли обслуживаться в view веб-страницы, указанным URL.

Группа в ВК "Django_Python"
Chapter 3: Pages app 42

Стоит повторить эту модель, так как вы увидите ее снова и снова в Django разработке :
Templates, Views и URLs. Порядок, в котором вы их создаете, не имеет большого значения,
поскольку все три необходимы и работают в тесном сотрудничестве. URLs управляют
начальным маршрутом, точкой входа на страницу, например /about, views содержат
логику, а шаблон имеет HTML. Для веб-страниц, использующих модели базы данных, views
делает большую часть работы по определению какие данные доступны в шаблоне.

И так: Templates, Views, URLs. Эта модель будет справедлива для каждой веб-страницы
Django которую вы делаете. Однако потребуется некоторая практика, прежде чем вы
усвоите это.
Ок, двигаемся дальше. Вопрос о том, где разместить каталог шаблонов(templates), может
смутить новичков. По умолчанию Django ищет шаблоны в каждом приложении. Наше
pages приложение будет ожидать шаблон home.html который расположен в следующей
директории:

└── pages

├── templates

├── pages

├── home.html

Это означает, что нам нужно будет создать новый каталог шаблонов(templates), новый
каталог с именем приложения, pages, и наконец, сам наш шаблон home.html
Общий вопрос: почему эта повторяющаяся структура? Короткий ответ заключается в том,
что загрузчик шаблонов Django хочет быть действительно уверен, что он найдет
правильный шаблон, и именно так он запрограммирован на их поиск.
К счастью, есть еще один часто используемый подход построения шаблонов Django-
проекта. Вместо этого необходимо создать единый каталог шаблонов на уровне проекта,
доступный для всех приложений. Это тот подход, который мы и будем использовать.

Группа в ВК "Django_Python"
Chapter 3: Pages app 43

Сделав небольшую настройку в файле settings.py, мы можем указать Django также искать в
папке на уровне проекта для шаблонов.

Сначала остановите наш сервер с помощью Control-c. Затем создайте папку на уровне
проекта с именем templates и HTML файл с именем home.html.

Command Line

(pages) $ mkdir templates

(pages) $ touch templates/home.html

Далее нам нужно обновить settings.py и указать Django, чтобы он искал шаблоны на уровне
проекта. Для этого изменим строку 'DIRS' под TEMPLATES.

Code

# pages_project/settings.py

TEMPLATES = [

...

'DIRS': [os.path.join(BASE_DIR, 'templates')],

...

},

Затем мы можем добавить простой заголовок к нашему home.html файлу.

Группа в ВК "Django_Python"
Chapter 3: Pages app 44

Code

<!-- templates/home.html -->

<h1>Homepage.</h1>

Ок, наш шаблон готов! Следующий шаг это настройка url и views.

Class-Based Views
Ранние версии Django поставлялись только с функциональными views, но вскоре
разработчики обнаружили что снова и снова повторяли одни и те же действия. Напишите
view со списком всех объектов в модели. Напишите view, в котором отображается только
один подробный элемент модели и так далее.

Основанные на функциях view, были представлены, что бы абстрагировать эти


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

Классы это фундаментальная часть Python, но полное обсуждение их выходит за рамки


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

В нашем view мы будем использовать встроенный TemplateView . Обновить pages/


views.py файл.

Группа в ВК "Django_Python"
Chapter 3: Pages app 45

Code

# pages/views.py

from django.views.generic import TemplateView

class HomePageView(TemplateView):

template_name = 'home.html'

Обратите внимание, что мы капитализировали наш view, так как теперь это класс Python.
Классы, в отличие от функций, всегда должны быть капитализированы. TemplateView уже
содержит всю логику, необходимую для отображения нашего шаблона, нам просто нужно
указать имя шаблона.

URLs

Последним шагом мы обновим наш URLConfs. Вспомним из главы 2, Что нам нужно
внести изменения в двух местах. Сначала мы обновляем на уровене проекта urls.py файл,
чтобы указать на наше pages приложение, а затем в urls.py файле pages приложения мы
сопоставим с маршрутами views .

Давайте начнем с уровня проекта urls.py файл.

Группа в ВК "Django_Python"
Chapter 3: Pages app 46

Code

# pages_project/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('', include('pages.urls')),

Давайте рассмотрим код на этом этапе. Мы добавили include во второй строке, чтобы
указать существующий URL-адрес приложения pages.
Далее создайте на уровне приложения urls.py файл.

Command Line

(pages) $ touch pages/urls.py

И добавьте следующий код.

Code

# pages/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.HomePageView.as_view(), name='home'),

Группа в ВК "Django_Python"
Chapter 3: Pages app 47
Этот шаблон почти идентичен тому, что мы сделали в главе 2 с одним существенным
отличием. При использовании Views на основе класса мы всегда добавляем as_view() в
конце имени view.

Мы закончили! Если сейчас вы запустите сервер командой python manage.py runserver и


перейдете к http://127.0.0.1:8000/ вы увидете нашу новую домашнюю страницу.

Домашняя страница

Добавить About страницу

Процесс добавления страницы очень похож на то, что мы только что сделали. Мы создадим

новый файл шаблона, новый view и новый url.

Закройте сервер с помощью Control-c и создайте новый шаблон с именем about.html.

Command Line

(pages) $ touch templates/about.html

Затем введите в него короткий HTML заголовок.

Группа в ВК "Django_Python"
Chapter 3: Pages app 48

Code

<!-- templates/about.html -->

<h1>About page.</h1>

Создайте новый view для страницы.(подразумевается создать новый класс или функцию в данном случае
классAboutPageView)

Code

# pages/views.py

from django.views.generic import TemplateView

class HomePageView(TemplateView):

template_name = 'home.html'

class AboutPageView(TemplateView):

template_name = 'about.html'

А затем подключите его к url-адресу about/.

Группа в ВК "Django_Python"
Chapter 3: Pages app 49

Code

# pages/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.HomePageView.as_view(), name='home'),

path('about/', views.AboutPageView.as_view(), name='about'),

Запустите веб сервер командой python manage.py runserver.


Перейдите к http://127.0.0.1:8000/about и вы увидите нашу новую страницу
“About page”.

About page

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

Группа в ВК "Django_Python"
Chapter 3: Pages app 50

И это возможно! Давайте создадим файл base.html содержащий заголовок с ссылками на


две наши страницы. Сначала Control-c а затем введите следующую команду.

Command Line

(pages) $ touch templates/base.html

Django имеет минимальный язык шаблонов для добавления ссылок и базовой логики в
наши шаблоны. Вы можете увидеть полный список встроенных шаблонов здесь в
официальной документации. Теги шаблонов имеют вот такую форму{% что-то %} где “что-
то” - это сам тег шаблона. Вы даже можете создать свои собственные теги шаблонов, хотя
мы не будем делать этого в этой книге.

Чтобы добавить URL-ссылки в наш проект, мы можем использовать встроенный тег


шаблона url, который принимает имя шаблона URL в качестве аргумента. Помните, как мы
добавили необязательные имена URL в наши URL-маршруты? Вот для чего. Тег url
автоматически использует эти имена для того чтобы создать соединения для нас.

URL-Адрес нашей домашней страницы называется home, поэтому для настройки ссылки на
нее мы будем использовать следующее: {% url 'home' %}.

Code

<!-- templates/base.html -->

<header>

<a href="{% url 'home' %}">Home</a> | <a href="{% url 'about' %}">About</a>

</header>

{% block content %}

{% endblock %}

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

Группа в ВК "Django_Python"
Chapter 3: Pages app 51

Давайте обновим наш home.html и about.html для расширения base.html шаблона. Это
означает, что мы можем повторно использовать код из одного шаблона в другой шаблон.
Язык шаблонов Django поставляется с методом extends, который мы можем использовать
для этого.

Code

<!-- templates/home.html -->

{% extends 'base.html' %}

{% block content %}

<h1>Homepage.</h1>

{% endblock %}

Code

<!-- templates/about.html -->

{% extends 'base.html' %}

{% block content %}

<h1>About page.</h1>

{% endblock %}

Запустите сервер командой python manage.py runserver и снова откройте наш сайт
http://127.0.0.1:8000/ и http://127.0.0.1:8000/about вы увидите, что заголовок
волшебным образом включен в обоих местах.

Правда приятно?

Группа в ВК "Django_Python"
Chapter 3: Pages app 52

Домашняя страница с заголовком

About страница с заголовком

С шаблонами мы можем сделать гораздо больше, на практике мы обычно создаем base.html


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

Tests
Наконец мы подошли к тестам. Даже в этом базовом приложении важно добавлять тесты и
иметь привычку всегда добавлять их в наши проекты Django. По словам Джейкоба Каплан-
Мосса, одиного из создателей Джанго, “код без тестов нарушен полностью.”

Написание тестов важно, поскольку оно автоматизирует процесс подтверждения того, что
код работает должным образом. В таком приложении, как это, мы можем вручную
посмотреть и увидеть, что страница homepage и страница about page существуют и
содержат предполагаемое содержание. Но как только Django

Группа в ВК "Django_Python"
Chapter 3: Pages app 53

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

К счастью, Django поставляется с надежными, встроенными инструментами тестирования


для написания и запуска тестов.

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

Code

# pages/tests.py

from django.test import SimpleTestCase

class SimpleTests(SimpleTestCase):

def test_home_page_status_code(self):

response = self.client.get('/')

self.assertEqual(response.status_code, )

def test_about_page_status_code(self):

response = self.client.get('/about/')

self.assertEqual(response.status_code, )

Мы используем здесь SimpleTestCase поскольку мы не используем базу данных. Если бы


мы использовали базу данных, мы бы использовали TestCase. Затем мы выполняем
проверку состояния кода

Группа в ВК "Django_Python"
Chapter 3: Pages app 54

для 200 страниц, это стандартный ответ для успешного запроса HTTP. И это
причудливый способ сказать, что гарантированно данная веб-страница реально
существует, но ничего не говорит о содержании указанной страницы.

Для запуска тестов закройте server Control-c и введите python manage.py test в командной
строке:

Command Line

(pages) $ python manage.py test

Creating test database for alias 'default'...

..

----------------------------------------------------------------------

Ran tests in . s

OK

Destroying test database for alias 'default'...

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


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

Git и Bitbucket

Пришло время отслеживать наши изменения с git и выводить в Bitbucket. Начнем с


инициализации каталога.

Группа в ВК "Django_Python"
Chapter 3: Pages app 55

Command Line

(pages) $ git init

Используйте git status, чтобы увидеть все наши изменения кода, а затем git add -A, чтобы
добавить их все. Наконец, мы добавим наше первое сообщение о фиксации.

Command Line

(pages) $ git status

(pages) $ git add -A

(pages) $ git commit -m 'initial commit'

На Bitbucket создайте новый репозиторий, который мы назовем pages-app.

Bitbucket создание страницы

На следующей странице нажмите на ссылку с низу “I have an existing project”.


Скопируйте две команды для подключения, а затем нажмите repository to Bitbucket.

Группа в ВК "Django_Python"
Chapter 3: Pages app 56

Bitbucket Existing Project(существующий проект)


Он должен выглядеть следующим образом, замените wsvincent вашим именем
пользователя Bitbucket:
Command Line

(pages) $ git remote add origin git@bitbucket.org:wsvincent/pages-app.git

(pages) $ git push -u origin master

Local vs Production

До этого момента мы использовали собственный внутренний веб-сервер Django для


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

Группа в ВК "Django_Python"
Chapter 3: Pages app 57

Это называется вводом кода в производство (production). Локальный код работает только
на нашем компьютере; производственный код на внешнем сервере.
Есть много доступных поставщиков серверов, но мы будем использовать Heroku, потому
что он бесплатен для небольших проектов, широко используется и имеет относительно
простой процесс развертывания.

Heroku
Вы можете зарегистрировать бесплатный аккаунт на сайте Heroku и после того, как вы
подтвердите свою электронную почту Heroku перенаправит вас в раздел панели
инструментов сайта.

Heroku панель инструментов

Теперь нам необходимо установить Heroku’s Command Line Interface (CLI), так что мы
можем развернуть его из командной строки. Нам нужно установить Heroku глобально,
чтобы он был доступен на всем нашем компьютере, поэтому откройте новую вкладку
командной строки: Command + t на Mac, Control+ t на Windows. Если бы мы установили
Heroku в нашей виртуальной среде, она была бы доступна только там.

В новой вкладке на Mac используйте Homebrew для установки Heroku:

Группа в ВК "Django_Python"
Chapter 3: Pages app 58

Command Line

$ brew install heroku

Для правильной установки 32-разрядной или 64-разрядной версии Windows см. страницу
Heroku CLI.
После завершения установки вы можете закрыть нашу новую вкладку командной строки и
вернуться на начальную вкладку с активной виртуальной средой pages.
Введите команду heroku login и использованную электронную почту и пароль которые вы
указали при регистрации на Heroku.

Command Line

(pages) $ heroku login

Enter your Heroku credentials:

Email: will@wsvincent.com

Password: *********************************

Logged in as will@wsvincent.com

Дополнительный файл

Нам необходимо внести следующие четыре изменения в наш проект Pages, чтобы он был
готов к развертыванию онлайн на Heroku:

• обновить Pipfile.lock
• создать новый файл профиля (Procfile file)
• установить gunicorn как наш веб-сервер
• внести однострочное изменение в settings.py файл

В существующем Pipfile укажите версию Python которую мы используем, то есть �.�.


Добавьте эти две строки в конец файла.

Группа в ВК "Django_Python"
Chapter 3: Pages app 59

Code

# Pipfile

[requires]

python_version = " . "

Затем запустите pipenv lock для создания соответствующего файла


Pipfile.lock.

Command Line

(pages) $ pipenv lock

Фактически Heroku ищет в нашем Pipfile.lock информацию о нашей виртуальной

среде, поэтому мы здесь добавляем настройки языка.

Затем создайте Procfile который является специфическим для Heroku.

Command Line

(pages) $ touch Procfile

Откройте файл Procfile в текстовом редакторе и добавьте


следующее:

Code

web: gunicorn pages_project.wsgi --log-file -

Это указывает на то что используется наш существующий файл pages_project.wsgi но с


gunicorn, который является веб сервером и подходит для производства (production),
вместо Django сервера который используется только для локальной разработки.

Группа в ВК "Django_Python"
Chapter 3: Pages app 60

Command Line

(pages) $ pipenv install gunicorn

Последний шаг это однострочное изменение в settings.py. Прокрутите вниз до раздела


ALLOWED_HOSTS и добавьте '*', чтобы он выглядело как в примере ниже:

Code

# pages_project/settings.py

ALLOWED_HOSTS = ['*']

Параметр ALLOWED_HOSTS представляет имена хостов/доменов, которые может


обслуживать наш Django сайт. Это мера безопасности, предотвращающая атаки на HTTP
Host header, которые возможны даже при многих, казалось бы, безопасных конфигурациях
веб-сервера. Однако мы использовали подстановочный знак звездочку *, который означает,
что все домены приемлемы, для простоты. На сайте Django уже на уровне
производства(production) вы должны явно указать, какие домены разрешены.

Используйте git status, чтобы проверить наши изменения, добавить новые файлы, а затем
зафиксировать их:

Command Line

(pages) $ git status

(pages) $ git add -A

(pages) $ git commit -m "New updates for Heroku deployment"

И наконец отправим на Bitbucket чтобы у нас была онлайн резервная копия наших изменений кода.

Группа в ВК "Django_Python"
Chapter 3: Pages app 61

Command Line

(pages) $ git push -u origin master

Deploy (Развертывание)
Заключительный шаг фактически разворачивает на Heroku. Если вы в прошлом
настраивали сервер самостоятельно, то вы будете поражены тем, насколько простой
процесс с провайдером сервис платформы таким как Heroku.

Наш процесс будет следующим:

• Создадим новое приложение на Heroku и отправляем на него наш код.


• Добавим удаленный git “hook” для Heroku
• Настроим приложение, чтобы оно игнорировало статические файлы, которые
мы рассмотрим в последующих главах.
• Запустим сервер Heroku, чтобы приложение заработало.
• Посетим приложение на Heroku с предоставленным URL

Мы можем сделать первый шаг, создав новое приложение Heroku, из командной строки
heroku create. Heroku создаст случайное имя для нашего приложения, в моем случае это
cryptic-oasis-�����. у вас имя будет другим.

Command Line

(pages) $ heroku create

Creating app... done, cryptic-oasis-

https://cryptic-oasis- .herokuapp.com/ | https://git.heroku.com/cryptic-oasi\

s- .git

Теперь нам нужно добавить “hook” для Heroku в git. Это означает, что git сохранит обе
наши настройки для отправки кода на Bitbucket и Heroku. Мое Приложение Heroku
называется cryptic-oasis-����� поэтому моя команда выглядит следующим образом.

Группа в ВК "Django_Python"
Chapter 3: Pages app 62

Command Line

(pages) $ heroku git:remote -a cryptic-oasis-

Вы должны заменить cryptic-oasis-40349 на имя приложения которое предоставил Heroku.


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

Command Line

(pages) $ heroku config:set DISABLE_COLLECTSTATIC=

Теперь мы можем отправить наш код на Heroku. Потому что ранее мы установили наш
“hook” и он уйдет на Heroku.

Command Line

(pages) $ git push heroku master

Если мы просто наберем git push origin master, то код будет перенесен в Bitbucket, а не в
Heroku. Добавив heroku к команде пошлет код на Heroku. Это немного сбивает с толку
первое время.

Наконец, нам необходимо сделать ваше приложение Heroku рабочим. Поскольку веб-сайты
растут в трафике, им нужны дополнительные услуги Heroku, но для нашего основного
примера мы можем использовать самый низкий уровень, web=�, который также является
бесплатным!

Введите следующую команду.

Группа в ВК "Django_Python"
Chapter 3: Pages app 63

Command Line

(pages) $ heroku ps:scale web=

Мы закончили! Последний шаг, это подтвердить что наше приложение работает в


интернете. Если вы используете команду heroku open веб-браузер откроет новую вкладку с
URL вашего приложения:

Command Line

(pages) $ heroku open

У меня это https://cryptic-oasis-40349.herokuapp.com/about/. Вы сможете увидеть


страницу homepage и страницу about page .

Страница Homepage на Heroku

Страница About page на Heroku

Группа в ВК "Django_Python"
Chapter 3: Pages app 64

Вывод

Поздравляем с созданием и развертыванием второго проекта Django! На этот раз мы


использовали шаблоны, классовые vews, более полно исследовали URLConfs, добавили
базовые тесты и использовали Heroku. Далее мы перейдем к нашему первому проекту с
базой данных и посмотрим, где действительно Django блистает.

Группа в ВК "Django_Python"
Глава �: Message Board app (Приложение доска объявлений)
В этой главе мы будем использовать базу данных в первый раз, чтобы построить базовое
приложение доска объявлений, где пользователи могут отправлять и читать короткие
сообщения. Мы рассмотрим мощный встроенный интерфейс администратора Django,
который обеспечивает визуальный способ внесения изменений в наши данные. И после
добавления тестов мы будем отправлять наш код в Bitbucket и развернем приложение на
Heroku.

Django предоставляет встроенную поддержку для нескольких типов баз данных. Имея
всего несколько строк в нашем файле settings.py, он может поддерживать PostgreSQL,
MySQL, Oracle или SQLite. Но самым простым на сегодняшний день-является SQLite,
потому что он работает с одним файлом и не требует сложной установки. В отличие от
других, опции которых требуют, чтобы процессы работали в фоновом режиме и могут быть
довольно сложными для настройки. Django использует SQLite по умолчанию и это
идеальный выбор для небольших проектов.

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

• создать новый каталог для нашего кода на рабочем столе под названием mb
• установить Django в новой виртуальной среде
• создать новый проект с именем mb_project
• создать новое приложение posts
• обновить settings.py

Группа в ВК "Django_Python"
Chapter 4: Message Board app 66

В новой консоли командной строки введите следующие команды. Обратите внимание, что
я использую здесь (mb) для представления имени виртуальной среды, хотя на самом деле
(mb-XXX), где XXX случайные символы.

Command Line

$ cd ~/Desktop

$ mkdir mb

$ cd mb

$ pipenv install django

$ pipenv shell

(mb) $ django-admin startproject mb_project .

(mb) $ python manage.py startapp posts

Укажите Django о новом приложении posts, добавив его в нижнюю часть раздела
INSTALLED_APPS вашего settings.py файла открыв его в текстовом редакторе.

Code

# mb_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'posts', # new

Затем выполните команду migrate, чтобы создать исходную базу данных на основе настроек
Django по умолчанию.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 67

Command Line

(mb) $ python manage.py migrate

Если вы посмотрите в наш каталог с помощью команды ls, вы увидите, что у нас появился
файл db.sqlite� представляющий нашу базу данных SQLite.

Command Line

(mb) $ ls

db.sqlite mb_project manage.py

Отступление: Технически файл db.sqlite� создается при первом запуске migrate или
runserver. Использование runserver формирует базу данных, используя настройки Django
по умолчанию, однако migrate будет синхронизировать базу данных с текущим состоянием
любых моделей баз данных, находящихся в проекте и внесенных в INSTALLED_APPS.
Другими словами, чтобы убедиться, что база данных отражает текущее состояние проекта,
необходимо использовать migrate (а также makemigrations) при каждом обновлении
модели. Более подробно об этом чуть позже.

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

Command Line

(mb) $ python manage.py runserver

И перейдите к http://127.0.0.1:8000/ что бы увидеть хорошо знакомую страницу, Django


установлен правильно.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 68

Django welcome page

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


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

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


руководство, которое вы можете найти здесь, если это новое для вас.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 69

Откройте файл posts/models.py и посмотрите на код по умолчанию, который


предоставляет Django:
Code

# posts/models.py

from django.db import models

# Create your models here

Django импортирует модуль models, чтобы помочь нам построить новые модели баз
данных, которые будут “моделировать” характеристики данных в нашей базе данных. Мы
хотим создать модель для хранения текстового содержимого сообщений в доске
объявлений, которую мы можем выполнить следующим образом:

Code

# posts/models.py

from django.db import models

class Post(models.Model):

text = models.TextField()

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

Активация моделей

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

Группа в ВК "Django_Python"
Chapter 4: Message Board app 70

1. Сначала мы создаем файл миграции с помощью команды makemigrations, которая


генерирует команды SQL для предустановленных приложений в нашем параметре
INSTALLED_APPS. Файлы миграции не выполняют эти команды в файле базы
данных, а являются ссылкой на все новые изменения в наших моделях. Этот подход
означает, что у нас есть запись изменений в наших моделях .
2. Во-вторых, мы создаем фактическую базу данных с помощью migrate, которая
выполняет инструкции в нашем файле migrations.

Command Line

(mb) $ python manage.py makemigrations posts

(mb) $ python manage.py migrate posts

Обратите внимание, что вам не нужно включать имя после makemigrations или migrate.
Если вы просто запустите команды, то они будут применены ко всем доступным
изменениям. Это хорошая привычка, чтобы быть точным. Если бы у нас было два
отдельных приложения в нашем проекте, и мы обновили модели в обоих, а затем запустили
makemigrations, это создало бы файл миграции, содержащий данные об обоих изменениях.
Это усложняет отладку в будущем. Вам потребуется, чтобы каждый файл миграции был как
можно меньше и возможно отдельным. Таким образом, если вам нужно просмотреть
прошлые миграции, есть только одно изменение на миграцию, а не одно, которое
применяется к нескольким приложениям.

Django Admin

Django предоставляет нам мощный интерфейс администратора для взаимодействия с


нашей базой данных. Это поистине потрясающий функционал, который предлагают
немногие веб-фреймворки. Он имеет свои маршруты как в Django проекте
newspaper(газета - в этой книге ниже). Разработчики хотели CMS (систему управления
контентом), чтобы журналисты могли писать и редактировать свои истории без
необходимости затрагивать "код.” Пстепенно встроенное приложение администратора
превратилось в фантастический, из коробки инструмент для управления всеми аспектами
Группа в ВК "Django_Python"
проекта Django.
Chapter 4: Message Board app 71

Чтобы использовать Django админ, нам сначала нужно создать суперпользователя,


который может войти в систему. В консоли командной строки введите python manage.py
createsuperuser и заполните ответы на запросы для имени пользователя, электронной
почты и пароля:

Command Line

(mb) $ python manage.py createsuperuser

Username (leave blank to use 'wsv'): wsv

Email:

Password:

Password (again):

Superuser created successfully.

Примечание: При вводе пароля он не отображается в консоли командной строки по


соображениям безопасности.

Перезапустите сервер Django с помощью python manage.py runserver и в браузере перейдите


к http://127.0.0.1:8000/admin/. Вы должны увидеть экран входа администратора:

Группа в ВК "Django_Python"
Chapter 4: Message Board app 72

Admin login page

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

Группа в ВК "Django_Python"
Chapter 4: Message Board app 73

Admin homepage

Но где наше приложение posts ? Оно не отображается на главной странице


администратора!
Нам необходимо явно указать Django, что отображать в админке. К счастью, мы можем
легко изменять это, открыв файл posts/admin.py и отредактировать его следующим
образом:

Группа в ВК "Django_Python"
Chapter 4: Message Board app 74

Code

# posts/admin.py

from django.contrib import admin

from .models import Post

admin.site.register(Post)

Django теперь знает, что он должен отображать наше posts приложение и его модель базы
данных Post на странице администратора. Если вы обновите свой браузер, вы увидите, что
теперь оно появилось:

Admin homepage updated

Теперь давайте создадим наш первый пост в доске объявлений для нашей базы данных.
Нажмите на кнопку + Add напротив Posts. Введите свой текст в текстовое поле.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 75

Admin new entry

Затем нажмите кнопку "Save", которая перенаправит вас на главную страницу Post. Однако,
если вы посмотрите внимательно, то есть проблема: Наша новая запись называется “Post
object”, что не очень полезно.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 76

Admin new entry

Давайте изменим эту ситуацию. В файле posts/models.py добавьте новую функцию __str__
как в примере:

Code

# posts/models.py

from django.db import models

class Post(models.Model):

text = models.TextField()

def __str__(self):
"""Строковое отображение модели """

return self.text[:��]

Группа в ВК "Django_Python"
Chapter 4: Message Board app 77

Если вы обновите страницу администратора в браузере, вы увидите, что она изменена на


гораздо более наглядное и полезное отображение нашей записи базы данных.

Admin new entry

Гораздо лучше! Рекомендуется добавлять методы str () ко всем моделям, чтобы улучшить
их читаемость.

Views/Templates/URLs
Для того, чтобы отобразить содержимое нашей базы данных на нашей домашней странице,
мы должны подключить наши views, templates и URLConfs. Сейчас этот образец должен
начать казаться знакомым.

Начнем с view. Ранее в книге мы использовали встроенный универсальный Template-


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

В posts/views.py файле введите код Python ниже:

Группа в ВК "Django_Python"
Chapter 4: Message Board app 78

Code

# posts/views.py

from django.views.generic import ListView

from .models import Post

class HomePageView(ListView):

model = Post

template_name = 'home.html'

В первой строке мы импортируем ListView, а во второй строке нам нужно явно определить,
какую модель мы используем. В view мы создаем подкласс ListView, указываем название
модели и указываем ссылку на шаблон. Внутри ListView возвращает объект с именем
object_list, который мы хотим отобразить в нашем шаблоне (template).

Наш view готов, что означает, что нам все еще нужно настроить наши URL-адреса и сделать
наш шаблон(template). Начнем с шаблона. Создадим каталог(папку) на уровне проекта с
именем templates и файл шаблона home.html.

Command Line

(mb) $ mkdir templates

(mb) $ touch templates/home.html

Затем обновите поле DIRS в settings.py файле, чтобы Django знал, что нужно искать в этой
папке шаблонов.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 79

Code

# settings.py

TEMPLATES = [

...

'DIRS': [os.path.join(BASE_DIR, 'templates')],

...

},

В нашем файле шаблона home.html мы можем использовать Django Templating


Language’s(Язык шаблонов Django) для цикла, чтобы перечислить все объекты в
object_list. Помните, что object_list-это то, что ListView возвращает нам.

Code

<!-- templates/home.html -->

<h1>Message board homepage</h1>

<ul>

{% for post in object_list %}

<li>{{ post }}</li>

{% endfor %}

</ul>

Последний шаг заключается в том, чтобы настроить наш URLConfs. Давайте начнем с
файла urls.py на уровне проекта где мы просто подключаем include нашего posts и
добавляем include во вторую строку.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 80

Code

# mb_project/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('', include('posts.urls')),

Затем создайте файл urls.py на уровне приложения.

Command Line

(mb) $ touch posts/urls.py

И обновите его таким образом:

Code

# posts/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.HomePageView.as_view(), name='home'),

Перезапустите сервер с python manage.py runserver и перейдите на наш сайт


http://127.0.0.1:8000/ в котором теперь перечислены наши сообщения на доске
объявлений.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 81

Homepage with posts

В основном мы закончили, но давайте создадим еще несколько сообщений на доске


объявлений в администраторе Django, чтобы убедиться, что они будут отображаться
правильно на главной странице.

Добавление новых записей

Чтобы добавить новые сообщения в нашу доску объявлений, вернитесь в Admin:

http://127.0.0.1:8000/admin/

И создайте еще два поста. Вот как выглядит мой:

Группа в ВК "Django_Python"
Chapter 4: Message Board app 82

Admin entry

Группа в ВК "Django_Python"
Chapter 4: Message Board app 83

Admin entry

Группа в ВК "Django_Python"
Chapter 4: Message Board app 84

Updated admin entries section

Если вы вернетесь на домашнюю страницу, вы увидите, что она автоматически отображает


наши отформатированные сообщения. Ура!

Homepage with three entries

Все работает так, что самое время инициализировать наш каталог, добавить новый код и

Группа в ВК "Django_Python"
Chapter 4: Message Board app 85

и включить наш первый git commit.

Command Line

(mb) $ git init

(mb) $ git add -A

(mb) $ git commit -m 'initial commit'

Tests
Раньше мы тестировали только статические страницы, поэтому мы использовали
SimpleTestCase(простой тест). Но теперь, когда наша домашняя страница работает с базой
данных, нам нужно использовать TestCase, которая позволит нам создать «тестовую» базу
данных, которую мы можем проверить. Другими словами, нам не нужно запускать тесты в
нашей реальной базе данных, но вместо этого можно создать отдельную тестовую базу
данных, заполнить ее образцами данных, а затем протестировать ее.
Начнем с добавления образца записи в текстовое поле базы данных, а затем проверим,
правильно ли она хранится в базе данных. Важно, чтобы все наши методы тестирования
начинались с test_, чтобы Django знал что тестировать! Код будет выглядеть следующим
образом:

Code

# posts/tests.py

from django.test import TestCase

from .models import Post

class PostModelTest(TestCase):

def setUp(self):

Post.objects.create(text='just a test')

Группа в ВК "Django_Python"
Chapter 4: Message Board app 86

def test_text_content(self):

post=Post.objects.get(id= )

expected_object_name = f'{post.text}'

self.assertEqual(expected_object_name, 'just a test')

В верхней части мы импортируем модуль TestCase, который позволяет нам создать образец
базы данных, а затем импортировать нашу Post модель. Мы создаем новый класс
PostModelTest и добавляем метод setUp для создания новой базы данных, которая имеет
только одну запись: сообщение с текстовым полем, содержащим строку 'just a test'.

Затем мы запускаем наш первый тест, test_text_content, чтобы проверить, что поле базы
данных на самом деле содержит только тест. Мы создаем переменную post, которая
представляет первый идентификатор (id) в нашей модели Post. Запомните, что Django
автоматически устанавливает этот id для нас. Если бы мы создали еще одну запись, у нее
был бы идентификатор 2, следующий был бы 3 и так далее.
Следующая строка использует f strings которая является очень классным дополнением к
Python 3.6. Она позволяет помещать переменные непосредственно в строки, если
переменные окружены фигурными скобками {}. Здесь мы устанавливаем
expected_object_name как строку значения в post.text, который должен быть просто тест.

В последней строке мы используем assertEqual, чтобы проверить, что наша недавно


созданная запись действительно соответствует тому, что мы вводим наверху. Продолжайте
и запустите тест в командной строке python manage.py test.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 87

Command Line

(mb) $ python manage.py test

Creating test database for alias 'default'...

System check identified no issues ( silenced).

----------------------------------------------------------------------

Ran test in . s

OK

Destroying test database for alias 'default'...

Прошло!

Не волнуйтесь, если предыдущее объяснение было похоже на информационную


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

Время для нашего второго теста. Первый тест был для модели, но теперь мы хотим
проверить нашу единственную страницу: домашнюю страницу (homepage). В частности, мы
хотим проверить, что она существует (throws an HTTP 200 response), использует view home
и использует home.html шаблон(template).
Нам нужно добавить еще один импорт наверху для reverse и совершенно новый класс
HomePageViewTest для нашего теста.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 88

Code

from django.test import TestCase

from django.urls import reverse

from .models import Post

class PostModelTest(TestCase):

def setUp(self):

Post.objects.create(text='just a test')

def test_text_content(self):

post=Post.objects.get(id= )

expected_object_name = f'{post.text}'

self.assertEqual(expected_object_name, 'just a test')

class HomePageViewTest(TestCase):

def setUp(self):

Post.objects.create(text='this is another test')

def test_view_url_exists_at_proper_location(self):

resp = self.client.get('/')

self.assertEqual(resp.status_code, )

def test_view_url_by_name(self):

resp = self.client.get(reverse('home'))

self.assertEqual(resp.status_code, )

Группа в ВК "Django_Python"
Chapter 4: Message Board app 89

def test_view_uses_correct_template(self):

resp = self.client.get(reverse('home'))

self.assertEqual(resp.status_code, )

self.assertTemplateUsed(resp, 'home.html')

Если вы запустите наши тесты снова, вы должны увидеть, что они проходят.

Command Line

(mb) $ python manage.py test

Creating test database for alias 'default'...

System check identified no issues ( silenced).

----------------------------------------------------------------------

Ran tests in . s

OK

Destroying test database for alias 'default'...

Почему там написано четыре теста? Запомните, что наш setUp метод на самом деле не тест,
он просто позволяет нам запускать последующие тесты. Наши четыре реальных теста это
test_test_content, test_view_url_exists_at_proper_location, test_view_url_by_name, и
test_view_-uses_correct_template.
Любая функция, имеющая в начале слово test* в существующем файле tests.py будет
запущена при выполнении команды python manage.py test.

Мы закончили добавление кода для нашего тестирования, пришло время внести изменения
в git.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 90

Command Line

(mb) $ git add -A

(mb) $ git commit -m 'added tests'

Bitbucket

Нам также нужно сохранить наш код на Bitbucket. Это хорошая привычка, чтобы
сохранить код в случае, если что-нибудь случится с вашим локальным компьютером, и это
также позволяет вам делиться и сотрудничать с другими разработчиками.

У Вас уже должна быть учетная запись Bitbucket из главы 3, поэтому создайте новый
репозиторий, который мы назовем mb-app.

Bitbucket create app

На следующей странице нажмите на нижнюю ссылку “I have an existing project”.


Скопируйте две команды для подключения, а затем нажмите repository to Bitbucket.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 91

Это должно выглядеть так, замените wsvincent (мое имя пользователя)на ваш логин в
системе bitbucket :

Command Line

(mb) $ git remote add origin git@bitbucket.org:wsvincent/mb-app.git

(mb) $ git push -u origin master

Heroku configuration
Вы также должны уже иметь настройки учетной записи Heroku установленные из главы 3.
Нам необходимо внести следующие изменения в наш проект доски объявлений, чтобы
развернуть его в интернете:

• обновить Pipfile.lock
• новый Procfile
• установить gunicorn
• обновить settings.py

В вашем Pipfile укажите версию Python мы используем �.�. Добавьте эти две строки в
конец файла.

Code

# Pipfile

[requires]

python_version = " . "

Запустите pipenv lock для создания соответствующего Pipfile.lock.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 92

Command Line

(mb) $ pipenv lock

Затем создайте Procfile, который указывает Heroku, как запустить удаленный сервер, где
наш код будет работать.

Command Line

(mb) $ touch Procfile

Теперь мы укажем Heroku использовать gunicorn в качестве нашего рабочего сервера и


посмотреть в наш mb_project.wsgi файл для получения дальнейших инструкций.

Command Line

web: gunicorn mb_project.wsgi --log-file -

Далее установим gunicorn который мы будем использовать для production но при этом
используя внутренний сервер Django для локальной разработки.

Command Line

(mb) $ pipenv install gunicorn

Наконец, обновите ALLOWED_HOSTS в нашем файле settings.py

file. Code
# mb_project/settings.py

ALLOWED_HOSTS = ['*']

Все мы закончили! Добавьте и зафиксируйте наши новые изменения в git, а затем


отправьте их в Bitbucket.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 93

Command Line

(mb) $ git status

(mb) $ git add -A

(mb) $ git commit -m 'New updates for Heroku deployment'

(mb) $ git push -u origin master

Heroku развертывание

Убедитесь, что вы вошли в свою учетную запись Heroku.

Command Line

(mb) $ heroku login

Затем запустите команду heroku create и Heroku случайным образом сгенерирует для вас
имя приложения. Вы сможете настроить это позже, если будет необходимо.

Command Line

(mb) $ heroku create

Creating app... done, agile-inlet-

https://agile-inlet- .herokuapp.com/ | https://git.heroku.com/agile-inlet- \

.git

Настройте git на использование имени вашего нового приложения, когда вы отправляете


код на Heroku. Мое сгенерированное имя Heroku это agile-inlet-����� поэтому
команда выглядит следующим образом.

Группа в ВК "Django_Python"
Chapter 4: Message Board app 94

Command Line

(mb) $ heroku git:remote -a agile-inlet-

Укажите Heroku игнорировать статические файлы, которые мы подробно рассмотрим при


развертывании нашего приложения блога в книге позже.

Command Line

(mb) $ heroku config:set DISABLE_COLLECTSTATIC=

Отправьте код в Heroku и добавьте бесплатное масштабирование, чтобы он действительно


работал в интернете, иначе код просто сидит там.

Command Line

(mb) $ git push heroku master

(mb) $ heroku ps:scale web=

Если вы откроете новый проект heroku open, он автоматически запустит новое окно
браузера с URL вашего приложения. Мой находится по адресу:

https://agile-inlet-25811.herokuapp.com/.

Live site

Группа в ВК "Django_Python"
Chapter 4: Message Board app 95

Вывод

На данный момент мы создали, протестировали и развернули наше первое приложение на


основе базы данных. Хотя это намеренно довольно просто, теперь мы знаем, как создать
модель базы данных, обновить ее с помощью панели администратора, а затем отобразить
содержимое на веб-странице. Но чего-то не хватает, не так ли?
В реальном мире пользователям нужны формы для взаимодействия с нашим сайтом. Ведь
не у всех должен быть доступ к админ панели. В следующей главе мы создадим приложение
блога, которое использует формы, чтобы пользователи могли создавать, редактировать и
удалять записи. Мы также добавим стили CSS.

Группа в ВК "Django_Python"
ϏҵҪҬҪ�:Blogapp ҹҺҲҵҸҰүҷҲүύҵҸҭ
ώ ӇҼҸҳ ҭҵҪҬү ҶӅ ҹҸһҼҺҸҲҶ ҹҺҲҵҸҰүҷҲү  ҴҸҼҸҺҸү ҹҸұҬҸҵӉүҼ ҹҸҵӆұҸҬҪҼүҵӉҶ һҸұҮҪҬҪҼӆ 
ҺүҮҪҴҼҲҺҸҬҪҼӆ Ҳ ҽҮҪҵӉҼӆ һҸҸҫӃүҷҲӉ ϙҪ ҭҵҪҬҷҸҳ һҼҺҪҷҲӀү ҫҽҮҽҼ ҹүҺүӁҲһҵүҷӅ Ҭһү
һҸҸҫӃүҷҲӉ Ҭ ҫҵҸҭү  Ҳ ҮҵӉ ҴҪҰҮҸҭҸ ҸҼҮүҵӆҷҸҭҸ һҸҸҫӃүҷҲӉ ҫҽҮүҼ ҬӅҮүҵүҷҪ ҸҼҮүҵӆҷҪӉ
һҼҺҪҷҲӀҪϘӅҼҪҴҰүҹҸұҷҪҴҸҶҲҶһӉһ$44ҮҵӉһҼҲҵҲұҪӀҲҲҲҽұҷҪүҶ ҴҪҴ%KBOHPҺҪҫҸҼҪүҼһҸ
һҼҪҼҲӁүһҴҲҶҲҾҪҳҵҪҶҲ

ϙҪӁҪҵӆҷҪӉҷҪһҼҺҸҳҴҪ
Как описано в предыдущих главах, наши шаги для создания нового проекта Django будут
выглядеть следующим образом:

• Создать новую папку для нашего проекта на рабочем столе с именем blog

• Установить Django в новой виртуальной среде


• Создать новый проект Django с именем blog_project
• Создать новое приложение blog

• Выполнить миграцию для установки базы данных


• Обновить settings.py

Выполните следующие команды в новой консоли командной строки. Обратите внимание,


что фактическое имя виртуальной среды будет (blog-XXX), где XXX представляет
случайные символы. Я использую здесь (blog) , чтобы было проще, так как мое имя будет
отличаться от вашего.

И не забудьте указать пробел с точкой ". " в конце команды для создания нашего нового
blog_project. (прм. переводчика. Создает проект Django в существующей директории без
создания новой папки, не обязательно)

Группа в ВК "Django_Python"
Chapter 5: Blog app 97

Command Line

$ cd ~/Desktop

$ mkdir blog

$ cd blog

$ pipenv install django

$ pipenv shell

(blog) $ django-admin startproject blog_project .

(blog) $ python manage.py startapp blog

(blog) $ python manage.py migrate

(blog) $ python manage.py runserver

Чтобы убедиться, что Django знает о нашем новом приложении, откройте текстовый
редактор и добавьте новое приложение в INSTALLED_APPS в нашем settings.py файле:

Code

# blog_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'blog', # new

Если вы перейдете по http://127.0.0.1:8000/ должны увидеть следующую страницу.

Группа в ВК "Django_Python"
Chapter 5: Blog app 98

Django welcome page

Хорошо, первичная настройка закончена! Далее мы создадим модель базы данных для
записей блога.

Database Models(модели базы данных)


Каковы характеристики типичного приложения для блога? В нашем случае
давайте придерживаться простоты и предположим, что у каждого сообщения есть
название, автор и тело. Мы можем превратить это в модель базы данных, откроем
файл blog/models.py и введем показанный ниже код :

Группа в ВК "Django_Python"
Chapter 5: Blog app 99

Code

# blog/models.py

from django.db import models

class Post(models.Model):

title = models.CharField(max_length= )

author = models.ForeignKey(

'auth.User',

on_delete=models.CASCADE,

body = models.TextField()

def __str__(self):

return self.title

В верхней части мы импортируем класс models а затем создаем подкласс models.Model с


именем Post. Используя функционал подкласса, мы автоматически имеем доступ ко всему
в django.db.models.Models и можем добавлять дополнительные поля и методы по
своему желанию.

Для title мы ограничиваем длину до 200 символов, а для body мы используем текстовое
поле, которое будет автоматически расширяться по мере необходимости, чтобы
соответствовать тексту пользователя. В Django доступно много типов полей; вы можете
увидеть полный список здесь.

Для поля author мы используем ForeignKey это предоставляет связь многие-к-одному .


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

Группа в ВК "Django_Python"
Chapter 5: Blog app 100

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

Command Line

(blog) $ python manage.py makemigrations blog

(blog) $ python manage.py migrate blog

Наша база настроена! Что дальше?

Admin

Нам нужен способ доступа к нашим данным. Войдем в Django админ! Но сначала создадим
учетную запись суперпользователя, введя приведенную ниже команду и после отвечая на
запросы введем адрес электронной почты и пароль. Обратите внимание, что при вводе
пароля он не будет отображаться на экране по соображениям безопасности.

Command Line

(blog) $ python manage.py createsuperuser

Username (leave blank to use 'wsv'): wsv

Email:

Password:

Password (again):

Superuser created successfully.

Теперь снова запустите сервер Django с помощью команды python manage.py runserver и
откройте Django админ http://127.0.0.1:8000/admin/. Войдите с новой учетной
записью суперпользователя.

Упс! А где наша новая Post модель?

Группа в ВК "Django_Python"
Chapter 5: Blog app 101

Admin homepage

Мы забыли обновить blog/admin.py так что давайте сейчас сделаем это.

Code

# blog/admin.py

from django.contrib import admin

from .models import Post

admin.site.register(Post)

Если вы обновите страницу, вы увидите это обновление.

Группа в ВК "Django_Python"
Chapter 5: Blog app 102

Admin homepage

Давайте добавим два сообщения в блог, чтобы у нас были примеры данных для работы.
Нажмите кнопку + Add рядом с Posts чтобы создать новую запись. Не забудьте добавить
“author” к каждому сообщению, так как по умолчанию все поля модели обязательны для
заполнения. Если вы попытаетесь ввести сообщение без автора вы увидите ошибку. Если
мы захотим изменить это, мы можем добавить field options(параметры поля) в нашу
модель, чтобы сделать это поле необязательным или заполнить его значением по
умолчанию

Группа в ВК "Django_Python"
Chapter 5: Blog app 103

Первый пост

Группа в ВК "Django_Python"
Chapter 5: Blog app 104

Второй пост

Группа в ВК "Django_Python"
Chapter 5: Blog app 105

Админка с двумя постами

Теперь, когда наша модель базы данных завершена, нам нужно создать необходимые view,
URL-адреса и шаблоны, чтобы мы могли отображать информацию в нашем веб-
приложении.

URLs

Мы хотим, чтобы сообщения в блоге отображались на главной странице, поэтому, как и в


предыдущих главах, мы сначала настроим URLConfs уровня проекта, а затем URLConfs
уровня приложения, что бы достичь этого. Обратите внимание на то, что “уровень
проекта” означает в той же родительской папке где папка blog_project и папка приложения
blog.

В командной строке остановите существующий сервер с помощью Control-c и создайте


новый urls.py файл в вашем blog:

Группа в ВК "Django_Python"
Chapter 5: Blog app 106

Command Line

(blog) $ touch blog/urls.py

Теперь обновите его с помощью кода ниже.

Code

# blog/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.BlogListView.as_view(), name='home'),

Мы импортируем views (которые скоро создадим) в верхней части. Пустая строка ''
говорит Python что соответствует всем значениям, далее мы делаем URL именованным
home, к которому мы можем обратиться в наших views позже. Хотя это не обязательно но

добавление имени к URL это хорошая практика которую необходимо принять так как это
помогает сохранять организованность когда количество ваших URL растет.

Также мы должны обновить файл urls.py на уровне проекта, чтобы он мог пересылать все
запросы непосредственно на blog приложение.

Группа в ВК "Django_Python"
Chapter 5: Blog app 107

Code

# blog_project/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('', include('blog.urls')),

Мы добавили include во второй строке и url образец использовав пустую строку


регулярного выражения '' указывающее, что URL запросы должны быть перенаправлены
на URL-адреса блога(blog) для дальнейших инструкций.

Views

Мы собираемся использовать views на основе классов, но если вы хотите увидеть


функциональный способ создания приложения для блога, я настоятельно рекомендую
Django Girls Tutorial. Это отличный учебник.
В нашем файле views добавьте код ниже, чтобы отобразить содержимое нашей модели Post
с помощью ListView.

Группа в ВК "Django_Python"
Chapter 5: Blog app 108

Code

# blog/views.py

from django.views.generic import ListView

from . models import Post

class BlogListView(ListView):

model = Post

template_name = 'home.html'

ώҮҬҽҿҬүҺҿҷҲҿһҼҺҸҴҪҿҶӅҲҶҹҸҺҼҲҺҽүҶ-JTU7JFXҲҷҪӂҽҶҸҮүҵӆҫҪұӅҮҪҷҷӅҿ1PTUϓҪҼүҶ
ҶӅһҸұҮҪүҶҹҸҮҴҵҪһһ-JTU7JFXҲҮҸҫҪҬҵӉүҶһһӅҵҴҲҷҪҷҪӂҽҶҸҮүҵӆҲӂҪҫҵҸҷҧҼҸӇҴҸҷҸҶҲҼ
ҷҪҶҶҷҸҭҸҴҸҮҪҹҸһҺҪҬҷүҷҲӈһҺүҪҵҲұҪӀҲүҳҬһүҭҸӇҼҸҭҸһҷҽҵӉ

Templates ӂҪҫҵҸҷӅ
ϞүҹүҺӆ һ ҷҪӂҲҶ 63-$POGT Ҳ WJFXT ұҪҴҸҷӁҲҵҲ  ҷҪҶ ҷү ҿҬҪҼҪүҼ ҼҸҵӆҴҸ ҼҺүҼӆүҳ ӁҪһҼҲ
ҭҸҵҸҬҸҵҸҶҴҲӂҪҫҵҸҷҸҬ UFNQMBUFT ϖҪҴҶӅҽҰүузнали изҭҵҪҬы ҶӅҶҸҰүҶҷҪһҵүҮҸҬҪҼӆҸҼ
ҮҺҽҭҲҿ ӂҪҫҵҸҷҸҬ  ӁҼҸҫӅ ҮүҺҰҪҼӆ наш ҴҸҮ Ҭ ӁҲһҼҸҼү ϛҸӇҼҸҶҽ ҷҪӁҷүҶ һ ҾҪҳҵҪ CBTFIUNM Ҳ
ҾҪҳҵҪ IPNFIUNM  ҴҸҼҸҺӅҳ ҷҪһҵүҮҽүҼһӉ ҸҼ ҷүҭҸ ϛҸұҰү  ҴҸҭҮҪ ҶӅ ҮҸҫҪҬҲҶ ӂҪҫҵҸҷӅ ҮҵӉ
һҸұҮҪҷҲӉҲҺүҮҪҴҼҲҺҸҬҪҷҲӉһҸҸҫӃүҷҲҳҫҵҸҭҪ ҸҷҲҼҪҴҰүҶҸҭҽҼҷҪһҵүҮҸҬҪҼӆһӉҸҼbase.html.

Начнем с создания каталога шаблонов(templates) на уровне проекта с двумя файлами


шаблонов.

Группа в ВК "Django_Python"
Chapter 5: Blog app 109

Command Line

(blog) $ mkdir templates

(blog) $ touch templates/base.html

(blog) $ touch templates/home.html

Затем обновите settings.py чтобы Django знал, где искать наши шаблоны.

Code

# blog_project/settings.py

TEMPLATES = [

...

'DIRS': [os.path.join(BASE_DIR, 'templates')],

...

},

Теперь обновите шаблон base.html следующим образом.

Code

<!-- templates/base.html -->

<html>

<head>

<title>Django blog</title>

</head>

<body>

<header>

<h1><a href="/">Django blog</a></h1>

</header>

Группа в ВК "Django_Python"
Chapter 5: Blog app 110

<div class="container">

{% block content %}

{% endblock content %}

</div>

</body>

</html>

Обратите внимание на то, что код между {% block content %} и {% endblock content %} может
быть заполнен другими шаблонами. Говоря об этом, вот код для home.html.

Code

<!-- templates/home.html -->

{% extends 'base.html' %}

{% block content %}

{% for post in object_list %}

<div class="post-entry">

<h2><a href="">{{ post.title }}</a></h2>

<p>{{ post.body }}</p>

</div>

{% endfor %}

{% endblock content %}

В верхней части отметим, что этот шаблон расширяет(extends) base.html и обертывает


нужный вам код с помощью контент-блоков. Мы используем язык шаблонов Django
Templating Language, чтобы создать простой цикл for для каждой записи блога. Обратите
внимание, что object_list происходит из ListView и содержит все объекты в нашем view.

Если вы снова запустите сервер Django: python manage.py runserver.

Группа в ВК "Django_Python"
Chapter 5: Blog app 111

И обновите http://127.0.0.1:8000/ мы видим, что это работает.

Главная страница блога с двумя постами

Но выглядит это ужасно. Давайте это исправим!

Static files(статичные файлы)

Нам нужно добавить CSS, который называется статическим файлом, потому что, в отличие
от нашего динамического контента базы данных, он не изменяется. К счастью добавлять,
статические файлы, такие как CSS, JavaScript и изображения в наш проект Django очень
просто.

В готовом к производству Django проекте вы, как правило, храните его в сети доставки
контента (CDN) для лучшей производительности, но для наших целей хранение файлов
локально это нормально.

Сначала закройте наш локальный сервер с Control-c. Затем создайте папку на уровне
проекта с именем static.

Группа в ВК "Django_Python"
Chapter 5: Blog app 112

Command Line

(blog) $ mkdir static

Как и в нашей папке с шаблонами, нам нужно обновить settings.py, чтобы сообщить Django,
где искать эти статические файлы. Мы можем обновить settings.py с изменением строки для
STATICFILES_DIRS. Добавьте его в нижнюю часть файла под заголовком STATIC_URL.

Code

# blog_project/settings.py

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

Теперь создайте папку css в static и добавьте в нее новый файл base.css.

Command Line

(blog) $ mkdir static/css

(blog) $ touch static/css/base.css

Что мы должны поместить в ваш файл? Как насчет изменения title на красный?

Code

/* static/css/base.css */

header h1 a {

color: red;

Последний шаг. Нам нужно добавить статические файлы в ваши шаблоны, добавив {% load
staticfiles %} в начало base.html. Потому что другие наши шаблоны наследуются от base.html
мы должны добавить это только один раз. Добавьте новую строку в нижней части кода
<head>< / head>, которая явно ссылается на наш новый файл base.css.

Группа в ВК "Django_Python"
Chapter 5: Blog app 113

Code

<!-- templates/base.html -->

{% load static %}

<html>

<head>

<title>Django blog</title>

<link rel="stylesheet" href="{% static 'css/base.css' %}">

</head>

...

УФ! Это было немного больно, но это одноразовая боль. Теперь мы можем добавить
статические файлы в нашу папку static, и они автоматически появятся во всех наших
шаблонах.
Запустите сервер снова с python manage.py runserver и посмотрите на нашем обновленном
сайте http://127.0.0.1:8000/.

Домашняя страница блога с красным заголовком

Мы можем сделать немного лучше. Как насчет того, чтобы добавить пользовательский
шрифт и еще немного CSS? Поскольку эта книга не является учебником по CSS, просто
вставьте следующее между тегами <head>< / head>, чтобы добавить Source Sans Pro,
бесплатный шрифт от Google.

Группа в ВК "Django_Python"
Chapter 5: Blog app 114

Code

<!-- templates/base.html -->

{% load static %}

<html>

<head>

<title>Django blog</title>

<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro: " rel="\

stylesheet">

<link rel="stylesheet" href="{% static 'css/base.css' %}">

</head>

...

Затем обновите наш css файл, скопировав и вставив следующий код:

Code

/* static/css/base.css */

body {

font-family: 'Source Sans Pro', sans-serif;

font-size: px;

header {

border-bottom: px solid # ;

margin-bottom: rem;

display: flex;

header h1 a {

color: red;

Группа в ВК "Django_Python"
Chapter 5: Blog app 115

text-decoration: none;

.nav-left {

margin-right: auto;

.nav-right {

display: flex;

padding-top: rem;

.post-entry {

margin-bottom: rem;

.post-entry h2 {

margin: . rem ;

.post-entry h2 a,

.post-entry h2 a:visited {

color: blue;

text-decoration: none;

.post-entry p {

margin: ;

Группа в ВК "Django_Python"
Chapter 5: Blog app 116

font-weight: ;

.post-entry h2 a:hover {

color: red;

Обновить домашнюю страницу http://127.0.0.1:8000/ и вы должны увидеть следующее.

Домашняя страница блога с CSS

Отдельные страницы блога


Теперь мы можем добавить функциональность для отдельных страниц блога. Как мы
можем это сделать?Мы должны создать новое представление, URL и шаблон. Я надеюсь,
что вы заметили шаблон в разработке с Django сейчас!

Начните с представления. Для упрощения можно использовать общее представление


DetailView на основе классов. В верхней части файла добавьте DetailView в список импорта,
а затем создайте наше новое представление под названием BlogDetailView.

Группа в ВК "Django_Python"
Chapter 5: Blog app 117

view called BlogDetailView.

Code

# blog/views.py

from django.views.generic import ListView, DetailView

from . models import Post

class BlogListView(ListView):

model = Post

template_name = 'home.html'

class BlogDetailView(DetailView):

model = Post

template_name = 'post_detail.html'

В этом новом представлении мы определяем модель, которую мы используем, Post и


шаблон, с которым мы хотим ее связать, post_detail.html. По умолчанию DetailView
предоставит контекстный объект, который мы можем использовать в нашем шаблоне,
называемый либо объектом, либо строчным именем нашей модели post. Кроме того,
DetailView ожидает либо первичный ключ, либо slug, переданный ему в качестве
идентификатора. Подробнее об этом вкратце.
Теперь выйдите из локального сервера Control-c и создайте наш новый шаблон для деталей
поста следующим образом:

Группа в ВК "Django_Python"
Chapter 5: Blog app 118

Command Line

(blog) $ touch templates/post_detail.html

Затем введите следующий код:

Code

<!-- templates/post_detail.html -->

{% extends 'base.html' %}

{% block content %}

<div class="post-entry">

<h2>{{ post.title }}</h2>

<p>{{ post.body }}</p>

</div>

{% endblock content %}

В верхней части мы указываем, что этот шаблон наследуется от base.html. Затем отобразите
title и body из нашего объекта контекста, который DetailView делает доступным как post.
Лично я нашел именование объектов контекста в общих представлениях чрезвычайно
запутанным при первом изучении Django. Поскольку наш объект контекста из подробного
представления является либо вашим именем модели post, либо объектом, мы также можем
обновить наш шаблон следующим образом, и он будет работать точно так же.

Группа в ВК "Django_Python"
Chapter 5: Blog app 119

Code

<!-- templates/post_detail.html -->

{% extends 'base.html' %}

{% block content %}

<div class="post-entry">

<h2>{{ object.title }}</h2>

<p>{{ object.body }}</p>

</div>

{% endblock content %}

Если вы обнаружите, что использование post или object сбивает с толку, мы также можем
явно задать имя объекта контекста в нашем представлении. Поэтому, если бы мы хотели
назвать его anything_you_want, а затем использовать его в шаблоне, код выглядел бы
следующим образом, и он работал бы так же.

Code

# blog/views.py

...

class BlogDetailView(DetailView):

model = Post

template_name = 'post_detail.html'

context_object_name = 'anything_you_want'

Группа в ВК "Django_Python"
Chapter 5: Blog app 120

Code

<!-- templates/post_detail.html -->

{% extends 'base.html' %}

{% block content %}

<div class="post-entry">

<h2>{{ anything_you_want.title }}</h2>

<p>{{ anything_you_want.body }}</p>

</div>

{% endblock content %}

"Волшебное" именование объекта контекста-это цена, которую вы платите за простоту и


удобство использования общих представлений. Они великолепны, если вы знаете, что они
делают, но их трудно настроить, если вы хотите другого поведения.
Хорошо, что будет дальше? Как насчет добавления нового URLConf для нашего
представления, который мы можем сделать следующим образом.

Code

# blog/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.BlogListView.as_view(), name='home'),

path('post/<int:pk>/', views.BlogDetailView.as_view(), name='post_detail'),

Группа в ВК "Django_Python"
Chapter 5: Blog app 121

Все записи в блоге будут начинаться с post/. Далее идет первичный ключ для нашей записи
post, которая будет представлена в виде целого числа <int: pk>. Вероятно вы спросите что
это за первичный ключ? Django автоматически добавляет автоматически увеличивающийся
первичный ключ к нашим моделям баз данных. Таким образом, как только мы объявили
название поля, author и body в нашей модели Post, Django под капотом также добавил еще
одно поле под названием id, который является нашим первичным ключом. Мы можем
получить к нему доступ как id или pk.
Этот pk для нашего первого поста "Привет, Мир" равен 1. Для второго поста это 2. и так
далее. Поэтому, когда мы переходим на страницу отдельной записи для нашего первого
сообщения, мы можем ожидать, что его url шаблон будет post/1.
Примечание: понимание того, как первичные ключи работают с DetailView является очень
распространенной путаницей для начинающих. Стоит перечитать предыдущие два абзаца
несколько раз, если не понимаете. С практикой это станет второй натурой.
Если теперь запустить сервер с python manage.py runserver и перейти непосредственно к
http://127.0.0.1:8000/post/1/ вы увидите специальную страницу для нашего первого
поста в блоге.

Blog post one detail

Ура! Вы также можете перейти к http://127.0.0.1:8000/post/2 посмотреть вторую запись.


Чтобы сделать нашу жизнь проще, мы должны обновить ссылку на главной странице,
чтобы мы могли напрямую получить доступ к отдельным сообщениям в блоге оттуда. В
настоящее время в home.html наша ссылка пуста: <a href="">. Обновите его до
<a href="{% url 'post_detail' post.pk %}">.

Группа в ВК "Django_Python"
Chapter 5: Blog app 122

Code

<!-- templates/home.html -->

{% extends 'base.html' %}

{% block content %}

{% for post in object_list %}

<div class="post-entry">

<h2><a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a></h2>

<p>{{ post.body }}</p>

</div>

{% endfor %}

{% endblock content %}

Мы начинаем с того, что говорим нашему шаблону Django, что мы хотим ссылаться на
URLConf, используя код {%url ... %}. Какой URL? Тот, который называется post_detail,
который мы назвали BlogDetailView в нашей url-конфигурации всего минуту назад. Если
мы посмотрим на post_detail в нашем URLConf, мы увидим, что он ожидает, что будет
передан аргумент pk, представляющий первичный ключ для записи блога. К счастью,
Django уже создал и включил это поле pk в наш объект post. Мы передаем его в URLConf,
добавляя его в шаблон как post.pk.

Чтобы убедиться, что все работает, обновите главную страницу http://127.0.0.1:8000/ и


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

Тесты
Сейчас нам нужно протестировать нашу модель и представления. Мы хотим убедиться, что
модель Post работает должным образом, включая ее представление. И мы хотим
протестировать как ListView, так и DetailView.

Группа в ВК "Django_Python"
Chapter 5: Blog app 123

Вот как выглядят примеры тестов в файле blog/tests.py.

Code

# blog/tests.py

from django.contrib.auth import get_user_model

from django.test import Client, TestCase

from django.urls import reverse

from .models import Post

class BlogTests(TestCase):

def setUp(self):

self.user = get_user_model().objects.create_user(

username='testuser',

email='test@email.com',

password='secret'

self.post = Post.objects.create(

title='A good title',

body='Nice body content',

author=self.user,

def test_string_representation(self):

post = Post(title='A sample title')

self.assertEqual(str(post), post.title)

Группа в ВК "Django_Python"
Chapter 5: Blog app 124

def test_post_content(self):

self.assertEqual(f'{self.post.title}', 'A good title')

self.assertEqual(f'{self.post.author}', 'testuser')

self.assertEqual(f'{self.post.body}', 'Nice body content')

def test_post_list_view(self):

response = self.client.get(reverse('home'))

self.assertEqual(response.status_code, )

self.assertContains(response, 'Nice body content')

self.assertTemplateUsed(response, 'home.html')

def test_post_detail_view(self):

response = self.client.get('/post/ /')

no_response = self.client.get('/post/ /')

self.assertEqual(response.status_code, )

self.assertEqual(no_response.status_code, )

self.assertContains(response, 'A good title')

self.assertTemplateUsed(response, 'post_detail.html')

В этих тестах много нового, поэтому мы будем проходить их медленно. Вверху


импортируем get_user_model для ссылки на активного пользователя. Мы импортируем
TestCase, который мы видели раньше, а также Client (), который является новым и
используется в качестве фиктивного веб-браузера для имитации GET и POST запросов на
URL. Другими словами, всякий раз, когда вы тестируете представления, вы должны
использовать Client().
В нашем методе setUp мы добавляем образец поста в блоге для тестирования, а затем
подтверждаем, что его строковое представление и содержимое верны. Затем мы
используем test_post_list_view, чтобы подтвердить, что наш сайт возвращает код статуса
HTTP 200, содержит наш текст, и использует правильный шаблон home.html.

Группа в ВК "Django_Python"
Chapter 5: Blog app 125
Наконец test_post_detail_view проверяет, что наша страница работает должным образом и
что неправильная страница возвращает 404. Всегда хорошо проверить, что что-то
существует, и что что-то неправильное не существует в ваших тестах.
Продолжайте и выполните эти тесты сейчас. Они все должны быть пройдены

Command Line

(testy) $ python manage.py test

Git

Сейчас самое время для нашего первого git commit. Сначала инициализируйте
наш каталог.Command Line

(testy) $ git init

Затем просмотрите все содержимое, которое мы добавили, проверив состояние. Добавьте


все новые файлы. И сделайте наш первый commit.

Command Line

(testy) $ git status

(testy) $ git add -A

(testy) $ git commit -m 'initial commit'

Conclusion
Мы создаем простое приложение блог с нуля! С помощью администратора Django мы
можем создавать, редактировать или удалять контент. И мы использовали DetailView
впервые создав детальное отдельное представление каждого поста блога.

Группа в ВК "Django_Python"
Chapter 5: Blog app 126

В следующем разделе Глава 6: Приложение блога с формами мы добавим формы, чтобы


нам вообще не пришлось использовать администратора Django для этих изменений.

Группа в ВК "Django_Python"
Глава 6: Формы
В этой главе мы продолжим работу над нашим приложением блога из главы 5, добавив
формы, чтобы пользователь мог создавать, редактировать или удалять любую из своих
записей блога.

Формы
Формы очень распространены и очень сложны для правильной реализации. Каждый раз,
когда вы принимаете пользовательский ввод, возникают проблемы безопасности (XSS
Атаки), требуется правильная обработка ошибок, а также вопросы пользовательского
интерфейса о том, как предупредить пользователя о проблемах с формой. Не говоря уже о
необходимости перенаправления при успехе.
К счастью для нас, встроенные формы Django абстрагируют большую часть сложности и
предоставляют богатый набор инструментов для обработки распространенных случаев
использования, работающих с формами.
Чтобы начать, обновите наш базовый шаблон, чтобы отобразить ссылку на страницу для
ввода новых сообщений в блоге. Он примет фрму <a href= "{% url 'post_new'%} " ></a>, где
post_new-это имя нашего URL.

Обновленный файл должен выглядеть следующим образом:

Группа в ВК "Django_Python"
Chapter 6: Forms 128

Code

<!-- templates/base.html -->

{% load staticfiles %}

<html>

<head>

<title>Django blog</title>

<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro: " rel\

="stylesheet">

<link rel="stylesheet" href="{% static 'css/base.css' %}">

</head>

<body>

<div class="container">

<header>

<div class="nav-left">

<h1><a href="/">Django blog</a></h1>

</div>

<div class="nav-right">

<a href="{% url 'post_new' %}">+ New Blog Post</a>

</div>

</header>

{% block content %}

{% endblock content %}

</div>

</body>

</html>

Давайте добавим новый URLConf для post_new:

Группа в ВК "Django_Python"
Chapter 6: Forms 129

Code

# blog/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.BlogListView.as_view(), name='home'),

path('post/<int:pk>/', views.BlogDetailView.as_view(), name='post_detail'),

path('post/new/', views.BlogCreateView.as_view(), name='post_new'),

Ваш url будет начинаться с post / new/, представление называется Blog Create View, и url
будет называться post_new. Просто, правда?
Теперь давайте создадим наше представление, импортировав новый универсальный класс
под названием Create View, а затем создадим его подкласс для создания нового
представления с именем BlogCreateView.

Code

# blog/views.py

from django.views.generic import ListView, DetailView

from django.views.generic.edit import CreateView

from . models import Post

class BlogListView(ListView):

model = Post

template_name = 'home.html'

Группа в ВК "Django_Python"
Chapter 6: Forms 130

class BlogDetailView(DetailView):

model = Post

template_name = 'post_detail.html'

class BlogCreateView(CreateView):

model = Post

template_name = 'post_new.html'

fields = '__all__'

В BlogCreateView мы указываем нашу модель базы данных Post, имя нашего шаблона
post_new.html и все поля с '__all__', так как у нас только два: title и author. Последний шаг -
создать наш шаблон, который мы назовем post_new.html.

Command Line

(blog) $ touch templates/post_new.html

А затем добавьте следующий код:

Группа в ВК "Django_Python"
Chapter 6: Forms 131

Code

<!-- templates/post_new.html -->

{% extends 'base.html' %}

{% block content %}

<h1>New post</h1>

<form action="" method="post">{% csrf_token %}

{{ form.as_p }}

<input type="submit" value="Save" />

</form>

{% endblock %}

Давайте разберемся с тем, что мы сделали:

• В верхней строке мы наследуем от нашего базового шаблона.


• Используются HTML-теги <form> с методом POST, так как мы отправляем данные.
Если бы мы получали данные из формы, например, в окне поиска, мы бы
использовали GET.
• Добавлен {% csrf_token %}, который предоставляется Django для защиты нашей
формы от атак межсайтового скриптинга. Вы должны использовать его для всех
ваших форм в Django.
• Затем для вывода данных формы используется {{ form.as_p }}, который отображает
его в тегах параграфа <p>.

• Наконец, указываем тип ввода submit и присваиваем ему значение "Save".

Для просмотра нашей работы запустите сервер с python manage.py runserver и перейдите на
домашнюю страницу по адресу http://127.0.0.1:8000/.

Группа в ВК "Django_Python"
Chapter 6: Forms 132

Homepage with New button

Нажмите на нашу ссылку "+ New Blog Post", которое перенаправит вас

на: http://127.0.0.1:8000/post/new/.

Группа в ВК "Django_Python"
Chapter 6: Forms 133

Blog new page

Попробуйте создать новую запись в блоге и отправить ее.

Группа в ВК "Django_Python"
Chapter 6: Forms 134

Blog new page

Упс! что случилось?

Группа в ВК "Django_Python"
Chapter 6: Forms 135

Blog new page

Сообщение об ошибке Django является весьма полезным. Он жалуется, что мы не указали,


куда отправить пользователя после успешной отправки формы. Давайте отправим
пользователя на страницу сведений после успешной отправки; таким образом они смогут
увидеть свою завершенную публикацию.
Мы можем следовать предложению Django и добавить get_absolute_url в нашу модель. Это
лучшая практика, которую вы всегда должны делать. Он устанавливает канонический URL-
адрес для объекта, поэтому, даже если структура ваших URL-адресов изменится в будущем,
ссылка на конкретный объект будет одинаковой. Короче говоря, вы должны добавить
get_absolute_url() и __ str_ _ () метод к каждой модели, которую вы пишете.

Группа в ВК "Django_Python"
Chapter 6: Forms 136

Откройте models.py файл. Добавьте импорт во второй строке для reverse и новый метод
get_-absolute_url.

Command Line

# blog/models.py

from django.db import models

from django.urls import reverse

class Post(models.Model):

title = models.CharField(max_length= )

author = models.ForeignKey(

'auth.User',

on_delete=models.CASCADE,

body = models.TextField()

def __str__(self):

return self.title

def get_absolute_url(self):

return reverse('post_detail', args=[str(self.id)])

Reverse - очень удобная утилита Django, позволяющая ссылаться на объект по имени


шаблона URL, в данном случае post_detail. Если вы всмните наш шаблон URL, то он
выглядит следующим образом:

Группа в ВК "Django_Python"
Chapter 6: Forms 137

Code

path('post/<int:pk>/', views.BlogDetailView.as_view(), name='post_detail'),

Это означает, что для того, чтобы этот маршрут работал, мы должны также передать
аргумент с ключом pk или первичным ключом объекта. Запутанно что pk и id
взаимозаменяемы в Django, хотя документация Django рекомендуют использовать self.id с
параметром get_absolute_url. Поэтому мы сообщаем Django, что конечным
местоположением записи Post является ее представление post_detail, которое представляет
собой posts / <int: pk> /, поэтому маршрут для первой созданной нами записи будет
находиться в posts / 1.
Попробуйте создать новую запись в блоге еще раз по адресу http://127.0.0.1:8000/post/new/,
и в случае успеха вы будете перенаправлены на страницу подробного просмотра, где
размещена запись.

Новая страница блога с вводом

Группа в ВК "Django_Python"
Chapter 6: Forms 138

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

Blog homepage with four posts

Хотя мы можем обратиться к администратору Django, чтобы удалить нежелательные


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

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

Группа в ВК "Django_Python"
Chapter 6: Forms 139

Для начала добавим новую ссылку на post_detail.html, чтобы возможность редактирования


сообщения блога появилась на отдельной странице блога.

Code

<!-- templates/post_detail.html -->

{% extends 'base.html' %}

{% block content %}

<div class="post-entry">

<h2>{{ object.title }}</h2>

<p>{{ object.body }}</p>

</div>

<a href="{% url 'post_edit' post.pk %}">+ Edit Blog Post</a>

{% endblock content %}

Мы добавили ссылку, используя <a href> ... </a> и тег движка шаблонов Django {% url ...%}.
В нем мы указали целевое имя нашего URL, которое будет называться post_edit, а также
передали необходимый параметр, который является первичным ключом поста post.pk.

Затем мы создаем шаблон для нашей страницы редактирования с именем post_edit.html.

Command Line

(blog) $ touch templates/post_edit.html

И добавьте следующий код:

Группа в ВК "Django_Python"
Chapter 6: Forms 140

Code

<!-- templates/post_edit.html -->

{% extends 'base.html' %}

{% block content %}

<h1>Edit post</h1>

<form action="" method="post">{% csrf_token %}

{{ form.as_p }}

<input type="submit" value="Update" />

</form>

{% endblock %}

Мы снова используем теги HTML <form></form>, csrf_token от Django для безопасности,


form.as_p, чтобы отобразить наши поля формы с тегами абзаца, и, наконец, дать ему
значение “Update” на кнопке отправки.

Теперь к нашему представлению нужно импортировать UpdateView на второй строке с


верху, а затем создать его подкласс в нашем новом представлении BlogUpdateView.

Code

# blog/views.py

from django.views.generic import ListView, DetailView

from django.views.generic.edit import CreateView, UpdateView

from . models import Post

class BlogListView(ListView):

model = Post

template_name = 'home.html'

Группа в ВК "Django_Python"
Chapter 6: Forms 141

class BlogDetailView(DetailView):

model = Post

template_name = 'post_detail.html'

class BlogCreateView(CreateView):

model = Post

template_name = 'post_new.html'

fields = '__all__'

class BlogUpdateView(UpdateView):

model = Post

fields = ['title', 'body']

template_name = 'post_edit.html'

Обратите внимание, что в представлении BlogUpdateView мы явно перечисляем поля,


которые мы хотим использовать ['title', 'body'], а не " __all__". Это потому, что мы
предполагаем, что автор сообщения не меняется; мы хотим, чтобы только заголовок и
текст были редактируемыми.
Заключительный этап-это обновление вашего файла urls.py как указано:

Группа в ВК "Django_Python"
Chapter 6: Forms 142

Code

# blog/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.BlogListView.as_view(), name='home'),

path('post/<int:pk>/', views.BlogDetailView.as_view(), name='post_detail'),

path('post/new/', views.BlogCreateView.as_view(), name='post_new'),

path('post/<int:pk>/edit/',

views.BlogUpdateView.as_view(), name='post_edit'),

В верхней части мы добавляем наше представление BlogUpdateView в список


импортированных представлений, затем создали новый шаблон url для /post/pk / edit и
дали ему имя post_edit.
Теперь, если вы нажмете на запись в блоге, вы увидите нашу новую кнопку
редактирования.

Страница блога с кнопкой редактировать

Группа в ВК "Django_Python"
Chapter 6: Forms 143

Если вы нажмете на "+ Edit Blog Pos", вы будете перенаправлены на http://127.0.0.1:8000/post/1/


edit/if это ваш первый пост в блоге.

Страница редактирования

Обратите внимание, что форма предварительно заполняется существующими данными


нашей базы данных для публикации. Давайте внесем изменения…

Группа в ВК "Django_Python"
Chapter 6: Forms 144

Blog edit page

И после нажатия кнопки "Update" мы перенаправляемся в подробный вид поста, где вы


можете увидеть изменения. Это из-за нашей установки get_absolute_url. Перейдите на
главную страницу, и вы увидите изменения рядом со всеми остальными записями.

Группа в ВК "Django_Python"
Chapter 6: Forms 145

Домашняя страница блога с отредактированным сообщением

Удаление View

Процесс создания формы для удаления записей блога очень похож на процесс обновления
записи. Мы будем использовать еще одно общее представление на основе классов,
DeleteView, для создания представления, url-адреса и шаблона функциональности.
Давайте начнем с добавления ссылки для удаления записей блога на Вашей странице
блога,post_-detail.html.

Группа в ВК "Django_Python"
Chapter 6: Forms 146

Code

<!-- templates/post_detail.html -->

{% extends 'base.html' %}

{% block content %}

<div class="post-entry">

<h2>{{ object.title }}</h2>

<p>{{ object.body }}</p>

</div>

<p><a href="{% url 'post_edit' post.pk %}">+ Edit Blog Post</a></p>

<p><a href="{% url 'post_delete' post.pk %}">+ Delete Blog Post</a></p>

{% endblock content %}

Затем создайте новый файл для нашего шаблона удаления страницы. Сначала выйдите из
локального сервера Control-c, а затем введите следующую команду:

Command Line

(blog) $ touch templates/post_delete.html

И заполните его этим кодом:

Группа в ВК "Django_Python"
Chapter 6: Forms 147

Code

<!-- templates/post_delete.html -->

{% extends 'base.html' %}

{% block content %}

<h1>Delete post</h1>

<form action="" method="post">{% csrf_token %}

<p>Are you sure you want to delete "{{ post.title }}"?</p>

<input type="submit" value="Confirm" />

</form>

{% endblock %}

Обратите внимание, что мы используем post.title для отображения заголовка нашего блога.
Мы также могли бы просто использовать object.title, поскольку он также предоставляется
DetailView.

Теперь обновите наш файл views.py, импортировав DeleteView и reverse_lazy вверху, затем
создайте новое представление, которое подкласс DeleteView.

Code

# blog/views.py

from django.views.generic import ListView, DetailView

from django.views.generic.edit import CreateView, UpdateView, DeleteView

from django.urls import reverse_lazy

from . models import Post

class BlogListView(ListView):

model = Post

Группа в ВК "Django_Python"
Chapter 6: Forms 148

template_name = 'home.html'

class BlogDetailView(DetailView):

model = Post

template_name = 'post_detail.html'

class BlogCreateView(CreateView):

model = Post

template_name = 'post_new.html'

fields = '__all__'

class BlogUpdateView(UpdateView):

model = Post

fields = ['title', 'body']

template_name = 'post_edit.html'

class BlogDeleteView(DeleteView):

model = Post

template_name = 'post_delete.html'

success_url = reverse_lazy('home')

Мы используем reverse_lazy вместо просто reverse, чтобы он не выполнял перенаправление


URL, пока наше представление не завершило удаление сообщения в блоге.

Наконец, добавьте URL, импортировав наш BlogDeleteView и добавив новый шаблон:

Группа в ВК "Django_Python"
Chapter 6: Forms 149

Code

# blog/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.BlogListView.as_view(), name='home'),

path('post/<int:pk>/', views.BlogDetailView.as_view(), name='post_detail'),

path('post/new/', views.BlogCreateView.as_view(), name='post_new'),

path('post/<int:pk>/edit/',

views.BlogUpdateView.as_view(), name='post_edit'),

path('post/<int:pk>/delete/',

views.BlogDeleteView.as_view(), name='post_delete'),

Если вы снова запустите сервер python manage.py runserver и обновите страницу


отдельного сообщения, вы увидите ссылку «Удалить сообщение в блоге».

Группа в ВК "Django_Python"
Chapter 6: Forms 150

Blog delete post

При нажатии на ссылку мы попадаем на страницу удаления записи блога, где отображается
название записи блога.

страница удалить запись

Если вы нажмете на кнопку “Confirm”, то будуте перенаправлены на домашнию страницу,


где пост был удален!

Группа в ВК "Django_Python"
Chapter 6: Forms 151

Домашняя страница с удаленным сообщением

Так это работает!

Tests

Время для тестов, чтобы убедиться, что все работает сейчас–и в будущем-как и ожидалось.
Мы добавили метод get_absolute_url в нашу модель и новые представления для создания,
обновления и редактирования записей. Это означает, что нам нужно четыре новых теста:

• def test_get_absolute_url
• def test_post_create_view
• def test_post_update_view
• def test_post_delete_view

Обновите существующий tests.py файл следующим образом.

Группа в ВК "Django_Python"
Chapter 6: Forms 152

Code

# blog/tests.py

from django.contrib.auth import get_user_model

from django.test import Client, TestCase

from django.urls import reverse

from .models import Post

class BlogTests(TestCase):

def setUp(self):

self.user = get_user_model().objects.create_user(

username='testuser',

email='test@email.com',

password='secret'

self.post = Post.objects.create(

title='A good title',

body='Nice body content',

author=self.user,

def test_string_representation(self):

post = Post(title='A sample title')

self.assertEqual(str(post), post.title)

Группа в ВК "Django_Python"
Chapter 6: Forms 153

def test_get_absolute_url(self):

self.assertEquals(self.post.get_absolute_url(), '/post/ /')

def test_post_content(self):

self.assertEqual(f'{self.post.title}', 'A good title')

self.assertEqual(f'{self.post.author}', 'testuser')

self.assertEqual(f'{self.post.body}', 'Nice body content')

def test_post_list_view(self):

response = self.client.get(reverse('home'))

self.assertEqual(response.status_code, )

self.assertContains(response, 'Nice body content')

self.assertTemplateUsed(response, 'home.html')

def test_post_detail_view(self):

response = self.client.get('/post/ /')

no_response = self.client.get('/post/ /')

self.assertEqual(response.status_code, )

self.assertEqual(no_response.status_code, )

self.assertContains(response, 'A good title')

self.assertTemplateUsed(response, 'post_detail.html')

def test_post_create_view(self):

response = self.client.post(reverse('post_new'), {

'title': 'New title',

'body': 'New text',

'author': self.user,

})

Группа в ВК "Django_Python"
Chapter 6: Forms 154

self.assertEqual(response.status_code, )

self.assertContains(response, 'New title')

self.assertContains(response, 'New text')

def test_post_update_view(self):

response = self.client.post(reverse('post_edit', args=' '), {

'title': 'Updated title',

'body': 'Updated text',

})

self.assertEqual(response.status_code, )

def test_post_delete_view(self):

response = self.client.get(

reverse('post_delete', args=' '))

self.assertEqual(response.status_code, )

Мы ожидаем, что URL нашего теста будет в post / 1 /, поскольку там только один пост, а 1 -
его первичный ключ, который Django автоматически добавляет для нас. Чтобы
протестировать создание представления, мы создаем новый ответ и затем проверяем, что
ответ проходит (код состояния 200) и содержит наш новый заголовок и основной текст.
Для просмотра обновлений мы обращаемся к первому сообщению, в котором в качестве
единственного аргумента указан pk, равный 1, и подтверждаем, что оно приводит к
перенаправлению 302. Наконец, мы проверяем наше представление удаления,
подтверждая, что если мы удаляем сообщение, код состояния 200 успех.

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

Группа в ВК "Django_Python"
Chapter 6: Forms 155

Заключение

В небольшом количестве кода мы создали приложение blog которое позволяет создавать,


читать, обновлять и удалять сообщения в блоге. Эта основная функциональность известна
под аббревиатурой CRUD: Create-Read-Update-Delete. Хотя есть несколько способов
достичь этой же функциональности-мы могли бы использовать представления на основе
функций или написать наши собственные представления на основе классов-мы
продемонстрировали, как мало кода требуется в Django, чтобы это произошло.

В следующей главе мы добавим учетные записи пользователей, а также функции входа,


выхода и регистрации.

Группа в ВК "Django_Python"
Глава 7: Учетные Записи Пользователей
На данный момент мы создали работающее приложение блога, которое использует формы,
но нам не хватает основной части большинства веб-приложений: аутентификации
пользователей.Реализация правильной аутентификации пользователя очень сложна; на
этом пути есть много проблем с безопасностью, так что вы действительно не захотите
реализовывать это самостоятельно. К счастью, Django поставляется с мощной встроенной
системой аутентификации пользователей, которую мы можем использовать.Каждый раз,
когда вы создаете новый проект, по умолчанию Django устанавливает приложение auth,
которое предоставляет нам объект User, содержащий:

• username
• password
• email
• first_name
• last_name

Мы будем использовать этот объект User для реализации входа в систему, выхода из
системы и регистрации в нашем приложении блога.

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

Сначала обновите файл urls.py уровня проекта. Мы разместим наши страницы входа и
выхода в accounts/ URL. Это однострочное дополнение в предпоследней строке.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 157

Code

# blog_project/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('accounts/', include('django.contrib.auth.urls')),

path('', include('blog.urls')),

Как отмечается в документации к LoginView, по умолчанию Django будет искать в папке с


шаблонами, с именем registration , файл с именем login.html для формы входа. Таким
образом, нам нужно создать новый каталог с именем Registration и необходимым файлом
внутри него. В командной строке введите Control-C, чтобы выйти из нашего локального
сервера. Затем введите следующее:

Command Line

(blog) $ mkdir templates/registration

(blog) $ touch templates/registration/login.html

Теперь введите следующий код шаблона для нашего вновь созданного файла.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 158

Code

<!-- templates/registration/login.html -->

{% extends 'base.html' %}

{% block content %}

<h2>Login</h2>

<form method="post">

{% csrf_token %}

{{ form.as_p }}

<button type="submit">Login</button>

</form>

{% endblock content %}

Мы используем HTML теги <form> </ form> и указываем метод POST, так как мы
отправляем данные на сервер (мы будем использовать GET, если запрашиваем данные,
например, в форме поисковой системы). Мы добавляем {% csrf_token%} из соображений
безопасности, а именно для предотвращения атаки XSS. Содержимое формы выводится
между тегами абзаца благодаря {{form.as_p}}, а затем мы добавляем кнопку «submit».На
последнем шаге нам нужно указать, куда перенаправить пользователя при успешном входе
в систему. Мы можем установить это с помощью параметра LOGIN_REDIRECT_URL.
Внизу файла settings.py добавьте следующее:

Code

# settings.py

LOGIN_REDIRECT_URL = 'home'

Теперь пользователь будет перенаправлен на шаблон "home", который является нашей


домашней страницей.
На данный момент мы закончили! Если теперь снова запустить сервер Django с python
manage.py runserver и перейдите на нашу страницу входа:

Группа в ВК "Django_Python"
Chapter 7: User Accounts 159

http://127.0.0.1:8000/accounts/login/

You’ll see the following:

Login page

После ввода регистрационных данных для нашей учетной записи суперпользователя, мы


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

Обновленние домашней страницы


Давайте обновим наш шаблон base.html, чтобы мы могли отображать сообщение
пользователям независимо от того, вошли они в систему или нет. Мы можем использовать
для этого атрибут is_authenticated.На данный момент мы можем просто разместить этот
код на видном месте. Позже мы можем стилизовать его более подходящим образом.
Обновите файл base.html, добавив новый код, начинающийся с закрывающего тега
</ header>.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 160

Code

<!-- templates/base.html -->

...

</header>

{% if user.is_authenticated %}

<p>Hi {{ user.username }}!</p>

{% else %}

<p>You are not logged in.</p>

<a href="{% url 'login' %}">login</a>

{% endif %}

{% block content %}

{% endblock content %}

Если пользователь вошел в систему, мы передаем ему привет по имени, если нет, мы
предоставляем ссылку на нашу недавно созданную страницу входа.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 161

Homepage logged in

Это сработало! Моего суперпользователя зовут wsv, вот что я вижу на странице.

Ссылка на выход
Мы добавили логику страницы шаблона для пользователей, вышедших из системы, но ...
как нам выйти сейчас? Мы могли бы зайти в панель администратора и сделать это вручную,
но есть и лучший способ. Давайте вместо этого добавим ссылку выхода из системы,
которая перенаправляет на домашнюю страницу. Благодаря системе аутентификации
Django этого достичь очень просто.

В нашем файле base.html добавьте однострочную ссылку {% url 'logout'%} для выхода из
системы.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 162

Command Line

<!-- templates/base.html-->

...

{% if user.is_authenticated %}

<p>Hi {{ user.username }}!</p>

<p><a href="{% url 'logout' %}">logout</a></p>

{% else %}

...

Это все, что нам нужно сделать, так как необходимое представление предоставляется нам
приложением Django auth. Нам нужно указать, куда перенаправить пользователя при
выходе из системы.

Обновите файл settings.py, чтобы предоставить ссылку для перенаправления, которая


соответственно называется LOGOUT_-REDIRECT_URL. Мы можем добавить его прямо
рядом с нашим перенаправлением входа в систему, чтобы нижняя часть файла выглядела
следующим образом:

Code

# blog_project/settings.py

LOGIN_REDIRECT_URL = 'home'

LOGOUT_REDIRECT_URL = 'home'

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

Группа в ВК "Django_Python"
Chapter 7: User Accounts 163

Homepage logout link

И нажав на нее, вы вернетесь на домашнюю страницу с ссылкой для входа.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 164

Homepage logged out

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

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

Есть много способов организовать ваш код и структуру URL для надежной системы
аутентификации пользователей. Здесь мы создадим новое приложение, accounts, для нашей
страницы регистрации.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 165

Command Line

(blog) $ python manage.py startapp accounts

Добавьте новое приложение в параметр INSTALLED_APPS в settings.py

файл. Code

# blog_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'blog',

'accounts',

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

Группа в ВК "Django_Python"
Chapter 7: User Accounts 166

Code

# blog_project/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('accounts/', include('django.contrib.auth.urls')),

path('accounts/', include('accounts.urls')),

path('', include('blog.urls')),

Здесь важен порядок наших URL, потому что Django читает этот файл сверху вниз.
Поэтому, когда мы запрашиваем их /accounts/signup url, Django сначала смотрит в auth, не
находит его, а затем переходит к приложению account.

Давайте продолжим и создадим наш файл account / urls.py.

Command Line

(blog) $ touch accounts/urls.py

И добавьте следующий код:

Группа в ВК "Django_Python"
Chapter 7: User Accounts 167

Code

# acounts/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('signup/', views.SignUpView.as_view(), name='signup'),

Мы используем еще не созданное представление с именем RegistrationView, которое, как


мы уже знаем, основано на классе, так как оно написано заглавными буквами и имеет
суффикс as_view (). Его путь просто signup/ поэтому общим путем будет accounts/
signup/.Сейчас представление, которое использует встроенную UserCreationForm и
универсальный CreateView.

Code

# accounts/views.py

from django.contrib.auth.forms import UserCreationForm

from django.urls import reverse_lazy

from django.views import generic

class SignUpView(generic.CreateView):

form_class = UserCreationForm

success_url = reverse_lazy('login')

template_name = 'signup.html'

Мы создаем подкласс общего представления на основе классов CreateView в нашем классе


SignUpView. Мы указываем использование встроенного UserCreationForm и еще не
созданного шаблона в signup.html. И мы используем reverse_lazy, чтобы перенаправить
пользователя на страницу входа в систему после успешной регистрации.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 168

Зачем здесь использовать reverse_lazy вместо reverse? Причина в том, что для всех общих
представлений на основе классов URL не загружаются при импорте файла, поэтому мы
должны использовать ленивую форму реверса, чтобы загрузить их позже, когда они станут
доступны.

Теперь давайте добавим signup.html в нашу папку шаблонов уровня проекта:

Command Line

(blog) $ touch templates/signup.html

Добавьте, а затем заполните его кодом ниже.

Code

<!-- templates/signup.html -->

{% extends 'base.html' %}

{% block content %}

<h2>Sign up</h2>

<form method="post">

{% csrf_token %}

{{ form.as_p }}

<button type="submit">Sign up</button>

</form>

{% endblock %}

Этот формат очень похож на то, что мы делали раньше. Мы расширяем наш базовый
шаблон сверху, размещаем нашу логику между тегами <form> </ form>, используем
csrf_token для безопасности, отображаем содержимое формы в тегах абзаца с помощью
form.as_p и добавляем кнопку отправки.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 169

Теперь мы закончили! Чтобы проверить это, перейдите на нашу новую

страницу: http://127.0.0.1:8000/accounts/signup/

Django signup page

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

Я создал нового пользователя с именем «william» и после отправки был перенаправлен на


страницу входа. Затем, после успешного входа в систему с использованием моего нового
пользователя и пароля, я был перенаправлен на домашнюю страницу с нашим
персонализированным приветствием «Привет, имя пользователя».

Группа в ВК "Django_Python"
Chapter 7: User Accounts 170

Homepage for user wsvincent

Таким образом, наш конечный поток: Signup -> Login -> Homepage. И, конечно, мы можем
настроить это, как мы хотим. RegistrationView перенаправляет на вход в систему, потому
что мы установили success_url = reverse_lazy ('login'). Страница входа перенаправляется на
домашнюю страницу, потому что в нашем файле settings.py мы установили
LOGIN_REDIRECT_URL = 'home'.
На первый взгляд может показаться сложным отслеживать все различные части проекта
Django. Это нормально. Но я обещаю, что со временем они станут более понятными.

Bitbucket

Прошло много времени с тех пор, как мы сделали git commit. Давайте сделаем это, а затем
вставим копию нашего кода в Bitbucket.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 171

Сначала проверьте всю новую работу, которую мы сделали с git status.

Command Line

(blog) $ git status

Затем добавьте новый контент.

Command Line

(blog) $ git commit -m 'forms and user accounts'

Создайте новый репозиторий на Bitbucket, который вы можете назвать как угодно. Я


выберу название blog-app. Поэтому после создания нового репо на сайте Bitbucket я могу
ввести следующие две команды. Обязательно замените мое имя пользователя wsvincent
вашим именем пользователя из Bitbucket.

Command Line

(blog) $ git remote add origin git@bitbucket.org:wsvincent/blog-app.git

(blog) $ git push -u origin master

Все сделано! Теперь мы можем развернуть наше новое приложение на Heroku.

Heroku конфигурация

Это наше третье развертывание приложения. Как и в нашем приложении Message Board
(доска сообщений), есть четыре изменения, которые нам нужно сделать, чтобы его можно
было развернуть на Heroku.
• обновить Pipfile.lock
• новый Procfile
• установить gunicorn

Группа в ВК "Django_Python"
Chapter 7: User Accounts 172

• обновить settings.py

Мы укажем версию Python в нашем Pipfile, а затем запустим pipenv, чтобы применить ее к
Pipfile.lock. Мы добавим Procfile, который является файлом конфигурации, специфичным
для Heroku, установим gunicorn для запуска в качестве нашего рабочего веб-сервера вместо
локального сервера Django и, наконец, обновим ALLOWED_HOSTS, чтобы любой мог
просматривать наше приложение.

Откройте Pipfile в текстовом редакторе и в нижней части добавьте следующие две строки.

Code

# Pipfile

[requires]

python_version = " . "

Мы используем здесь «3.6», А не более конкретный «3.6.4», так что наше приложение
автоматически обновляется до самой последней версии Python 3.6x для Heroku.Теперь
запустите pipenv lock, чтобы обновить наш Pipfile.lock, поскольку Heroku будет
использовать его для создания новой среды на серверах Heroku для нашего приложения.

Command Line

(blog) $ pipenv lock

Создайте новый файл Procfile .

Command Line

(blog) $ touch Procfile

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

Группа в ВК "Django_Python"
Chapter 7: User Accounts 173

Code

web: gunicorn blog_project.wsgi --log-file -

Сейчас установите gunicorn.

Command Line

(blog) $ pipenv install gunicorn

Наконец, обновите ALLOWED_HOSTS, чтобы принять все домены, которые представлены


звездочкой *.

Code

# blog_project/settings.py

ALLOWED_HOSTS = ['*']

Мы можем зафиксировать(commit) наши новые изменения и отправить их в Bitbucket.

Command Line

(blog) $ git status

(blog) $ git add -A

(blog) $ git commit -m 'Heroku config files and updates'

(blog) $ git push -u origin master

Развертывание на Heroku

Для развертывания в Heroku сначала убедитесь, что вы вошли в свою существующую


учетную запись Heroku.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 174

Command Line
(blog) $ heroku login

Затем выполните команду create, которая скажет Heroku создать новый контейнер для
нашего приложения. Если вы просто запустите heroku create, Heroku назначит вам
произвольное имя, однако вы можете указать произвольное имя, но оно должно быть
уникальным в Heroku. Другими словами, так как я выбираю название dfb-blog, вы не
сможете дать такое же имя. Вам нужна другая комбинация букв и цифр.

Command Line

(blog) $ heroku create dfb-blog

Теперь настройте git таким образом, чтобы при переходе на Heroku он переходил к новому
имени приложения (заменив dfb-blog своим произвольным именем).

Command Line

(blog) $ heroku git:remote -a dfb-blog

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

Command Line

(blog) $ pipenv install whitenoise

Затем нам нужно обновить наши static настройки, чтобы они были использованы в
производстве. В вашем текстовом редакторе откройте settings.py. Добавьте whitenoise в
INSTALLED_APPS над встроенным приложением staticfiles, а также в MIDDLEWARE в
третьей строке. Порядок имеет значение как для INSTALLED_APPS, так и для
MIDDLEWARE.
Внизу файла добавьте новые строки для STATIC_ROOT и STATICFILES_STORAGE. Это
должно выглядеть следующим образом.

Группа в ВК "Django_Python"
Chapter 7: User Accounts 175

Code

# blog_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'whitenoise.runserver_nostatic', # new!

'django.contrib.staticfiles',

'blog',

'accounts',

MIDDLEWARE = [

'django.middleware.security.SecurityMiddleware',

'django.contrib.sessions.middleware.SessionMiddleware',

'whitenoise.middleware.WhiteNoiseMiddleware', # new!

'django.middleware.common.CommonMiddleware',

'django.middleware.csrf.CsrfViewMiddleware',

'django.contrib.auth.middleware.AuthenticationMiddleware',

'django.contrib.messages.middleware.MessageMiddleware',

'django.middleware.clickjacking.XFrameOptionsMiddleware',

...

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # new!

Группа в ВК "Django_Python"
Chapter 7: User Accounts 176

STATIC_URL = '/static/'

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' \

# new!

Не забудьте добавить и зафиксировать новые изменения. Затем нажмите на Bitbucket.

Command Line

(blog) $ git add -A

(blog) $ git commit -m 'Heroku config'

(blog) $ git push origin master

Наконец, мы можем перенести наш код в Heroku и добавить веб-процесс, чтобы dyno работал.

Command Line

(blog) $ git push heroku master

(blog) $ heroku ps:scale web=

URL вашего нового приложения будет в выходных данных командной строки, или вы
можете запустить heroku open, чтобы найти его. Мой находится по адресу https://df b-
blog.herokuapp.com/.

Heroku site

Группа в ВК "Django_Python"
Chapter 7: User Accounts 177

Заключение

С минимальным количеством кода, фреймворк Django позволил нам создать поток


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

Группа в ВК "Django_Python"
Глава 8: Custom User Model
Встроенная в Django модель User позволяет нам сразу начать работать с пользователями,
как мы только что делали с нашим приложением блога в предыдущих главах. Однако
официальная документация Django настоятельно рекомендует использовать
пользовательскую модель для новых проектов. Причина в том, что если вы хотите внести
какие-либо изменения в пользовательскую модель в будущем например, добавив поле
возраста с помощью custom user model с самого начала это довольно просто. Но если вы не
создадите custom user model, обновление модели пользователя по умолчанию в
существующем проекте Django будет очень и очень сложным.
Поэтому всегда используйте пользовательскую модель для всех новых проектов Django.
Однако пример официальной документации не совсем то, что рекомендуют многие
эксперты Django. Он использует довольно сложный AbstractBaseUser, чем если мы просто
используем AbstractUser, все гораздо проще и все еще настраиваемо. Это подход, который
мы будем использовать в этой главе, где мы правильно запускаем новое приложение
Newspaper(Газета) с пользовательской моделью. Выбор приложения газеты отдает дань
уважения корням Django в качестве веб фреймворка, созданного для редакторов и
журналистов в Lawrence Journal-World.

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

• создать и перейти в новый каталог для нашего кода


• создать новую виртуальную среду news

• установка Django
• создать новый проект Django newspaper_project

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 179

• сделать новое приложение users

Мы называем наше приложение для управления пользователями users, но вы также


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

Вот команды для запуска:

Command Line

$ cd ~/Desktop

$ mkdir news

$ cd news

$ pipenv install django

$ pipenv shell

(news) $ django-admin startproject newspaper_project .

(news) $ python manage.py startapp users

(news) $ python manage.py runserver

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

Если вы перейдете к http://127.0.0.1:8000 вы увидите знакомый экран приветствия Django.

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 180

Welcome page

Custom User Model

Создание нашей собственной модели пользователя


требует четыре шага:

• обновить settings.py

• создание новой CustomUser модели


• создание новой формы для UserCreation и
UserChangeForm
• обновить админку сайта

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 181

В settings.py мы добавим приложение users в наш INSTALLED_APPS. Затем в нижней части


файла используйте конфигурацию AUTH_USER_MODEL, чтобы указать Django
использовать нашу новую пользовательскую модель вместо встроенной модели User. Мы
будем называть нашу пользовательскую пользовательскую модель CustomUser так,
поскольку она существует в нашем users приложении, мы называем ее users.CustomUser.

Code

# newspaper_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'users', # new

...

AUTH_USER_MODEL = 'users.CustomUser'

Теперь обновите users / models.py новой моделью User, которую мы будем называть
CustomUser. Мы также добавим наше первое дополнительное поле для «age»(возраста)
наших пользователей. Мы можем использовать PositiveIntegerField от Django, что означает,
что целое число должно быть либо положительным, либо нулевым.

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 182

Code

# users/models.py

from django.contrib.auth.models import AbstractUser

from django.db import models

class CustomUser(AbstractUser):

age = models.PositiveIntegerField(default= )

Это действительно весь код, который нам нужен! Поскольку мы расширяем AbstractUser,
наш CustomUser является в основном копией модели User по умолчанию. Единственное
обновление - наше поле age.

Формы
Если мы на шаг отступим назад, как мы будем взаимодействовать с нашей новой моделью
CustomUser? Первый случай, когда пользователь регистрирует новую учетную запись на
нашем сайте. Другой находится в приложении администратора, которое позволяет нам, как
суперпользователям, изменять существующих пользователей. Поэтому нам нужно
обновить две встроенные формы для этой функции: UserCreationForm и UserChangeForm.

Остановите локальный сервер с помощью Control + c и создайте новый файл в приложении


users с именем forms.py.

Command Line

(news) $ touch users/forms.py

Мы обновим его следующим кодом, чтобы расширить существующие формы


UserCreationForm и UserChangeForm.

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 183

Code

# users/forms.py

from django import forms

from django.contrib.auth.forms import UserCreationForm, UserChangeForm

from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):

class Meta(UserCreationForm.Meta):

model = CustomUser

fields = UserCreationForm.Meta.fields

class CustomUserChangeForm(UserChangeForm):

class Meta:

model = CustomUser

fields = UserChangeForm.Meta.fields

Для обеих новых форм мы устанавливаем модель CustomUser и используем поля по


умолчанию с помощью Meta.fields.
TПоначалу понятие полей в форме может сбивать с толку, поэтому давайте уделим
немного времени, чтобы изучить его дальше. Наша модель CustomUser содержит все поля
модели пользователя по умолчанию и наше дополнительное поле возраста, которое мы
установили. Но что это за поля по умолчанию? Оказывается, есть много, включая имя
пользователя, имя, фамилия, адрес электронной почты, пароль, группы и многое другое.
Тем не менее, когда пользователь регистрирует новую учетную запись в Django, форма по
умолчанию запрашивает только имя пользователя, адрес электронной почты и пароль.

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 184

Это говорит нам о том, что настройка по умолчанию для полей в UserCreationForm - это
просто имя пользователя, адрес электронной почты и пароль, хотя доступно еще много
полей. Это может быть не понятно, так как для правильного понимания форм и моделей
требуется некоторое время. В следующей главе мы создадим наши собственные страницы
регистрации, входа и выхода, которые будут более четко связывать нашу модель
CustomUser и формы. Так что держитесь! Последний шаг это обновить наш файл admin.py,
так как Admin тесно связан с моделью пользователя по умолчанию. Мы расширим
существующий класс UserAdmin для использования нашей новой модели CustomUser и
двух новых форм.

Code

# users/admin.py

from django.contrib import admin

from django.contrib.auth.admin import UserAdmin

from .forms import CustomUserCreationForm, CustomUserChangeForm

from .models import CustomUser

class CustomUserAdmin(UserAdmin):

add_form = CustomUserCreationForm

form = CustomUserChangeForm

list_display = ['email', 'username', 'age']

model = CustomUser

admin.site.register(CustomUser, CustomUserAdmin)

Обратите внимание, что наш CustomUserAdmin также имеет параметр list_display, чтобы
он отображал только поля email, username и age, даже если на данный момент в модели
CustomUser их гораздо больше.

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 185

Хорошо, мы закончили! Запустите makemigrations и выполните миграцию в первый раз,


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

Command Line

(news) $ python manage.py makemigrations

(news) $ python manage.py migrate

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

Command Line

(news) $ python manage.py createsuperuser

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

Запустите веб-сервер.

Command Line

(news) $ python manage.py runserver

Затем перейдите в админку по адресу http://127.0.0.1:8000/admin и войдите в систему.

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 186

Admin page

Если вы нажмете на ссылку "Users", вы увидите свою учетную запись суперпользователя.


Обратите внимание, что три поля, которые мы видим, электронная почта, имя
пользователя и возраст, так как мы установили, как list_-display в нашем CustomUserAdmin.

Группа в ВК "Django_Python"
Chapter 8: Custom User Model 187

Admin one user

Заключение

Теперь, когда наша пользовательская модель готова, мы можем сосредоточиться на


создании остальной части нашего приложения Newspaper . В следующей главе мы
настроим страницы регистрации, входа и выхода.

Группа в ВК "Django_Python"
Глава 9: Аутентификация пользователя
Теперь, когда у нас есть рабочая пользовательская модель, мы можем добавить
функциональность, которая нужна каждому веб-сайту: возможность регистрироваться,
входить и выходить из системы. Django предоставляет все необходимое для входа и выхода,
но нам нужно будет создать собственную форму для регистрации новых пользователей. Мы
также создадим базовую домашнюю страницу со ссылками на все три функции, поэтому
нам не придется каждый раз вводить URL вручную.

Templates(Шаблоны)

По умолчанию загрузчик шаблонов Django ищет шаблоны в вложенной структуре в


каждом приложении. Таким образом, шаблон home.html для пользователей должен быть
расположен по адресу users / templates / users / home.html. Но подход к папке с шаблонами
на уровне проекта более понятен и лучше масштабируется, поэтому мы будем использовать
его.Давайте создадим новый каталог шаблонов и внутри него папку registration, в которой
Django будет искать шаблон login.

Command Line

(news) $ mkdir templates

(news) $ mkdir templates/registration

Теперь нам нужно сообщить Django об этом новом каталоге, обновив конфигурацию для
'DIRS' в settings.py. Это изменение в одну строку.

Группа в ВК "Django_Python"
Chapter 9: User Authentication 189

Code

# newspaper_project/settings.py

TEMPLATES = [

...

'DIRS': [os.path.join(BASE_DIR, 'templates')],

...

Если вы думаете о том, что происходит, когда вы входите или выходите из сайта, вы сразу
же перенаправляетесь на следующую страницу. Нам нужно указать Django, куда отправлять
пользователей в каждом конкретном случае. Делают это настройки
LOGIN_REDIRECT_URL и LOGOUT_REDIRECT_URL. Мы настроим оба, чтобы
перенаправить на нашу домашнюю страницу, которая будет иметь названный URL
«home».Помните, что когда мы создаем наши маршруты URL, у нас есть возможность
добавить имя для каждого из них. Поэтому, когда мы создадим URL домашней страницы,
мы обязательно назовем его «home».Добавьте эти две строки внизу файла settings.py.

Code

# newspaper_project/settings.py

LOGIN_REDIRECT_URL = 'home'

LOGOUT_REDIRECT_URL = 'home'

Now we can create four new templates:

Группа в ВК "Django_Python"
Chapter 9: User Authentication 190

Command Line

(news) $ touch templates/registration/login.html

(news) $ touch templates/base.html

(news) $ touch templates/home.html

(news) $ touch templates/signup.html

Вот HTML код для каждого используемого файла. base.html будет унаследован каждым
другим шаблоном в нашем проекте. Используя блок типа {% block content %}, мы можем
позже переопределить содержимое только в этом месте в других шаблонах.

Code

<!-- templates/base.html -->

<!DOCTYPE html>

<html>

<head>

<meta charset="utf- ">

<title>Newspaper App</title>

</head>

<body>

<main>

{% block content %}

{% endblock %}

</main>

</body>

</html>

Группа в ВК "Django_Python"
Chapter 9: User Authentication 191

Code

<!-- templates/home.html -->

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}

{% if user.is_authenticated %}

Hi {{ user.username }}!

<p><a href="{% url 'logout' %}">logout</a></p>

{% else %}

<p>You are not logged in</p>

<a href="{% url 'login' %}">login</a> |

<a href="{% url 'signup' %}">signup</a>

{% endif %}

{% endblock %}

Code

<!-- templates/registration/login.html -->

{% extends 'base.html' %}

{% block title %}Login{% endblock %}

{% block content %}

<h2>Login</h2>

<form method="post">

{% csrf_token %}

{{ form.as_p }}

Группа в ВК "Django_Python"
Chapter 9: User Authentication 192

<button type="submit">Login</button>

</form>

{% endblock %}

Code

<!-- templates/signup.html -->

{% extends 'base.html' %}

{% block title %}Sign Up{% endblock %}

{% block content %}

<h2>Sign up</h2>

<form method="post">

{% csrf_token %}

{{ form.as_p }}

<button type="submit">Sign up</button>

</form>

{% endblock %}

Наши шаблоны готовы. Тем не менее перейдем к нашим URL и представлениям.

URLs

Давайте начнем с URL-маршрутов. В нашем файле urls.py уровня проекта мы хотим, чтобы
наш шаблон home.html отображался в качестве домашней страницы. Но мы пока не хотим
создавать приложение для выделенных страниц, поэтому мы можем использовать быстрый
доступ импортируя TemplateView и установки имени шаблона прямо в нашем шаблоне
URL.

Группа в ВК "Django_Python"
Chapter 9: User Authentication 193

Далее мы хотим «включить» и приложение пользователя, и встроенное приложение


аутентификации. Причина в том, что встроенное приложение авторизации уже
предоставляет представления и URL для входа и выхода из системы. Но для регистрации
нам нужно создать наш собственное представление и URL. Чтобы обеспечить
согласованность наших URL маршрутов, мы размещаем в users/, поэтому возможными URL
будут / users / login, / users / logout и / users / signup.

Code

# newspaper_project/urls.py

from django.contrib import admin

from django.urls import path, include

from django.views.generic.base import TemplateView

urlpatterns = [

path('', TemplateView.as_view(template_name='home.html'), name='home'),

path('admin/', admin.site.urls),

path('users/', include('users.urls')),

path('users/', include('django.contrib.auth.urls')),

Создайте файл urls.py в приложении users.

Command Line

(news) $ touch users/urls.py

Обновите users / urls.py следующим кодом:

Группа в ВК "Django_Python"
Chapter 9: User Authentication 194

Code

# users/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('signup/', views.SignUp.as_view(), name='signup'),

Последний шаг это наш файл views.py, который будет содержать логику для нашей формы
регистрации. Здесь мы используем общий CreateView от Django и говорим ему
использовать нашу CustomUserCreation-Form и перенаправлять на вход в систему после
успешной регистрации пользователя, и что наш шаблон называется signup.html.

Code

# users/views.py

from django.urls import reverse_lazy

from django.views import generic

from .forms import CustomUserCreationForm

class SignUp(generic.CreateView):

form_class = CustomUserCreationForm

success_url = reverse_lazy('login')

template_name = 'signup.html'

Отлично! Мы это сделали. Давайте все проверим.Запустите сервер с помощью python


manage.py runserver и перейдите на домашнюю страницу по адресу http://127.0.0.1:8000/.

Группа в ВК "Django_Python"
Chapter 9: User Authentication 195

Homepage logged in

Мы вошли в систему администратора в предыдущей главе, поэтому вы должны увидеть


персональное приветствие. Нажмите на ссылку выхода.

Homepage logged out

Теперь мы на главной странице. Идем вперед и нажимаем на ссылку входа в систему и


используем свои учетные данные суперпользователя.

Login

После успешного входа в систему вы будете перенаправлены обратно на главную страницу


и увидите то же персонализированное приветствие. Работает!

Группа в ВК "Django_Python"
Chapter 9: User Authentication 196

Homepage logged in

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

Homepage logged out

Вы будете перенаправлены на страницу регистрации(signup ).

Signup page

Создайте нового пользователя. Мой называется testuser. После успешной отправки формы
вы будете перенаправлены на страницу входа(login ). Войдите в систему с новым
пользователем, и вы снова будете

Группа в ВК "Django_Python"
Chapter 9: User Authentication 197

перенаправлены на домашнюю страницу с персонализированным приветствием для нового


пользователя.

Homepage for testuser

Все работает, как и ожидалось.

Admin

Давайте также войдем в admin для просмотра наших двух учетных записей

пользователей. Перейдите к: http://127.0.0.1:8000/admin и …

Группа в ВК "Django_Python"
Chapter 9: User Authentication 198

Admin login wrong

Что это такое? Почему мы не можем войти?

Потому что мы вошли в систему с нашей новой учетной записью тестового пользователя, а
не с учетной записью суперпользователя. Только учетная запись суперпользователя имеет
разрешения для входа в систему администратора! Поэтому используйте свою учетную
запись суперпользователя для входа.

После этого вы увидите обычную домашнюю страницу администратора. Нажмите на Users


и вы увидите наших двух пользователей: того, которого мы только что создали, и ваше
предыдущее имя суперпользователя (мое-wsv).

Группа в ВК "Django_Python"
Chapter 9: User Authentication 199

Users in the Admin

Все работает, но вы можете заметить, что для нашего тестового пользователя нет поля
электронной почты. Почему так? Ну, оглянитесь на страницу регистрации в users/signup/, и
вы увидите, что она запрашивает только имя пользователя и пароль, а не электронную
почту! Именно так работает настройка Django по умолчанию. Однако мы можем легко
изменить это. Вернемся к нашему файлу users/forms.py.

Сейчас это выглядит так:

Группа в ВК "Django_Python"
Chapter 9: User Authentication 200

Code

# users/forms.py

from django import forms

from django.contrib.auth.forms import UserCreationForm, UserChangeForm

from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):

class Meta(UserCreationForm.Meta):

model = CustomUser

fields = UserCreationForm.Meta.fields

class CustomUserChangeForm(UserChangeForm):

class Meta:

model = CustomUser

fields = UserChangeForm.Meta.fields

Мы используем Meta.поля, которые просто отображают настройки по умолчанию имя


пользователя/пароль. Но мы также можем явно установить, какие поля мы хотим
отобразить, поэтому давайте обновим его, чтобы запросить имя пользователя/адрес
электронной почты/пароль, установив его в ('username', 'email',). Нам не нужно включать поле
пароля, потому что оно необходимо! Но все остальные поля могут быть настроены, как мы
выберем.

Группа в ВК "Django_Python"
Chapter 9: User Authentication 201

Code

# users/forms.py

...

class CustomUserCreationForm(UserCreationForm):

class Meta(UserCreationForm.Meta):

model = CustomUser

fields = ('username', 'email', ) # new

class CustomUserChangeForm(UserChangeForm):

class Meta:

model = CustomUser

fields = ('username', 'email', ) # new

Теперь, если вы попробуете страницу регистрации снова на http://127.0.0.1:8000/users/


signup/ вы можете увидеть дополнительное поле “Email address”.

Группа в ВК "Django_Python"
Chapter 9: User Authentication 202

New sign up page

Зарегистрируйтесь с новой учетной записью пользователя. Я назвал свою testuser2 с


адресом электронной почты testuser2@email.com. Если мы затем переключимся обратно на
страницу администратора и войдем с помощью учетной записи суперпользователя, мы
обнаружим теперь три пользователя.

Группа в ВК "Django_Python"
Chapter 9: User Authentication 203

Three users in the Admin

Аутентификации пользователя Django требует немного настройки, но вы должны начать


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

Заключение

До сих пор наше приложение Newspaper имеет кастомную модель пользователя и рабочие
страницы регистрации, входа и выхода. Но вы, возможно, заметили, что наш сайт выглядит
не очень хорошо. В следующей главе мы добавим Bootstrap для стилизации и создадим
приложение Pages.

Группа в ВК "Django_Python"
Глава ��: Bootstrap
Веб-разработка требует много навыков. Вам нужно не только программировать сайт для
корректной работы, но так же пользователи ожидают, что он будет хорошо выглядеть.
Когда вы создаете все с нуля, может быть крайне тяжело добавить все необходимые
HTML / CSS для красивого сайта.
К счастью, есть Bootstrap, самая популярная платформа для создания адаптивных проектов,
ориентированных на мобильные устройства. Вместо того, чтобы писать все наши
собственные CSS и JavaScript для общих функций макета веб-сайта, мы можем вместо этого
положиться на Bootstrap, чтобы сделать тяжелую работу. Это означает, что с небольшим
количеством кода с нашей стороны мы сможем быстро создать великолепно выглядящие
сайты. И если мы хотим вносить изменения по мере развития проекта, то при
необходимости Bootstrap легко переопределить.

Если вы хотите сосредоточиться на функциональности проекта, а не на дизайне, Bootstrap -


отличный выбор. Вот почему мы будем использовать его здесь.

Приложение Pages

В предыдущей главе мы отображали нашу домашнюю страницу, включив логику просмотра


в наш файл urls.py. Хотя этот подход работает, он кажется мне несколько хакерским, и,
конечно, он не масштабируется, поскольку веб-сайт со временем растет. Это также,
вероятно, несколько сбивает с толку новичков в Django. Вместо этого мы можем и должны
создать специальное приложение для всех наших статических страниц. Это сделает наш код
красивым и организованным далее. В командной строке используйте команду startapp для
создания нашего нового приложения pages. Если сервер все еще работает, вам может
потребоваться сначала нажать Control + c, чтобы выйти из него.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 205

Command Line

(news) $ python manage.py startapp pages

Затем сразу обновите наш файл settings.py. Я часто забываю делать это, поэтому
рекомендуется думать о создании нового приложения как о двухэтапном процессе:
запустите команду startapp, а затем обновите INSTALLED_APPS.

Code

# newspaper_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'users',

'pages', # new

Теперь мы можем обновить наш файл urls.py на уровне проекта. Идем дальше и удалим
импорт TemplateView. Мы также обновим маршрут ' ', чтобы включить приложение pages.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 206

Code

# newspaper_project/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('', include('pages.urls')),

path('admin/', admin.site.urls),

path('users/', include('users.urls')),

path('users/', include('django.contrib.auth.urls')),

Пришло время добавить нашу домашнюю страницу, что означает стандартные urls/views/
templates. Начнем с файла pages/urls.py . Сначала создайте его.

Command Line

(news) $ touch pages/urls.py

Затем импортируйте наши еще не созданные представления, задайте пути маршрута и


убедитесь, что каждый url так же имеет имя(name).

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 207

Code

# pages/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.HomePageView.as_view(), name='home'),

На данном этапе код views.py должен выглядеть знакомым. Мы используем представление


шаблонов на основе классов Django, что означает, что нам нужно только указать
template_name, чтобы использовать его.

Code

# pages/views.py

from django.views.generic import TemplateView

class HomePageView(TemplateView):

template_name = 'home.html'

У нас уже есть существующий шаблон home.html . Давайте проверим, что он по прежнему
работает должным образом с нашим новым url и представлением. Перейдите к странице
http://127.0.0.1:8000/ что бы проверить что все остается неизменным.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 208

Homepage logged in

Он должен показывать имя вашей учетной записи суперпользователя, которую мы


использовали в конце последней главы.

Тесты

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

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

Сейчас наш проект имеет четыре страницы:

• home
• signup
• login
• logout

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

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 209

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

Вот как должен выглядеть код в вашем файле pages/tests.py.

Code
# pages/tests.py

from django.contrib.auth import get_user_model

from django.test import SimpleTestCase, TestCase

from django.urls import reverse

class HomePageTests(SimpleTestCase):

def test_home_page_status_code(self):

response = self.client.get('/')

self.assertEqual(response.status_code, )

def test_view_url_by_name(self):

response = self.client.get(reverse('home'))

self.assertEqual(response.status_code, )

def test_view_uses_correct_template(self):

response = self.client.get(reverse('home'))

self.assertEqual(response.status_code, )

self.assertTemplateUsed(response, 'home.html')

class SignupPageTests(TestCase):

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 210

username = 'newuser'

email = 'newuser@email.com'

def test_signup_page_status_code(self):

response = self.client.get('/users/signup/')

self.assertEqual(response.status_code, )

def test_view_url_by_name(self):

response = self.client.get(reverse('signup'))

self.assertEqual(response.status_code, )

def test_view_uses_correct_template(self):

response = self.client.get(reverse('signup'))

self.assertEqual(response.status_code, )

self.assertTemplateUsed(response, 'signup.html')

def test_signup_form(self):

new_user = get_user_model().objects.create_user(

self.username, self.email)

self.assertEqual(get_user_model().objects.all().count(), )

self.assertEqual(get_user_model().objects.all()

[ ].username, self.username)

self.assertEqual(get_user_model().objects.all()

[ ].email, self.email)

В верхней строке мы используем get_user_model() для ссылки на нашу пользовательскую


модель. Затем для обеих страниц мы тестируем три вещи:

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 211

• страница существует и возвращает код состояния HTTP 200


• страница использует правильное имя URL в представлении
• используется правильный шаблон

Наша страница регистрации также имеет форму, поэтому мы должны проверить и это. В
тестовой форме test_sign up_ мы проверяем, что когда имя пользователя и адрес
электронной почты публикуются (отправляются в базу данных), они соответствуют тому,
что хранится в модели CustomUser.
Выйдите из локального сервера с Control+c, а затем запустите наши тесты, чтобы
подтвердить, что все проходит.

Command Line

(news) $ python manage.py test

Bootstrap

Если вы никогда не использовали Bootstrap, вы получите реальное удовольствие от его


использования. Он так много делает в таком маленьком коде.

Существует два способа добавить Bootstrap в проект: можно загрузить все файлы и
обслуживать их локально или использовать Сеть Доставки Контента (CDN). Второй
подход проще реализовать, если у вас есть стабильное подключение к интернету, это мы и
будем использовать здесь.

Bootstrap поставляется со стартовым шаблоном, который включает в себя основные


необходимые файлы. В частности, есть четыре, которые мы подключаем:

• Bootstrap.css
• jQuery.js
• Popper.js
• Bootstrap.js

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 212

Вот как должен выглядеть обновленный файл base.html. Как правило, вы должны набирать
все примеры кода самостоятельно, но так как это довольно длинный код, здесь его можно
скопировать и вставить.

Code
<!-- templates/base.html -->

<!doctype html>

<html lang="en">

<head>

<!-- Required meta tags -->

<meta charset="utf- ">

<meta name="viewport" content="width=device-width, initial-scale= , shrink-t\

o-fit=no">

<!-- Bootstrap CSS -->

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/ . . \

/css/bootstrap.min.css" integrity="sha -Gn xqQ aoWXA+ RXPxPg fy IWvTNh E \

XmFcJlSAwiGgFAW/dAiS JXm" crossorigin="anonymous">

<title>Hello, world!</title>

</head>

<body>

<h1>Hello, world!</h1>

<!-- Optional JavaScript -->

<!-- jQuery first, then Popper.js, then Bootstrap JS -->

<script src="https://code.jquery.com/jquery- . . .slim.min.js" integrity="sh\

a -KJ o DKtIkvYIK UENzmM KCkRr/rE /Qpg aAZGJwFDMVNA/GpGFF hXpG KkN" crossorig\

in="anonymous"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/ . . /umd/pop\

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 213

per.min.js" integrity="sha -ApNbgh B+Y QKtv Rn W mgPxhU K/ScQsAP hUibX j fakF\

PskvXusvfa b Q" crossorigin="anonymous"></script>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/ . . /js/bootstrap.mi\

n.js" integrity="sha -JZR Spejh U d jOt vLEHfe/JQGiRRSQQxSfFWpi MquVdAyjUar +\

PVCmYl" crossorigin="anonymous"></script>

</body>

</html>

Если вы снова запустите сервер с помощью python manage.py runserver и обновите домашнюю
страницу по адресу http://127.0.0.1:8000/, вы увидите, что в данный момент изменился
только размер шрифта.

Давайте добавим панель навигации вверху страницы, которая содержит наши ссылки для
домашней страницы, входа в систему, выхода из системы и регистрации. В частности, мы
можем использовать теги if / else в шаблонизаторе Django для добавления некоторой
базовой логики. Мы хотим показать кнопки «войти» и «зарегистрироваться»
пользователям, которые вышли из системы, а кнопки «выйти» и «изменить пароль»
пользователям, которые вошли в систему.

Вот как выглядит код. Опять же, здесь можно копировать / вставлять, поскольку эта книга
посвящена изучению Django, а не HTML, CSS и Bootstrap.

Code

<!-- templates/base.html -->

<!doctype html>

<html lang="en">

<head>

<!-- Required meta tags -->

<meta charset="utf- ">

<meta name="viewport" content="width=device-width, initial-scale= , shrink-t\

o-fit=no">

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 214

<!-- Bootstrap CSS -->

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/ . . \

/css/bootstrap.min.css" integrity="sha -Gn xqQ aoWXA+ RXPxPg fy IWvTNh E \

XmFcJlSAwiGgFAW/dAiS JXm" crossorigin="anonymous">

<title>{% block title %}Newspaper App{% endblock title %}</title>

</head>

<body>

<nav class="navbar navbar-expand-md navbar-dark bg-dark mb- ">

<a class="navbar-brand" href="{% url 'home' %}">Newspaper</a>

<button class="navbar-toggler" type="button" data-toggle="collapse" data-t\

arget="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" ari\

a-label="Toggle navigation">

<span class="navbar-toggler-icon"></span>

</button>

<div class="collapse navbar-collapse" id="navbarCollapse">

{% if user.is_authenticated %}

<ul class="navbar-nav ml-auto">

<li class="nav-item">

<a class="nav-link dropdown-toggle" href="#" id="userMenu" data-to\

ggle="dropdown" aria-haspopup="true" aria-expanded="false">

{{ user.username }}

</a>

<div class="dropdown-menu dropdown-menu-right" aria-labelledby="us\

erMenu">

<a class="dropdown-item" href="{% url 'password_change' %}">Chan\

ge password</a>

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 215

<div class="dropdown-divider"></div>

<a class="dropdown-item" href="{% url 'logout' %}">Log out</a>

</div>

</li>

</ul>

{% else %}

<form class="form-inline ml-auto">

<a href="{% url 'login' %}" class="btn btn-outline-secondary">Log in\

</a>

<a href="{% url 'signup' %}" class="btn btn-primary ml- ">Sign up</a>

</form>

{% endif %}

</div>

</nav>

<div class="container">

{% block content %}

{% endblock %}

</div>

<!-- Optional JavaScript -->

<!-- jQuery first, then Popper.js, then Bootstrap JS -->

<script src="https://code.jquery.com/jquery- . . .slim.min.js" integrity="sh\

a -KJ o DKtIkvYIK UENzmM KCkRr/rE /Qpg aAZGJwFDMVNA/GpGFF hXpG KkN" crossorig\

in="anonymous"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/ . . /umd/pop\

per.min.js" integrity="sha -ApNbgh B+Y QKtv Rn W mgPxhU K/ScQsAP hUibX j fakF\

PskvXusvfa b Q" crossorigin="anonymous"></script>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/ . . /js/bootstrap.mi\

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 216

n.js" integrity="sha -JZR Spejh U d jOt vLEHfe/JQGiRRSQQxSfFWpi MquVdAyjUar +\

PVCmYl" crossorigin="anonymous"></script>

</body>

</html>

Если вы обновите домашнюю страницу на http://127.0.0.1:8000 наша новая навигационная


панель появится волшебным образом!

Homepage with Bootstrap nav logged in

Нажмите на имя пользователя в верхнем правом углу - wsv в моем случае - чтобы увидеть
хорошее выпадающее меню, которое предлагает Bootstrap.

Homepage with Bootstrap nav logged in and dropdown

Если вы нажмете на ссылку «logout», то наша навигационная панель изменится, предложив


ссылки либо «log in», либо «sign up».

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 217

Homepage with Bootstrap nav logged out

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

Homepage mobile with hamburger icon

Обновите домашнюю страницу, и вы увидите ее в действии. Вы даже можете изменить


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

Если вы нажмете кнопку «logout», а затем «log in» в верхней панели навигации, вы также
увидите, что наша страница входа в систему http://127.0.0.1:8000/users/login также выглядит
лучше.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 218

Bootstrap login

Единственное, что отвлекает - это кнопка «Login». Мы можем использовать Bootstrap,


чтобы добавить приятный стиль, например сделать его зеленым и
привлекательным.Измените строку “button” в templates/registration/login.html следующим
образом.

Code

<!-- templates/registration/login.html -->

...

<button class="btn btn-success ml- " type="submit">Login</button>

...

Теперь обновите страницу, чтобы увидеть нашу новую кнопку.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 219

Bootstrap login with new button

Signup Form

Наша страница регистрации по адресу http://127.0.0.1:8000/users/signup/ есть стили


Bootstrap, а также отвлекающий вспомогательный текст. Например, после “Username”
написано “Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.”

Updated navbar logged out

Откуда взялся этот текст? Всякий раз, когда что-то кажется «волшебным» в Django, будьте
уверены, что это определенно не так. Вероятно, код пришел из внутреннего фрагмента
Django.

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

Например, если вы выполните поиск «150 символов или меньше», вы окажетесь на


странице django / contrib / auth / models.py, расположенной, в строке 301.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 220

Текст поставляется как часть приложения auth, в поле имени пользователя для AbstractUser.
Сейчас у нас есть три варианта:

• переопределить существующий help_text


• скрыть help_text
• изменить стиль help_text

Мы выберем третий вариант, так как это хороший способ представить отличный
сторонний пакет django-crispy-forms. Работа с формами является сложной задачей, и
django-crispy-forms упрощает написание DRY кода.

Сначала остановите локальный сервер с Control+c. Затем используйте Pip env для
установки пакета в нашем проекте.

Command Line

(news) $ pipenv install django-crispy-forms

Добавьте новое приложение в список INSTALLED_APPS в settings.py файл. Поскольку


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

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 221

Code

# newspaper_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

# rd Party

'crispy_forms',

# Local

'users',

'pages',

Поскольку мы используем Bootstrap 4, мы также должны добавить эту конфигурацию в


settings.py файл. Это идет в нижней части файла.

Code

# newspaper_project/settings.py

CRISPY_TEMPLATE_PACK = 'bootstrap '

Теперь в нашем шаблоне signup.html мы можем быстро использовать crispy формы. Сначала мы
загружаем crispy_forms_tags сверху, а затем меняем {{ form.as_p }} на {{ form|crispy }}.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 222

Code

<!-- templates/signup.html -->

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %}Sign Up{% endblock %}

{% block content %}

<h2>Sign up</h2>

<form method="post">

{% csrf_token %}

{{ form|crispy }}

<button type="submit">Sign up</button>

</form>

{% endblock %}

Если вы снова запустите сервер с помощью python manage.py runserver и обновите страницу
регистрации, мы увидим новые изменения.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 223

Crispy signup page

Намного лучше. Хотя, как насчет того, чтобы наша кнопка “Sign up” была немного более
привлекательной? В Bootstrap есть все виды стилей кнопок, которые мы можем выбрать.
Давайте использовать “success”, который имеет зеленый фон и белый текст.

Обновите файл signup.html в строке для кнопки регистрации.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 224

Code
<!-- templates/signup.html -->

...

<button class="btn btn-success" type="submit">Sign up</button>

...

Обновите страницу, и вы увидите нашу обновленную работу.

Crispy signup page green button

Следующий шаг

Наше приложение Newspaper начинает выглядеть довольно хорошо. Последний шаг это
настройка смены и сброса пароля.

Группа в ВК "Django_Python"
Chapter 10: Bootstrap 225

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

Группа в ВК "Django_Python"
Глава ��: Изменение и сброс пароля
В этой главе мы завершим поток авторизации нашего приложения Newspaper, добавив
функцию изменения и сброса пароля. Пользователи смогут изменить свой текущий пароль
или, и если они его забыли сбросить его по электронной почте.
Так же как в Django поставляется с встроенным представлением и URL для входа и выхода,
также поставляется и с views/URL смены и сброса пароля. Сначала мы рассмотрим версии
по умолчанию, а затем узнаем, как настроить их с помощью наших собственных шаблонов
Bootstrap и службы электронной почты.

Изменение пароля

Разрешение пользователям менять свои пароли является распространенной функцией на


многих веб-сайтах. Django предоставляет реализацию по умолчанию, которая уже работает
на этом этапе. Чтобы попробовать это, сначала нажмите кнопку “log in”, убедитесь, что вы
вошли в систему. Затем перейдите на страницу “Password change” по адресу
http://127.0.0.1:8000/users/password_change/.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 227

Password change

Введите старый пароль, а затем новый. Затем нажмите кнопку “Change My Password”.

Вы будете перенаправлены на страницу “Password change successful” (“пароль успешно изменен”),

расположенную по адресу:

http://127.0.0.1:8000/users/password_change/done/.

Password change done

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 228

Настройка изменения пароля


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

Command Line

(news) $ touch templates/registration/password_change_form.html

(news) $ touch templates/registration/password_change_done.html

Обновите password_change_form.html следующим кодом.

Code

<!-- templates/registration/password_change_form.html -->

{% extends 'base.html' %}

{% block title %}Password Change{% endblock %}

{% block content %}

<h1>Password change</h1>

<p>Please enter your old password, for security's sake, and then enter your ne\

w password twice so we can verify you typed it in correctly.</p>

<form method="POST">

{% csrf_token %}

{{ form.as_p }}

<input class="btn btn-success" type="submit" value="Change my password">

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 229

</form>

{% endblock %}

В верхней части мы расширяем base.html и устанавливаем title нашей страницы. Поскольку


мы использовали title «block» в нашем файле base.html, мы можем переопределить их здесь.
Форма использует POST, так как мы отправляем данные и csrf_token по соображениям
безопасности. Используя form.as_p, мы просто отображаем в абзацах содержание формы
для сброса пароля по умолчанию. И, наконец, мы включаем кнопку отправки, которая
использует стиль Bootstrap btn btn-success, чтобы сделать его зеленым.Перейдите на
страницу http://127.0.0.1:8000/users/password_change/ и обновите страницу, чтобы увидеть
наши изменения.

New password change form

Следующий шаблон password_change_done.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 230

Code

<!-- templates/registration/password_change_done.html -->

{% extends 'base.html' %}

{% block title %}Password Change Successful{% endblock %}

{% block content %}

<h1>Password change successful</h1>

<p>Your password was changed.</p>

{% endblock content %}

Он также расширяет base.html и включает новый title. Однако на странице нет формы,
только новый текст.Новая страница находится по адресу http://127.0.0.1:8000/users/
password_change/done/.

New password change done

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

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 231

Сброс пароля

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

Единственная необходимая конфигурация указать Django, как отправлять электронные


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

Внизу файла settings.py сделайте следующее однострочное изменение.

Code

# newspaper_project/settings.py

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

И у нас все готово! Джанго позаботится обо всем остальном за нас. Давайте
попробуем.Перейдите по адресу http://127.0.0.1:8000/users/password_reset/, чтобы
просмотреть страницу сброса пароля по умолчанию.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 232

Default password reset page

Убедитесь, что введенный вами адрес электронной почты соответствует одной из ваших учетных
записей. После отправки вы будете перенаправлены на страницу сброса пароля по адресу:
http://127.0.0.1:8000/users/password_reset/done/.

Default password reset done page

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

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 233

Command Line

Content-Type: text/plain; charset="utf- "

MIME-Version: .

Content-Transfer-Encoding: bit

Subject: Password reset on . . . :

From: webmaster@localhost

To: will@wsvincent.com

Date: Thu, Mar : : -

Message-ID: < . . @ . . . .in-addr.arpa>

You're receiving this email because you requested a password reset for your user\

account at . . . : .

Please go to the following page and choose a new password:

http:// . . . : /users/reset/MQ/ up- c db ead /

Your username, in case you've forgotten: wsv

Thanks for using our site!

The . . . : team

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

• "кому” в шестой строке содержит адрес электронной почты пользователя


• ссылка URL содержит безопасный токен, который Django генерирует случайным
образом и может быть использован только один раз

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 234

• Django вежливо напоминает нам о вашем имени пользователя

В ближайшее время мы настроим весь текст электронного письма по умолчанию, но пока


сосредоточимся на поиске ссылки. В сообщении у меня:
http://127.0.0.1:8000/users/reset/MQ/4up-678712c114db2ead7780/
Введите эту ссылку в свой браузер и вы будете перенаправлены на страницу “смены
пароля”.

Default change password page

Теперь введите новый пароль и нажмите кнопку “Change my password”. Последним шагом
будет перенаправление вас на страницу “сброс пароля выполнен”.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 235

Default password reset complete

Чтобы подтвердить, что все сработало, нажмите на ссылку “Log in” и используйте свой
новый пароль. Это должно сработать.

Собственные шаблоны

Как и в случае с “сменой пароля”, нам нужно только создать новые шаблоны, чтобы
настроить внешний вид сброса пароля.

Создайте четыре новых файла шаблонов.

Command Line

(news) $ touch templates/registration/password_reset_form.html

(news) $ touch templates/registration/password_reset_done.html

(news) $ touch templates/registration/password_reset_confirm.html

(news) $ touch templates/registration/password_reset_complete.html

Начните с формы сброса пароля, которым является password_reset_form.html.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 236

Code

<!-- templates/registration/password_reset_form.html -->

{% extends 'base.html' %}

{% block title %}Forgot Your Password?{% endblock %}

{% block content %}

<h1>Forgot your password?</h1>

<p>Enter your email address below, and we'll email instructions for setting a ne\

w one.</p>

<form method="POST">

{% csrf_token %}

{{ form.as_p }}

<input class="btn btn-success" type="submit" value="Send me instructions!">

</form>

{% endblock %}

В верхней части мы расширяем base.html и устанавливаем titles нашей страницы.


Поскольку мы использовали titles «block» в нашем файле base.html, мы можем
переопределить их здесь. Форма использует POST, так как мы отправляем данные и
csrf_token по соображениям безопасности. Используя form.as_p, мы просто отображаем в
абзацах содержание формы для сброса пароля по умолчанию. Наконец, мы вставляем
кнопку отправки и используем стиль Bootstrap btn btn-success, чтобы сделать его зеленым.

Если вы перейдете по адресу http://127.0.0.1:8000/users/password_reset/ вы увидите нашу


новую страницу.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 237

New password reset

Теперь мы можем обновить остальные три страницы. Каждая из них имеет одинаковую
форму расширения base.html, нового заголовка, нового текста контента, а также для
«подтверждения сброса пароля» обновленную форму.

Code

<!-- templates/registration/password_reset_done.html -->

{% extends 'base.html' %}

{% block title %}Email Sent{% endblock %}

{% block content %}

<h1>Check your inbox.</h1>

<p>We've emailed you instructions for setting your password. You should receiv\

e the email shortly!</p>

{% endblock %}

Проверьте изменения, перейдя по ссылке http://127.0.0.1:8000/users/password_reset/done/.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 238

New reset done

Следующая страница подтверждения сброса пароля.

Code

<!-- templates/registration/password_reset_confirm.html -->

{% extends 'base.html' %}

{% block title %}Enter new password{% endblock %}

{% block content %}

<h1>Set a new password!</h1>

<form method="POST">

{% csrf_token %}

{{ form.as_p }}

<input class="btn btn-success" type="submit" value="Change my password">

</form>

{% endblock %}

В командной строке выберите ссылку URL из электронного письма, выведенного на консоль - мое
было http://���.�.�.�:����/users/reset/MQ/�up-������c���db�ead����/ вы увидите
следующее.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 239

New set password

Наконец, вот полный код сброса пароля.

Code

<!-- templates/registration/password_reset_complete.html -->

{% extends 'base.html' %}

{% block title %}Password reset complete{% endblock %}

{% block content %}

<h1>Password reset complete</h1>

<p>Your new password has been set. You can log in now on the <a href="{% url 'lo\

gin' %}">log in page</a>.</p>

{% endblock %}

Вы можете просмотреть его по адресу http://127.0.0.1:8000/users/reset/done/.

Группа в ВК "Django_Python"
Chapter 11: Password Change and Reset 240

New password reset complete

Теперь пользователи могут сбросить свой пароль учетной записи!

Заключение

В следующей главе мы подключим наше приложение Newspaper к почтовой службе


SendGrid, чтобы фактически отправлять наши автоматические электронные письма
пользователям, а не выводить их в нашей консоли командной строки.

Группа в ВК "Django_Python"
Глава ��: Email
На данный момент вы можете чувствовать себя немного перегруженным всей
информацией о конфигурации аутентификации пользователя, которую мы сделали до
этого момента. Это нормально. В конце концов, мы еще даже не создали каких либо
основных функций приложения Newspaper! Все было о настройке пользовательских
учетных записей пользователей и прочем.

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

Теперь мы хотим, чтобы наши электронные письма отправлялись пользователям, а не


просто выводились в консоль командной строки. Нам нужно зарегистрироваться в
SendGrid и обновить ваш settings.py файл. Django позаботится об остальном. Готовы?

SendGrid

SendGrid это популярный сервис для отправки транзакционных писем, поэтому мы будем
использовать его. Django не заботится о том, какой сервис вы выбираете, хотя; вы можете
так же легко использовать MailGun или любой другой сервис по вашему выбору.

На главной странице SendGrid нажмите на большую синюю кнопку “See Plans and Pricing”.

Группа в ВК "Django_Python"
Chapter 12: Email 242

SendGrid homepage

На следующей странице немного прокрутите вниз и найдите в левой части кнопку “Try for Free”.
SendGrid предоставляет бесплатный уровень, который мы можем использовать, хотя они
несколько затрудняют его поиск.

Группа в ВК "Django_Python"
Chapter 12: Email 243

SendGrid pricing

Зарегистрируйте бесплатную учетную запись на следующей странице.

Группа в ВК "Django_Python"
Chapter 12: Email 244

SendGrid new account

Убедитесь, что учетная запись электронной почты, используемая для SendGrid, не


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

После подтверждения вашей новой учетной записи по электронной почте вас попросят
войти в систему и перейти на страницу панели инструментов SendGrid.

Группа в ВК "Django_Python"
Chapter 12: Email 245

SendGrid loggedin

Теперь мы можем настроить наш код Django в файле settings.py. Сначала мы обновляем
почтовый сервер для использования SMTP.

Code

# newspaper_project/settings.py

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

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

Группа в ВК "Django_Python"
Chapter 12: Email 246

Code

# newspaper_project/settings.py

EMAIL_HOST = 'smtp.sendgrid.net'

EMAIL_HOST_USER = 'sendgrid_username'

EMAIL_HOST_PASSWORD = 'sendgrid_password'

EMAIL_PORT =

EMAIL_USE_TLS = True

Убедитесь, что вы используете свое собственное имя пользователя SendGrid для


EMAIL_HOST_USER и пароль для EMAIL_HOST_PASSWORD.

Вот и все. Мы закончили! Перейдите к форме сброса пароля снова на:

http://127.0.0.1:8000/users/password_reset/

Вы должны получить электронное письмо в свой почтовый ящик! Текст будет точно таким
же, как и в консоли командной строки ранее.

Пользовательские письма электронной почты

Текущий текст электронной почты не очень личный, не так ли? Давайте изменим. На этом
этапе я мог бы просто показать вам, какие шаги предпринять, но я думаю, что было бы
полезно, если бы я мог объяснить, как я понял, как это сделать. В конце концов, вы хотите
иметь возможность настраивать все части Django по мере необходимости.

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

Группа в ВК "Django_Python"
Chapter 12: Email 247

Github Django

Используйте панель поиска Github и введите несколько слов из текста электронного


письма. Если вы введете “You’re receiving this email because”, вы попадете на страницу
поиска Github.

Github search

Первый результат - тот, который нам нужен. Он показывает, что код находится по адресу
django / contrib / ad-min / templates / registration / password_reset_email.html. Это означает,
что в приложении contrib файл который нам нужен, называется password_reset_email.html.

Группа в ВК "Django_Python"
Chapter 12: Email 248

Вот текст по умолчанию из исходного кода Django.

Code

{% load i n %}{% autoescape off %}

{% blocktrans %}You're receiving this email because you requested a password res\

et for your user account at {{ site_name }}.{% endblocktrans %}

{% trans "Please go to the following page and choose a new password:" %}

{% block reset_link %}

{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb =uid token=to\

ken %}

{% endblock %}

{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}

{% trans "Thanks for using our site!" %}

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

{% endautoescape %}

Давайте изменим это. Нам нужно создать новый файл password_reset_email.html в нашей
папке registration .

Command Line

(news) $ touch templates/registration/password_reset_email.html

Затем используйте следующий код, который настраивает то, что Django предоставляет
по умолчанию.

Группа в ВК "Django_Python"
Chapter 12: Email 249

Code

<!-- templates/registration/password_reset_email.html -->

{% load i n %}{% autoescape off %}

{% trans "Hi" %} {{ user.get_username }},

{% trans "We've received a request to reset your password. If you didn't make th\

is request, you can safely ignore this email. Otherwise, click the button below \

to reset your password." %}

{% block reset_link %}

{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb =uid token=to\

ken %}

{% endblock %}

{% endautoescape %}

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

Мы приветствуем пользователя по имени благодаря user.get_username. Затем мы


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

Давайте также обновим заголовок темы электронного письма. Для этого мы создадим
новый файл templates / registration / password_reset subject.txt.

Группа в ВК "Django_Python"
Chapter 12: Email 250

Command Line

(news) $ touch templates/registration/password_reset_subject.txt

Затем добавьте следующую строку кода в файл password_reset_subject.txt.

Please reset your password

И у нас все готово. Попробуйте снова наш новый поток, введя новый пароль по адресу
http://127.0.0.1:8000/users/password_reset/. Затем проверьте свою электронную почту, и она
будет иметь наш новый контент и тему.

Заключение

Теперь мы завершили реализацию полного процесса аутентификации пользователя.


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

Группа в ВК "Django_Python"
Глава ��: приложение Newspaper
Пришло время создать наше приложение Newspaper. У нас будет страница статей, на
которой журналисты могут публиковать статьи, установим разрешения, чтобы только
автор статьи мог их редактировать или удалять, и, наконец, добавим возможность другим
пользователям писать комментарии к каждой статье, в которых будет введена концепция
внешних ключей.

Приложение Articles(статьи)
Для начала создайте приложение статей и определите модели нашей базы данных. Не
существует жестких и быстрых правил, касающихся имен ваших приложений, за
исключением того, что вы не можете использовать имя встроенного приложения. Если вы
посмотрите раздел INSTALLED_APPS файла settings.py, вы увидите, какие имена
приложений запрещены: admin, auth, contenttypes, sessions, messages, и staticfiles.. Общее
практическое правило заключается в использовании множественного числа имени
приложения - сообщений, платежей, пользователей и т. д. - если только это не является
явно неправильным, как в случае блога, где единственное число имеет больше смысла.

Start by creating our new articles app.

Command Line

(news) $ python manage.py startapp articles

Затем добавьте его в наш INSTALLED_APPS и обновите часовой пояс, так как мы будем
отмечать время в наших статьях. Вы можете найти свой часовой пояс в списке Википедии.
Например, я живу в Бостоне, штат Массачусетс, который находится в восточном часовом
поясе Соединенных Штатов. Поэтому моя запись - America/New_York.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 252

Code

# newspaper_project/settings.py

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

# rd Party

'crispy_forms',

# Local

'users',

'pages',

'articles', # new

TIME_ZONE = 'America/New_York'

Далее мы определяем нашу модель базы данных, которая содержит четыре поля: заголовок,
тело, дата и автор. Обратите внимание, что мы позволяем Django автоматически
устанавливать время и дату в соответствии с нашей настройкой TIME_ZONE. В поле
автора мы хотим сослаться на нашу пользовательскую модель пользователя
users.CustomUser, которую мы установили в файле settings.py как AUTH_USER_MODEL.
Поэтому, если мы импортируем настройки, мы можем ссылаться на них как
settings.AUTH_USER_MODEL.
Мы также реализуем лучшие практики определения get_absolute_url с самого начала и
метод __str__ для просмотра модели в нашем интерфейсе администратора.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 253

Code

# articles/models.py

from django.conf import settings

from django.db import models

from django.urls import reverse

class Article(models.Model):

title = models.CharField(max_length= )

body = models.TextField()

date = models.DateTimeField(auto_now_add=True)

author = models.ForeignKey(

settings.AUTH_USER_MODEL,

on_delete=models.CASCADE,

def __str__(self):

return self.title

def get_absolute_url(self):

return reverse('article_detail', args=[str(self.id)])

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

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 254

Command Line

(news) $ python manage.py makemigrations articles

(news) $ python manage.py migrate

На этом этапе мне нравится переходить в админку, чтобы поиграться с моделью, прежде
чем создавать urls/views/templates, необходимые для фактического отображения данных на
веб сайте. Но сначала нам нужно обновить admin.py, чтобы наше новое приложение
отобразилось.

Code

# articles/admin.py

from django.contrib import admin

from . import models

admin.site.register(models.Article)

Теперь мы запускаем сервер.

Command Line

(news) $ python manage.py runserver

Перейдите к http://127.0.0.1:8000/admin/ и войдите в систему.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 255

Admin page

Если вы нажмете “Articles” в верхней части страницы, мы сможем ввести некоторые


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

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 256

Admin articles add page

Я добавил три новые статьи, как вы можете видеть на обновленной странице статей.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 257

Admin three articles

Если вы нажмете на отдельную статью, вы увидите, что отображаются title, body, и author, но не
дата. Это потому, что Django автоматически добавила нам дату и поэтому не может быть
изменена администратором. Мы могли бы сделать дату редактируемой - в более сложных
приложениях обычно есть поля create_at и updated_at - но для простоты мы просто покажем
дату создания. Даже если дата здесь не отображается, мы по-прежнему сможем получить к ней
доступ в наших шаблонах, чтобы ее можно было отображать на веб страницах.

URLs и Views

Следующий шаг - настройка URLs и представлений. Пусть наши статьи появятся в разделе
articles/. Импортируйте include во вторую строку и добавьте шаблон URL для статей на
уровне проекта файл urls.py.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 258

Code

# newspaper_project/urls.py

from django.contrib import admin

from django.urls import path, include # new

urlpatterns = [

path('', include('pages.urls')),

path('articles/', include('articles.urls')), # new

path('admin/', admin.site.urls),

path('users/', include('users.urls')),

path('users/', include('django.contrib.auth.urls')),

Далее мы создаем файл articles/urls.py.

Command Line

(news) $ touch articles/urls.py

Затем заполните его нашими маршрутами. Начнем со страницы со списком всех статей в
articles/ которая будет использовать представление ArticleListView.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 259

Code

# articles/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.ArticleListView.as_view(), name='article_list'),

Теперь создайте наше представление, используя встроенный универсальный ListView от Django

Code

# articles/views.py

from django.views.generic import ListView

from . import models

class ArticleListView(ListView):

model = models.Article

template_name = 'article_list.html'

Нам нужно указать только два поля: модель Article и имя нашего шаблона, который будет
article_list.html.

Последний шаг это создать наш шаблон. Мы можем сделать пустой файл из командной
строки.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 260

Command Line

(news) $ touch templates/article_list.html

Bootstrap имеет встроенный компонент под названием Cards , который мы можем


настроить для наших отдельных статей. Напомним, что ListView возвращает объект с
именем object_list, который мы можем перебрать с помощью цикла for.
В каждой статье мы показываем заголовок, текст, автора и дату. Мы даже можем
предоставить ссылки для «редактирования» и «удаления» функций, которые мы еще не
создали.

Code

<!-- templates/article_list.html -->

{% extends 'base.html' %}

{% block title %}Articles{% endblock %}

{% block content %}

{% for article in object_list %}

<div class="card">

<div class="card-header">

<span class="font-weight-bold">{{ article.title }}</span> &middot;

<span class="text-muted">by {{ article.author }} | {{ article.date }}</s\

pan>

</div>

<div class="card-body">

{{ article.body }}

</div>

<div class="card-footer text-center text-muted">

<a href="#">Edit</a> | <a href="#">Delete</a>

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 261

</div>

</div>

<br />

{% endfor %}

{% endblock content %}

Снова запустите сервер с помощью python manage.py runserver и посетите нашу страницу
по адресу http://127.0.0.1:8000/articles/.

Articles page

Не плохо а? Если бы мы захотели проявить фантазию, мы могли бы создать собственный


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

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 262

Редактирование/Удаление

Как мы можем добавить параметры редактирования и удаления? Нам нужны новые urls,
представления и шаблоны. Давайте начнем с urls. Мы можем воспользоваться тем, что
Django автоматически добавляет первичный ключ в каждую базу данных. Поэтому наша
первая статья с первичным ключом 1 будет находиться в article / 1 / edit /, а маршрут удаления
будет находиться в article / 1 / delete /.

Code

# articles/urls.py

from django.urls import path

from . import views

urlpatterns = [

path('', views.ArticleListView.as_view(), name='article_list'),

path('<int:pk>/edit/',

views.ArticleUpdateView.as_view(), name='article_edit'), # new

path('<int:pk>/',

views.ArticleDetailView.as_view(), name='article_detail'), # new

path('<int:pk>/delete/',

views.ArticleDeleteView.as_view(), name='article_delete'), # new

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


основе классов Django для DetailView, UpdateView и DeleteView. Мы указываем, какие поля могут
быть обновлены - заголовок и тело - и куда перенаправлять пользователя после удаления
статьи: article_list.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 263

Code

# articles/views.py

from django.views.generic import ListView, DetailView

from django.views.generic.edit import UpdateView, DeleteView

from django.urls import reverse_lazy

from . import models

class ArticleListView(ListView):

model = models.Article

template_name = 'article_list.html'

class ArticleDetailView(DetailView):

model = models.Article

template_name = 'article_detail.html'

class ArticleUpdateView(UpdateView):

model = models.Article

fields = ['title', 'body', ]

template_name = 'article_edit.html'

class ArticleDeleteView(DeleteView):

model = models.Article

template_name = 'article_delete.html'

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 264

success_url = reverse_lazy('article_list')

Наконец нам нужно добавить наши новые шаблоны. Остановите сервер с помощью Control
+ c и введите следующее.

Command Line

(news) $ touch templates/article_detail.html

(news) $ touch templates/article_edit.html

(news) $ touch templates/article_delete.html

Мы начнем со страницы details , где будут отображаться заголовок, дата, тело и автор со
ссылками для редактирования и удаления. Она также будет ссылаться на все статьи.
Напомним, что тегу url языка шаблонов Django требуется имя URL, а затем передаются все
аргументы. Имя нашего маршрута редактирования - article_edit, и нам нужно передать его
первичный ключ article.pk. Имя маршрута для удаления - article_delete, для него также
требуется первичный ключ article.pk. Наша страница статей представляет собой ListView,
поэтому ей не нужно передавать никаких дополнительных аргументов.

Code

<!-- templates/article_detail.html -->

{% extends 'base.html' %}

{% block content %}

<div class="article-entry">

<h2>{{ object.title }}</h2>

<p>by {{ object.author }} | {{ object.date }}</p>

<p>{{ object.body }}</p>

</div>

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 265

<p><a href="{% url 'article_edit' article.pk %}">Edit</a> | <a href="{% url 'a\

rticle_delete' article.pk %}">Delete</a></p>

<p>Back to <a href="{% url 'article_list' %}">All Articles</a>.</p>

{% endblock content %}

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


чтобы кнопка редактирования была светло-голубой, а кнопка удаления - красной.

Code

<!-- templates/article_edit.html -->

{% extends 'base.html' %}

{% block content %}

<h1>Edit</h1>

<form action="" method="post">{% csrf_token %}

{{ form.as_p }}

<button class="btn btn-info ml- " type="submit">Update</button>

</form>

{% endblock %}

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 266

Code

<!-- templates/article_delete.html -->

{% extends 'base.html' %}

{% block content %}

<h1>Delete</h1>

<form action="" method="post">{% csrf_token %}

<p>Are you sure you want to delete "{{ article.title }}"?</p>

<button class="btn btn-danger ml- " type="submit">Confirm</button>

</form>

{% endblock %}

В качестве заключительного шага мы можем добавить ссылки редактировать и удалять на нашу страницу list в классе div для
card-footer.... Они будут такими же, как те, которые добавлены на страницу detail.

Code

<!-- templates/article_list.html -->

...

<div class="card-footer text-center text-muted">

<a href="{% url 'article_edit' article.pk %}">Edit</a> |

<a href="{% url 'article_delete' article.pk %}">Delete</a>

</div>

...

Хорошо, мы готовы посмотреть нашу работу. Запустите сервер с помощью python


manage.py runserver и перейдите на страницу статей http://127.0.0.1:8000/articles/. Нажмите
на ссылку "edit” в первой статье, и вы будете перенаправлены на:

http://127.0.0.1:8000/articles/1/edit/

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 267

Edit page

Если вы обновите поле “title” и нажмете кнопку update , вы будете перенаправлены на


страницу detail, которая показывает изменение.

Detail page

Если вы нажмете на ссылку “Delete” вы будете перенаправлены на страницу delete.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 268

Delete page

Нажмите страшную красную кнопку для “Delete”, и вы будете перенаправлены на страницу


статей, которая теперь имеет только две записи.

Articles page two entries

Создание страницы
Последним шагом является создание страницы для новых статей, которые мы можем
сделать с Django CreateView. Наши три шага - это создание представления, url-адреса и
шаблона. Этот процесс должен быть уже довольно знакомым.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 269

В нашем файле представлений добавьте Create View к импорту вверху и создайте новый
класс ArticleCreateView Create View, который указывает вашу модель, шаблон и доступные
поля.

Code

# articles/views.py

...

from django.views.generic.edit import CreateView, UpdateView, DeleteView

class ArticleCreateView(CreateView):

model = models.Article

template_name = 'article_new.html'

fields = ['title', 'body', 'author',]

...

Обратите внимание, что у наших полей есть автор, так как мы хотим связать новую статью
с автором, однако после создания статьи мы не хотим, чтобы пользователь мог изменить
автора, поэтому ArticleUpdateView имеет только поля ['title', 'body',].

Обновите файл url новым маршрутом для представления.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 270

Code

# articles/urls.py

...

urlpatterns = [

...

path('new/', views.ArticleCreateView.as_view(), name='article_new'),

...

Затем остановите сервер с Control+c, чтобы создать новый шаблон с именем article_new.html.

Command Line

(news) $ touch templates/article_new.html

И обновите его следующим HTML кодом.

Code

<!-- templates/article_new.html -->

{% extends 'base.html' %}

{% block content %}

<h1>New article</h1>

<form action="" method="post">{% csrf_token %}

{{ form.as_p }}

<button class="btn btn-success ml- " type="submit">Save</button>

</form>

{% endblock %}

В качестве заключительного шага мы должны добавить ссылку на создание новых статей в


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

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 271

Code

<!-- templates/base.html -->

...

<body>

<nav class="navbar navbar-expand-md navbar-dark bg-dark mb- ">

<a class="navbar-brand" href="{% url 'home' %}">Newspaper</a>

{% if user.is_authenticated %}

<ul class="navbar-nav mr-auto">

<li class="nav-item"><a href="{% url 'article_new' %}">+ New</a></li>

</ul>

{% endif %}

<button class="navbar-toggler" type="button" data-toggle="collapse" data-tar\

get="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-\

label="Toggle navigation">

<span class="navbar-toggler-icon"></span>

</button>

...

И почему бы не использовать Bootstrap, чтобы улучшить нашу оригинальную домашнюю


страницу? Мы можем обновить templates/home.html следующим образом.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 272

Code

<!-- templates/home.html -->

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}

<div class="jumbotron">

<h1 class="display- ">Newspaper app</h1>

<p class="lead">A Newspaper website built with Django.</p>

<p class="lead">

<a class="btn btn-primary btn-lg" href="{% url 'article_list' %}" role="butt\

on">View All Articles</a>

</p>

</div>

{% endblock %}

Мы все закончили. Давайте просто подтвердим, что все работает, как ожидалось. Запустите
сервер снова python manage.py runserver и перейдите на нашу домашнюю страницу по адресу:

http://127.0.0.1:8000/.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 273

Homepage with new link in nav

Нажмите на ссылку " + New” в верхней панели навигации, и вы будете перенаправлены на


нашу страницу создания.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 274

Create page

Продолжайте и создайте новую статью. Затем нажмите на кнопку “Save”. Вы будете


перенаправлены на страницу detail . Почему? Потому что в нашем models.py файл мы
устанавливаем метод get_-absolute_url в article_detail. Это хороший подход, потому что если
мы позже изменим шаблон url для страницы detail, скажем, на articles/details/4/,
перенаправление все равно будет работать. Какой бы маршрут не был связан с
article_detail, будет применятся; нет жесткого кодирования самого маршрута.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 275

Detail page

Также обратите внимание, что первичный ключ здесь 4 в URL. Хотя сейчас мы показываем
только три статьи, Django не переупорядочивает первичные ключи только потому, что мы
удалили один. На практике большинство реальных сайтов ничего не удаляют; вместо этого
они «скрывают» удаленные поля, поскольку это облегчает поддержание целостности базы
данных и дает возможность «восстановить» позже, если это необходимо. С нашим текущим
подходом, как только что-то удалено, оно ушло навсегда!

Нажмите на ссылку “All Articles”, чтобы увидеть нашу новую страницу /articles page.

Группа в ВК "Django_Python"
Chapter 13: Newspaper app 276

Updated articles page

Как и ожидалось, внизу есть наша новая статья.

Заключение

Мы создали специальное приложение article с функцией CRUD. Но пока нет никаких


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

Группа в ВК "Django_Python"
Глава ��:Разрешения и авторизация
Есть несколько проблем с нашим текущим сайтом газеты. Во первых, мы хотим, чтобы
наша газета была финансово устойчивой. В будущем мы могли бы добавить платежное
приложение, чтобы взимать плату за доступ, но сейчас нам потребуется, чтобы
пользователь вошел в систему для просмотра любых статей. Это известно как авторизация.
Распространено устанавливать разные правила относительно того, кто имеет право
просматривать области вашего сайта. Обратите внимание, что это отличается от
аутентификации, которая представляет собой процесс регистрации и входа в систему
пользователей. Авторизация ограничивает доступ; Аутентификация позволяет
пользователю зарегистрироваться и войти в систему.
Как развитый веб фреймворк, Django имеет встроенную функциональность для
авторизации, которую мы можем быстро использовать. В этой главе мы ограничим доступ
к различным страницам открыв его только зарегистрированным пользователям. Мы также
добавим разрешения, чтобы только автор статьи мог ее обновить или удалить; На данный
момент это может сделать любой пользователь!

Улучшение CreateView

В настоящее время автором новой статьи может быть задан любой пользователь. Вместо
этого он должен быть автоматически установлен как текущий пользователь. Затем мы
установим права на редактирование / удаление, чтобы такие изменения мог вносить только
автор статьи. Созданный по умолчанию CreateView предоставляет нам множество
функциональных возможностей, но для того, чтобы назначить текущего пользователя
автором, нам нужно его настроить. Мы удалим автора из полей и вместо этого установим
его автоматически с помощью метода form_valid.

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 278

Code

# articles/views.py

...

class ArticleCreateView(CreateView):

model = models.Article

template_name = 'article_new.html'

fields = ['title', 'body']

def form_valid(self, form):

form.instance.author = self.request.user

return super().form_valid(form)

...

Как я узнал, что могу обновить CreateView следующим образом? Ответ я посмотрел на
исходный код и использовал Google. Представления на основе классов отлично подходят
для запуска новых проектов, но когда вы хотите настроить их, необходимо засучить рукава
и начать понимать, что происходит под капотом. Чем больше вы используете и
настраиваете встроенные представления, тем удобнее вам будет выполнять такие
настройки. Скорее всего, то, что вы пытаетесь сделать, уже где-то решено, либо в самом
Django, либо на форуме, подобно Stack Overflow. Не бойтесь просить о помощи!

Теперь перезагрузите браузер и попробуйте нажать на ссылку “+ New” в верхней части


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

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 279

New article link

Авторизация

Существует множество проблем, связанных с отсутствием разрешений в нашем текущем


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

Подумайте, что произойдет, если вышедший из системы пользователь попытается создать


новую статью? Чтобы попробовать, нажмите на свое имя пользователя в правом верхнем
углу навигационной панели, затем выберите “Log out” из выпадающих меню. Ссылка“+
New” исчезает из навигационной панели, но что произойдет, если вы перейдете к ней
напрямую: http://127.0.0.1:8000/articles/new/?

Страница все еще здесь.

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 280

Logged out new

Теперь попробуйте создать новую статью с заголовком и телом. Нажмите на кнопку “Save”.

Create page error

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 281

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

Mixins(Миксины)
Мы явно хотим установить некоторые полномочия, чтобы только зарегистрированные
пользователи могли получить доступ к сайту. Для этого мы можем использовать миксин,
который представляет собой особый вид множественного наследования, который
использует Django, чтобы избежать дублирования кода и сделать возможной настройку.
Например, встроенному универсальному ListView необходим способ возврата шаблона, так
же, как и DetailView, и практически любой другое представление. Вместо того, чтобы
повторять один и тот же код в каждом большом общем представлении, Django разбивает
эту функциональность на «миксин», известный как TemplateRespon-seMixin. И ListView, и
DetailView используют этот миксин для визуализации правильного шаблона.

IЕсли вы прочитаете исходный код Django, который свободно доступен на Github, вы


увидите, что миксины используются повсеместно.

Чтобы ограничить доступ к просмотру оставив его только зарегистрированным


пользователям, Django имеет миксин LoginRequired, который мы можем использовать. Он
мощный и очень краткий.

В существующем файле article / views.py импортируйте его вверху, а затем добавьте


LoginRequiredMixin в наш ArticleCreateView. Убедитесь, что миксин находится слева от
ListView, чтобы он читался первым. Мы хотим, чтобы ListView уже знал, что мы намерены
ограничить доступ.

И это все! Мы закончили.

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 282

Code

# articles/views.py

from django.contrib.auth.mixins import LoginRequiredMixin # new

...

class ArticleCreateView(LoginRequiredMixin, CreateView): # new

...

Вернитесь к URL создания новых сообщений по адресу http://127.0.0.1:8000/articles/new/, и вы


увидите следующую ошибку:

Error page

Что происходит? Django автоматически перенаправил нас в местоположение по умолчанию


для страницы входа в систему, которое находится по адресу / account / login, однако, если
вы помните, в наших URL уровня проекта мы используем users/ в качестве маршрута. Вот
почему наша страница авторизации находится на сайте users / login. Так как же нам
рассказать об этом в ArticleCreateView?

Если вы посмотрите на документацию для LoginRequired mixin, она скажет нам ответ. Мы
можем добавить login_url для переопределения параметра по умолчанию. Мы используем
именованный URL маршрут входа в систему.

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 283

Code

# articles/views.py

...

class ArticleCreateView(LoginRequiredMixin, CreateView):

model = models.Article

template_name = 'article_new.html'

fields = ['title', 'body',]

login_url = 'login' # new

def form_valid(self, form):

form.instance.author = self.request.user

return super().form_valid(form)

Попробуйте ссылку для создания новых сообщений еще раз: http://127.0.0.1:8000/articles/new/.


Теперь она перенаправляет пользователей на страницу входа. Как мы и хотели!

Updating views

Теперь мы видим, что ограничение доступа к представлению - это просто добавление


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

Полностью файл views.py теперь должен выглядеть следующим образом:

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 284

Code

# articles/views.py

from django.contrib.auth.mixins import LoginRequiredMixin

from django.views.generic import ListView, DetailView

from django.views.generic.edit import CreateView, UpdateView, DeleteView

from django.urls import reverse_lazy

from . import models

class ArticleListView(LoginRequiredMixin, ListView):

model = models.Article

template_name = 'article_list.html'

login_url = 'login'

class ArticleDetailView(LoginRequiredMixin, DetailView):

model = models.Article

template_name = 'article_detail.html'

login_url = 'login'

class ArticleUpdateView(LoginRequiredMixin, UpdateView):

model = models.Article

fields = ['title', 'body', ]

template_name = 'article_edit.html'

login_url = 'login'

Группа в ВК "Django_Python"
Chapter 14: Permissions and Authorization 285

class ArticleDeleteView(LoginRequiredMixin, DeleteView):

model = models.Article

template_name = 'article_delete.html'

success_url = reverse_lazy('article_list')

login_url = 'login'

class ArticleCreateView(LoginRequiredMixin, CreateView):

model = models.Article

template_name = 'article_new.html'

fields = ['title', 'body', ]

login_url = 'login'

def form_valid(self, form):

form.instance.author = self.request.user

return super().form_valid(form)

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

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

Группа в ВК "Django_Python"
Глава ��: Комментарии
Есть два способа добавить комментарии на сайт нашей газеты. Первый - создать
специальное приложение comments и связать его с articles, однако на данный момент это
похоже на чрезмерное проектирование. Вместо этого мы можем просто добавить
дополнительную модель comment в наше приложение articles и связать ее с моделью Article
с помощью внешнего ключа. К концу этой главы пользователи смогут оставлять
комментарии к статьям других пользователей.

Модель

Для начала мы можем добавить еще одну таблицу в нашу существующую базу данных под
названием Comment. Эта модель будет иметь связь внешнего ключа многие-к-одному к Article:
одна статья может иметь много комментариев, но не наоборот. Традиционно поле
внешнего ключа называется просто моделью, на которую оно ссылается, поэтому это поле
будет называться article. Остальные два поля будут comment и author.

Откройте файл articles/models.py и под существующим кодом добавьте следующее.

Группа в ВК "Django_Python"
Chapter 15: Comments 287

Code

# articles/models.py

...

class Comment(models.Model):

article = models.ForeignKey(Article, on_delete=models.CASCADE)

comment = models.CharField(max_length= )

author = models.ForeignKey(

settings.AUTH_USER_MODEL,

on_delete=models.CASCADE,

def __str__(self):

return self.comment

def get_absolute_url(self):

return reverse('article_list')

Наша модель Comment также имеет метод __str__ и метод get_absolute_url, который
возвращает на страницу статьи.

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

Группа в ВК "Django_Python"
Chapter 15: Comments 288

Command Line

(news) $ python manage.py makemigrations articles

(news) $ python manage.py migrate

Admin
После создания новой модели хорошо поиграть с ней в приложении администратора,
прежде чем отображать ее на нашем фактическом веб сайте. Добавьте Comment к вашему
файлу admin.py что бы он был виден.

Code
# articles/admin.py

from django.contrib import admin

from . import models

admin.site.register(models.Article)

admin.site.register(models.Comment)

Запустите сервер с помощью python manage.py runserver и перейдите на нашу главную страницу
админки http://127.0.0.1:8000/admin/

Группа в ВК "Django_Python"
Chapter 15: Comments 289

Admin page with Comments

Под нашим приложением “Articles” вы увидите две таблицы: Comments и Articles. Нажмите
на кнопку “+ Add” рядом с Comments. Вы увидите, что под статьей находится выпадающий
список существующих статей, то же самое для автора, и есть текстовое поле рядом с
Comment.

Группа в ВК "Django_Python"
Chapter 15: Comments 290

Admin Comments

Выберите статью, напишите комментарий, а затем выберите автора, который не является


вашим суперпользователем, возможно, testuser, как я сделал на картинке. Затем нажмите на
кнопку “Save”.

Вы должны увидеть свой комментарий на странице “Comments”.

Группа в ВК "Django_Python"
Chapter 15: Comments 291

Admin Comment One

На этом этапе мы можем добавить дополнительное поле администратора, чтобы увидеть комментарий и
статью на этой странице. Но не лучше ли просто просмотреть все модели комментариев, связанные с одной
моделью публикации? Оказывается, мы это можем ,с помощью функции Django admin называемой inlines,

которая отображает внешние ключевые связи приятным визуальным способом.

Используются два основных встроенных представления: TabularInline и StackedInline.


Единственное различие между ними - это шаблон для отображения информации. В
TabularInline все поля модели отображаются в одной строке, в то время как в StackedInline
каждое поле имеет свою собственную строку. Мы реализуем оба, так что вы сами можете
решить, какой из них вам выбрать.

Обновите articles/admin.py следующим образом в текстовом редакторе.

Группа в ВК "Django_Python"
Chapter 15: Comments 292

Code

# articles/admin.py

from django.contrib import admin

from . import models

class CommentInline(admin.StackedInline):

model = models.Comment

class ArticleAdmin(admin.ModelAdmin):

inlines = [

CommentInline,

admin.site.register(models.Article, ArticleAdmin)

admin.site.register(models.Comment)

Теперь вернитесь на главную страницу администратора по адресу http://127.0.0.1:8000/admin/ и


нажмите “Articles”. Выберите статью, для которой вы только что добавили комментарий, в
моем случае была “Local news”.

Группа в ВК "Django_Python"
Chapter 15: Comments 293

Admin change page

Лучше, не правда ли? Мы можем просматривать и изменять все связанные статьи и


комментарии в одном месте.
Хотя лично я предпочитаю использовать TabularInline, поскольку он показывает больше
информации в меньшем пространстве. Чтобы переключиться на него, нам нужно только
изменить ваш CommentInline от admin.StackedInline к admin.TabularInline.

Группа в ВК "Django_Python"
Chapter 15: Comments 294

Code

# articles/admin.py

from django.contrib import admin

from . import models

class CommentInline(admin.TabularInline):

model = models.Comment

class ArticleAdmin(admin.ModelAdmin):

inlines = [

CommentInline,

admin.site.register(models.Article, ArticleAdmin)

admin.site.register(models.Comment)

Обновите страницу администратора и вы увидите новое изменение: все поля для каждой
модели отображаются в одной строке.

Группа в ВК "Django_Python"
Chapter 15: Comments 295

TabularInline page

Намного лучше. Теперь нам нужно обновить шаблон для отображения комментариев.

Template(шаблон)
Поскольку Comment находится в нашем существующем приложении articles , нам нужно
только обновить существующие шаблоны для article_list.html и article_detail.html для
отображения нашего нового контента. Нам не нужно создавать новые шаблоны и возиться
с urls и представлениями.
То, что мы хотим сделать, это отобразить все комментарии, связанные с конкретной
статьей. Это называется “query”, поскольку мы запрашиваем у базы данных определенный
бит информации. В нашем случае, работая с внешним ключом, мы хотим проследить
обратную связь: в каждой статье ищем связанные модели Comment.

Группа в ВК "Django_Python"
Chapter 15: Comments 296

Django имеет встроенный синтаксис, который мы можем использовать известный как


FOO_set, где FOO - это имя исходной модели в нижнем регистре. Таким образом, для нашей

модели Article мы можем использовать article_set для доступа ко всем экземплярам модели.

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

Для начала добавьте атрибут related_name в нашу модель Comment. Хорошим значением
по умолчанию является множественное число модели с ForeignKey.

Code

# articles/models.py

...

class Comment(models.Model):

article = models.ForeignKey(

Article,

on_delete=models.CASCADE,

related_name='comments' # new

Поскольку мы только что внесли изменения в нашу модель базы данных, нам нужно
создать файл миграции и обновить базу данных. Остановите локальный сервер с помощью
Control+c и выполните следующие две команды. Затем снова запустите сервер, мы будем
использовать его в ближайшее время.

Группа в ВК "Django_Python"
Chapter 15: Comments 297

Command Line

(news) $ python manage.py makemigrations articles

(news) $ python manage.py migrate

(news) $ python manage.py runserver

Понимание запросов занимает некоторое время, поэтому не беспокойтесь, если идея


обратных связей запутана. Я покажу вам, как реализовать код по желанию. И как только вы
освоите эти основные примеры, вы сможете изучить, как фильтровать свои наборы
запросов очень подробно, чтобы они возвращали именно ту информацию, которую вы
хотите.

В нашем файле article_list.html мы можем добавить наши комментарии в card-footer. Обратите


внимание, что я перенес наши изменения и удалил ссылки в card-body. Чтобы получить
доступ к каждому комментарию, мы вызываем article.comments.all, что означает сначала
посмотреть на модель article , а затем comments, которые являются связанным именем всей
модели Comment, и выбрать все включенные. Может потребоваться некоторое время,
чтобы привыкнуть к этому синтаксису для ссылки на данные внешнего ключа в шаблоне!

Code

<!-- template/article_list.html -->

{% extends 'base.html' %}

{% block title %}Articles{% endblock %}

{% block content %}

{% for article in object_list %}

<div class="card">

<div class="card-header">

<span class="font-weight-bold">{{ article.title }}</span> &middot;

<span class="text-muted">by {{ article.author }} | {{ article.date }}</s\

Группа в ВК "Django_Python"
Chapter 15: Comments 298

pan>

</div>

<div class="card-body">

<p>{{ article.body }}</p>

<a href="{% url 'article_edit' article.pk %}">Edit</a> |

<a href="{% url 'article_delete' article.pk %}">Delete</a>

</div>

<div class="card-footer">

{% for comment in article.comments.all %}

<p>

<span class="font-weight-bold">{{ comment.author }} &middot;</span>

{{ comment }}

</p>

{% endfor %}

</div>

</div>

<br />

{% endfor %}

{% endblock content %}

Если мы обновим страницу articles по адресу http://127.0.0.1:8000/articles/, мы увидим наш


новый комментарий, отображаемый на странице.

Группа в ВК "Django_Python"
Chapter 15: Comments 299

Articles page with comments

Эй! Это работает. Мы можем видеть оба комментария, перечисленные под сообщением.

Заключение

С большим количеством времени мы бы сосредоточились на формах, чтобы пользователь


мог написать новую статью прямо на странице articles , а также добавить комментарии. Но
главная цель этой главы - показать, как работают связи с внешними ключями в Django.
Наше приложение Newspaper теперь завершено. Оно имеет надежный поток
аутентификации пользователя, включая использование электронной почты для сброса
паролей. Мы также используем CustomUser, поэтому, если мы хотим добавить
дополнительные поля в нашу пользовательскую модель, это так же просто, как добавить
дополнительное поле.

Группа в ВК "Django_Python"
Chapter 15: Comments 300

У нас уже есть поле возраста для всех пользователей, которое в настоящее время
устанавливается в 0 по умолчанию. Если бы мы хотели, мы могли бы добавить возрастное
раскрывающееся меню в форму регистрации и ограничить доступ пользователей только
для пользователей старше 13 лет. Или мы можем предложить скидки пользователям
старше 65 лет. Все, что мы захотим сделать с нашей CustomUser моделью, является опцией.

Большинство веб-разработок следуют тем же шаблонам и с помощью веб фреймворка,


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

Группа в ВК "Django_Python"
Заключение
Поздравляем с окончанием Django для начинающих! Начав с абсолютного нуля, мы
создали пять различных веб приложений с нуля. Мы рассмотрели все основные функции
Django: шаблоны, представления, urls, пользователей, модели, безопасность, тестирование
и развертывание. Теперь у вас есть знания, чтобы идти дальше и создавать свои
собственные современные веб сайты с Django.
Как и в случае с любым новым навыком, важно практиковать и применять то, что вы
только что узнали. Функциональность CRUD в нашем блоге и газете распространена во
многих, многих других веб приложениях. Например, можно ли создать веб приложение
списка задач? У вас уже есть все необходимые инструменты.
Веб разработка это очень глубокая область, и есть еще многое, что узнать о том, что Django
может предложить. Например, более продвинутый проект Django, скорее всего, будет
использовать несколько файлов настроек, переменных и PostgreSQL локально вместо
SQLite. Он может даже использовать сторонние пакеты, такие как django-allauth для
социальной аутентификации, django-debug-toolbar для отладки и django-extensions для
дополнительных вкусностей.
Лучший способ узнать больше о Django и веб разработке в целом это подумать о проекте,
который вы хотите построить, а затем шаг за шагом узнать, что вам нужно для его
завершения. Дополнительным ресурсом, который может помочь, является DjangoX,
который является реальным стартовым проектом, который имеет социальную
аутентификацию и многое другое.

Вы также можете подписаться на рассылку Django для начинающих для периодических


обновлений нового контента и скидок на будущие книги.

И последний ресурс мой личный сайт, wsvincent.com, который регулярно обновляется и


содержит статьи о некоторых из этих передовых методов:

Группа в ВК "Django_Python"
Conclusion 302

• Django Social Authentication


• Django Login Mega-Tutorial
• Django, PostgreSQL, and Docker
• Django Rest Framework Tutorial
• Django Rest Framework with React

Ресурсы Django

Чтобы продолжить изучение Джанго, я рекомендую работать через следующие бесплатные


онлайн уроки:

• Official Polls Tutorial


• Django Girls Tutorial
• MDN: Django Web Framework
• A Complete Beginner’s Guide to Django

Я также настоятельно рекомендую Two Scoops of Django 1.11: Best Practices for the
Django Web Framework, который является текущей Библией лучших практик для
разработчиков Django.

Книги Python

Если вы новичок в Python, есть несколько отличных книг от начинающих до продвинутых


Pythonistas:
• Python Crash Course это фантастическое введение в Python, которое также
проведет вас через три реальных проекта, включая приложение Django.
• Think Python одновременно вводит в основы Python и информатики.

Группа в ВК "Django_Python"
Conclusion 303

• Automate the Boring Stuff это еще одно отличное руководство по обучению и
использованию Python в реальных условиях.

• The Hitchhiker’s Guide to Python содержит рекомендации по программированию на Python.


• Python Tricks демонстрирует, как писать Python код.
• Effective Python отличное руководство не только по Python, но и по
программированию в целом.
• Fluent Python удивительно и обеспечивает глубокое понимание языка Python.

Блоги

Эти сайты предоставляют регулярные высококачественные статьи по Python и веб-


разработке.

• Real Python
• Dan Bader
• Trey Hunner
• Full Stack Python
• Ned Batchelder
• Armin Ronacher
• Kenneth Reitz
• Daniel Greenfeld

Обратная связь

Если вы прошли через всю книгу, мне бы очень хотелось услышать ваши мысли. Что тебе
понравилось или не понравилось? Какие области были особенно сложными? А какой
новый контент вы хотели бы видеть? Со мной можно связаться по адресу
will@wsvincent.com.

Группа в ВК "Django_Python"
От переводчика.

Перевод не является профессиональным, прошу сильно не пинать


эта книга мой первый опыт в переводе книг, но я старался
максимально точно передать содержимое на русском языке.
Изначально переводил для себя фрагментами как шпаргалки,
которые позже были собраны воедино. Надеюсь труд автора и мой не
будет напрасным для вас и вы получите необходимые знания.
Вступайте в группу "в контакте" https://vk.com/Python_Django_RU
делитесь своим мнением, обсуждайте и предлагайте новые книги к
переводу.

И если вы можете помочь с переводом других книг не стесняйтесь


предлагать помощь, вместе прогресс будет быстрей.

Группа в ВК "Django_Python"

Вам также может понравиться