Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Здесь на хабре много различных инструкций по использованию Django. Эти инструкции часто включают много кода и представляют последовательность шагов, которые нужно сделать, чтобы создать проект.
Когда я начинал изучать Django и Wagtail по таким инструкциям, меня часто смущало, что пара команд создает кучу непонятных файлов (особенно на самом старте). Последующее описание этих файлов в инструкциях содержало слишком много деталей, которые трудно было усвоить за раз.
В этом посте я бы хотел посмотреть на Django с очень «философского вида» — минимум кода, максимум общих фактов. Думаю, что такой взгляд поможет тем, кто хочет начать изучать Django но теряется на старте.
Хочу также сказать, что не являюсь профессионалом по части веб-программирования — я в этой области скорее любитель, которого интересуют исключительно личные проекты — один из них сайт по расшифровке данных ДНК тестов https://ru.bezoder.com — написан на Wagtail.
Сначала давайте вспомним, что сайт в интернете это просто программа, которая, возможно, работает почти на таком же компьютере, что находится перед вами.
Ваш компьютер (телефон и т.п.) посылает какой-то запрос к чужому компьютеру в интернет, тот его обрабатывает и отвечает. При обработке чужой компьютер, возможно, делает запрос или производит запись в базу данных. Теперь представим, что необходимо запрограммировать компьютер в интернете, чтобы он правильно обрабатывал каждый запрос.
Это можно сделать вообще на каком угодно языке программирования — вы получаете запрос и на его основе что-то выполняете. Но представьте сколько может быть вариантов как запрограммировать этот компьютер — их может быть бесконечно много! Например, можно написать функцию что-то вроде:
Думаю, понятно, что это был бы ужасный вариант программирования.
Нам нужно сделать все так, чтобы код был читаемым, безопасным, легко дополняемым, использовал какие-то возможности языка, на котором написан…
С таким набором задач нужно придумать какую-то концепцию.
Django предлагает все разделить на "слои". Слои отвечают за разные составляющие вашей программы. Между слоями есть связь, но она не затрудняет разработку каждого слоя изолированно (без большого внимания к другим слоям) — в Django это называется loose coupling. Вот несколько важных слоев Django:
Django дает инструменты для создания таких слоев и функционирование программы заключается в обмене данными между слоями.
Тут я немного подробнее остановлюсь на слоях Модели, Виды и Шаблоны.
Первый и, наверно, самый важный слой это модели(models) — отвечает за базу данных. База данных это много всяких таблиц — например, может быть таблица «пользователи» такого вида:
Как видите, в базе каждая строка это запись, относящаяся к пользователю сайта. В строке есть данные различного типа — в нашем случае числа и текст.
Распространенным языком баз данных является SQL — определенными командами вы можете создавать новые таблицы в базе или вносить и получать данные в и из существующих таблиц.
У SQL есть уязвимости — подробнее. Вкратце — если определенным образом расставить кавычки и точки с запятой в данных, которые отправляются в SQL команду, часть этих данных может быть интерпретирована как составляющая SQL команды.
Django берет всю головную боль, связанную с проблемами SQL на себя — вам даже не надо знать SQL, чтобы пользоваться Django, от вас нужен только python — Django сам сформирует SQL команды для создания таблиц, поиска и записи данных в таблицы и все это будет безопасно.
Идея Django в том, что классы на python повторяют структуру таблиц вашей базы данных.
То есть, для таблицы выше я могу создать класс в python что-то вроде:
но как связать такой класс с базой данных? Вот тут начинается магия Django:
Вы просто используете django.db.models.Model чтобы создать класс, далее каждое поле в вашем классе это также поле, взятое из django.db.models. В моем случае поле name это текстовое поле CharField, поле karma это число float. Список всех полей (Field types) есть в официальной документации.
У каждого поля есть опции (Field options) — в коде выше опция это max_length = 20. Опции зависят от полей, которые вы создаете в базе — например, max_length = 20 это максимальная длина в символах поля name в базе. В документации по ссылке выше также описаны опции для всех полей.
На основе этого кода Django сам создаст таблицу в базе данных и то, что я назвал полями в классе будут столбцами в этой таблице. Django дает вам также удобные команды в python как получать или записывать значения в базу данных. Все делается с помощью методов models.Model а также абстракции «Manager», отвечающей в Django за коммуникацию с базой данных (в данном посте я эти абстракции детально не рассматриваю). Например, CustomUser.objects.filter(name=«Михаил») вернет всех пользователей с именем «Михаил».
Такая связь между строками в базе данных и объектами (экземплярами, инстансами) в Python называется Object-relational mapping — в нашем случае Django-ORM.
А наши модели повторяют структуру базы данных и при этом являются классами в Python. Это значит, что к моделям (классы в Python) можно добавить методы. Например, продолжая логику сайта хабр, я могу добавить метод для изменения кармы:
Тут other — это другой пользователь. Как вы знаете здесь определенная логика добавления кармы. Всю эту логику я могу, например, создать в указанном методе.
В Django вы думаете какие таблицы хотите создать в своей базе и потом просто создаете классы python по примеру выше.
Следующим важным, на мой взгляд, слоем является слой видов (views). Ваши модели это некоторые абстракции, с которыми вам удобно работать или они интуитивно понятны. Но, когда вы хотите что-то показать пользователям, то, возможно, вас будут интересовать иные абстракции.
Например, вы создали три модели в Django: CustomUser, Article и Advertisement с разными полями. Модель Article это статья сайта, Advertisement — это реклама, которую вы показываете на сайте, CustomUser — зарегистрированный пользователь сайта.
Когда вы захотите создать вебстраницу со статьей, то вам понадобятся данные сразу из нескольких ваших моделей — разумеется вы хотите показать все поля в самой статье (название, содержание и т.д.), вы, скорее всего, также хотите показать какую-то рекламу рядом с этой статьей. Причем реклама зависит не от содержания статьи а от поведения пользователя CustomUser. При таком подходе будет нужна какая-то логика — как собирать данные. Так, слой view в данном случае и будет подходящим местом для этой логики. Тут можно собрать все данные, которые будут относиться к тому, что вы хотите показать.
Есть два типа видов view в Django — функциональный и классовый.
Функциональный вид это просто Python функция с аргументом request — это запрос к вашему сайту. В нем содержится информация о пользователе, типе запроса и многом другом. На основе этой информации вы формируете ответ и возвращаете его в своей функции.
Еще один тип view — классовый. Он позволяет создавать виды не на основе функций, а виды как экземпляры классов. Тут Django предоставляет также кучу всяких облегчающих жизнь классов и функций. Предположим, вы хотите создать вид на основе статьи Article:
Классовый вид на основе DetailView автоматически соберет всю информацию модели Article и затем отправит ее в следующий слой Django:
В коде выше template_name это переменная для названия html шаблона, который будет использован для формирования веб страницы, которая и будет показана пользователю. Вот пример кода из такого шаблона:
{{ article.title }} и {{ article.content }} это название статьи и ее содержание, заключенные в html теги. title и content повторяют название полей модели Article, которую вы создали в слое Модели. Слово article мы указали в context_object_name в виде. В результате обработки Django вставит соответствующие поля из Article в шаблон.
Это общий взгляд на некоторые Django слои. Описанная концепция позволяет разделить отдельные блоки программы. В слое модели вы создаете удобные абстракции вашей базы данных, в слое виды вы решаете, какие данные вы хотите показать, и в слое шаблоны вы создаете уже дизайн ваших страниц на основе html и добавляете в шаблоны немного логики с помощью языка Jinja — это из примера с фигурными скобками — {{ article.name }}.
Я тут не затронул довольно много важных тем — например связи между вашими моделями. У каждой статьи может быть один автор или несколько авторов — Django с легкостью справится с любым из перечисленных вариантов, и с помощью одной строки в Python вы сможете получить автора статьи или же коллекцию авторов в виде экземпляров класса Автор, созданного на основе models.Model.
Если вы создаете какое-то сложное приложение с кучей Моделей, видов и т.п. то это огромное количество кода надо как-то разбить на отдельные файлы. И файлы желательно организовать по папкам так, чтобы файл с моделями статьи был в той же папке что и виды статьи.
Вот тут приходит еще одна ключевая идея Django — приложения, которые заслуживают отдельного поста.
Когда я начинал изучать Django и Wagtail по таким инструкциям, меня часто смущало, что пара команд создает кучу непонятных файлов (особенно на самом старте). Последующее описание этих файлов в инструкциях содержало слишком много деталей, которые трудно было усвоить за раз.
В этом посте я бы хотел посмотреть на Django с очень «философского вида» — минимум кода, максимум общих фактов. Думаю, что такой взгляд поможет тем, кто хочет начать изучать Django но теряется на старте.
Хочу также сказать, что не являюсь профессионалом по части веб-программирования — я в этой области скорее любитель, которого интересуют исключительно личные проекты — один из них сайт по расшифровке данных ДНК тестов https://ru.bezoder.com — написан на Wagtail.
Сначала давайте вспомним, что сайт в интернете это просто программа, которая, возможно, работает почти на таком же компьютере, что находится перед вами.
Ваш компьютер (телефон и т.п.) посылает какой-то запрос к чужому компьютеру в интернет, тот его обрабатывает и отвечает. При обработке чужой компьютер, возможно, делает запрос или производит запись в базу данных. Теперь представим, что необходимо запрограммировать компьютер в интернете, чтобы он правильно обрабатывал каждый запрос.
Это можно сделать вообще на каком угодно языке программирования — вы получаете запрос и на его основе что-то выполняете. Но представьте сколько может быть вариантов как запрограммировать этот компьютер — их может быть бесконечно много! Например, можно написать функцию что-то вроде:
Если запрос == google.ru:
ответ "Привет"
Если запрос == google.de:
ответ "Hallo"
...
Думаю, понятно, что это был бы ужасный вариант программирования.
Нам нужно сделать все так, чтобы код был читаемым, безопасным, легко дополняемым, использовал какие-то возможности языка, на котором написан…
С таким набором задач нужно придумать какую-то концепцию.
Концепция Django
Django предлагает все разделить на "слои". Слои отвечают за разные составляющие вашей программы. Между слоями есть связь, но она не затрудняет разработку каждого слоя изолированно (без большого внимания к другим слоям) — в Django это называется loose coupling. Вот несколько важных слоев Django:
- Модели (Models) это слой ваших данных и то как вы их храните их в базе данных
- Виды (views) этот слой собирает все данные которые необходимо для создания веб страниц или обработки данных, отправленных через формы
- Шаблоны (Templates) этот слой получает данные из видов и отображает их на вебстранице (в этом слое вы работаете уже с html)
- Ссылки (url) этот слой организует работу ссылок на вашем сайте на какую ссылку нужно создавать какой вид и какой шаблон
- Формы (Forms) этот слой помогает создавать и обрабатывать веб формы для данных от пользователей
- ...
Django дает инструменты для создания таких слоев и функционирование программы заключается в обмене данными между слоями.
Тут я немного подробнее остановлюсь на слоях Модели, Виды и Шаблоны.
Слой модели
Первый и, наверно, самый важный слой это модели(models) — отвечает за базу данных. База данных это много всяких таблиц — например, может быть таблица «пользователи» такого вида:
ID |
name |
surname |
karma |
1 |
Михаил |
Трунов |
2 |
Как видите, в базе каждая строка это запись, относящаяся к пользователю сайта. В строке есть данные различного типа — в нашем случае числа и текст.
Распространенным языком баз данных является SQL — определенными командами вы можете создавать новые таблицы в базе или вносить и получать данные в и из существующих таблиц.
У SQL есть уязвимости — подробнее. Вкратце — если определенным образом расставить кавычки и точки с запятой в данных, которые отправляются в SQL команду, часть этих данных может быть интерпретирована как составляющая SQL команды.
Django берет всю головную боль, связанную с проблемами SQL на себя — вам даже не надо знать SQL, чтобы пользоваться Django, от вас нужен только python — Django сам сформирует SQL команды для создания таблиц, поиска и записи данных в таблицы и все это будет безопасно.
Идея Django в том, что классы на python повторяют структуру таблиц вашей базы данных.
То есть, для таблицы выше я могу создать класс в python что-то вроде:
class User:
def __init__(id, name, surname, karma)
self.id = id
self.name = name
...
но как связать такой класс с базой данных? Вот тут начинается магия Django:
# мы просто импортируем модуль models из Django
from django.db import models
# создаем класс, который наследует models.Model
class CustomUser(models.Model):
# создаем поля для базы данных в классе
name = models.CharField(max_length = 20)
...
karma = models.FloatField(...)
...
# Еще одна таблица в базе данных - статья
class Article(models.Model):
# создаем название и содержание статьи
title = models.CharField(...)
content = models.TextField(...)
...
Вы просто используете django.db.models.Model чтобы создать класс, далее каждое поле в вашем классе это также поле, взятое из django.db.models. В моем случае поле name это текстовое поле CharField, поле karma это число float. Список всех полей (Field types) есть в официальной документации.
У каждого поля есть опции (Field options) — в коде выше опция это max_length = 20. Опции зависят от полей, которые вы создаете в базе — например, max_length = 20 это максимальная длина в символах поля name в базе. В документации по ссылке выше также описаны опции для всех полей.
На основе этого кода Django сам создаст таблицу в базе данных и то, что я назвал полями в классе будут столбцами в этой таблице. Django дает вам также удобные команды в python как получать или записывать значения в базу данных. Все делается с помощью методов models.Model а также абстракции «Manager», отвечающей в Django за коммуникацию с базой данных (в данном посте я эти абстракции детально не рассматриваю). Например, CustomUser.objects.filter(name=«Михаил») вернет всех пользователей с именем «Михаил».
Такая связь между строками в базе данных и объектами (экземплярами, инстансами) в Python называется Object-relational mapping — в нашем случае Django-ORM.
А наши модели повторяют структуру базы данных и при этом являются классами в Python. Это значит, что к моделям (классы в Python) можно добавить методы. Например, продолжая логику сайта хабр, я могу добавить метод для изменения кармы:
from django.db import models
class CustomUser(models.Model):
...
# пример метода в модели Django
def change_karma(self, other):
....
if ...:
self.karma = self.karma +1
...
else:
...
Тут other — это другой пользователь. Как вы знаете здесь определенная логика добавления кармы. Всю эту логику я могу, например, создать в указанном методе.
В Django вы думаете какие таблицы хотите создать в своей базе и потом просто создаете классы python по примеру выше.
Слой виды
Следующим важным, на мой взгляд, слоем является слой видов (views). Ваши модели это некоторые абстракции, с которыми вам удобно работать или они интуитивно понятны. Но, когда вы хотите что-то показать пользователям, то, возможно, вас будут интересовать иные абстракции.
Например, вы создали три модели в Django: CustomUser, Article и Advertisement с разными полями. Модель Article это статья сайта, Advertisement — это реклама, которую вы показываете на сайте, CustomUser — зарегистрированный пользователь сайта.
Когда вы захотите создать вебстраницу со статьей, то вам понадобятся данные сразу из нескольких ваших моделей — разумеется вы хотите показать все поля в самой статье (название, содержание и т.д.), вы, скорее всего, также хотите показать какую-то рекламу рядом с этой статьей. Причем реклама зависит не от содержания статьи а от поведения пользователя CustomUser. При таком подходе будет нужна какая-то логика — как собирать данные. Так, слой view в данном случае и будет подходящим местом для этой логики. Тут можно собрать все данные, которые будут относиться к тому, что вы хотите показать.
Есть два типа видов view в Django — функциональный и классовый.
Функциональный вид это просто Python функция с аргументом request — это запрос к вашему сайту. В нем содержится информация о пользователе, типе запроса и многом другом. На основе этой информации вы формируете ответ и возвращаете его в своей функции.
Еще один тип view — классовый. Он позволяет создавать виды не на основе функций, а виды как экземпляры классов. Тут Django предоставляет также кучу всяких облегчающих жизнь классов и функций. Предположим, вы хотите создать вид на основе статьи Article:
# импорт полезного класса
from django.views.generic import DetailView
# импорт созданной в другом файле модели Article
from .models import Article
# создание классового вида
class ArticleDetailView(DetailView):
# модель на основе которой мы хотим создать вид
model = Article
# имя, которое будет использовано в html шаблоне (это другой слой - рассмотрим далее)
context_object_name = 'article'
# имя html шаблона, на основе которого будет создана веб страница
template_name = 'article/article_detail.html'
Классовый вид на основе DetailView автоматически соберет всю информацию модели Article и затем отправит ее в следующий слой Django:
Слой шаблоны
В коде выше template_name это переменная для названия html шаблона, который будет использован для формирования веб страницы, которая и будет показана пользователю. Вот пример кода из такого шаблона:
<h1>{{ article.title }}</h1>
<div>{{ article.content }}</div>
{{ article.title }} и {{ article.content }} это название статьи и ее содержание, заключенные в html теги. title и content повторяют название полей модели Article, которую вы создали в слое Модели. Слово article мы указали в context_object_name в виде. В результате обработки Django вставит соответствующие поля из Article в шаблон.
Резюме
Это общий взгляд на некоторые Django слои. Описанная концепция позволяет разделить отдельные блоки программы. В слое модели вы создаете удобные абстракции вашей базы данных, в слое виды вы решаете, какие данные вы хотите показать, и в слое шаблоны вы создаете уже дизайн ваших страниц на основе html и добавляете в шаблоны немного логики с помощью языка Jinja — это из примера с фигурными скобками — {{ article.name }}.
Я тут не затронул довольно много важных тем — например связи между вашими моделями. У каждой статьи может быть один автор или несколько авторов — Django с легкостью справится с любым из перечисленных вариантов, и с помощью одной строки в Python вы сможете получить автора статьи или же коллекцию авторов в виде экземпляров класса Автор, созданного на основе models.Model.
Но откуда столько файлов?
Если вы создаете какое-то сложное приложение с кучей Моделей, видов и т.п. то это огромное количество кода надо как-то разбить на отдельные файлы. И файлы желательно организовать по папкам так, чтобы файл с моделями статьи был в той же папке что и виды статьи.
Вот тут приходит еще одна ключевая идея Django — приложения, которые заслуживают отдельного поста.