Что бы я хотел знать когда начинал изучать Django? — очень общий взгляд

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

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

В этом посте я бы хотел посмотреть на Django с очень «философского вида» — минимум кода, максимум общих фактов. Думаю, что такой взгляд поможет тем, кто хочет начать изучать Django но теряется на старте.

image

Хочу также сказать, что не являюсь профессионалом по части веб-программирования — я в этой области скорее любитель, которого интересуют исключительно личные проекты — один из них сайт по расшифровке данных ДНК тестов 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 — приложения, которые заслуживают отдельного поста.

Комментарии 13

    +5
    Django берет всю головную боль, связанную с проблемами SQL на себя — вам даже не надо знать SQL, чтобы пользоваться Django, от вас нужен только python — Django сам сформирует SQL команды для создания таблиц, поиска и записи данных в таблицы и все это будет безопасно.

    Это до тех пор пока не начнутся проблемы и вы не взглянете на тот кошмар который джанговский ORM генерирует в виде SQL, если у вас в модели есть больше чем пара связей… а всякие prefetch и related_prefetch при бездумном использовании запросто выжрут пару гигабайт памяти на, казалось бы, простом запросе.

    я к тому что именно джанговский орм научил меня всегда смотреть sql который он генерирует… и иногда писать на чистом sql в обход orm — потому что код в orm, в некоторых случаях, получается гораааздо сложнее и трудночитаемей…
      +1

      Тут главное знать меру. Работал как-то я над проектом, в котором недавно ушедший lead dev очень любил raw sql и считал что orm тормозит. Код пестрил raw sql вставками и QuerySet.values(). Итого встроенное кэширование не работало, а для внесения даже небольших изменений требовалось изменять достаточно много кода.


      В результате — откат "оптимизаций" и использование преимущественно ORM + prefetch related.

        0
        В результате — откат «оптимизаций» и использование преимущественно ORM + prefetch related.

        Да, только вы отлично знаете как правильно их откатить и грамотно затюнить ORM, а текст в оригинале звучит как «вам даже не надо знать SQL, чтобы пользоваться Django»
        тут как раз его надо ооочень хорошо это знать, чтобы пользоватся django :)
        +1
        Не соглашусь по поводу ужасности джанговской ORM. Не знаю что там другие бэкенды генерируют, но с postgres sql генерируется довольно приемлимый и мне лишь единожды приходилось писать raw и это было связано скорее со сложными join.
        И вы наверно имели ввиду prefetch_related? Если так то этот метод не генерирует sql, точнее генерирует но в отдельных запросах. В отличие от select_related.
        В django ORM много механизмов оптимизации запросов и только исчерпав их все следует переходить к написанию raw
          0
          который джанговский ORM генерирует в виде SQL

          И это не основная его проблема. Это ActiveRecord со всеми вытекающими минусами и нарушениями SOLID.

            0
            Будучи приверженцем Flask, пробовал SqlAlchemy. Это всё, конечно, хорошо, если запросы простые, и нельзя делать жёсткую привязку к типу БД. Но, как только запросы становятся посложнее "...ON DUPLICATE KEY UPDATE..." — проще писать на чистом SQL. Иначе закопаешься в документации к ORM.
            В итоге сделал себе модуль поверх pymysql/mysql.connector с основными функциями, и с небольшими изменениями тягаю его во все проекты.
              0
              Хз. Очень редко на алхимии надобится писать сырой SQL, да и кроме того даже сырой sql имхо в алхимию понятнее встраивается в уже имеющиеся блоки чем в джанге.
            0
            А есть вариант Habra написанный на Dango типа LiveStreet?
              0
              <зануда мод>
              В django нет «классовых видов». Во первых, мне кажется более привычным называть view по русски как кальку с англ. т.е «вьюхи». Во вторых, все вьюхи всегда функции, которые на вход принимают request и набор позиционных и именнованных аргументов из url, если они есть. То что вы называете «классовыми видами» на самом деле class based view — вьюхи основанные на классах. В этих классах есть спец метод as_view, который и генерирует нужную функцию на основе класса. Именно результат этого метода мы и используем в urlconf.
              </зануда мод>
              Но это просто маленькое уточнение. На начальном этапе изучения это не так важно. Статья думаю полезна для новичков.
                0

                <зануда мод>
                Это всё же не калька, а морфологическая передача.


                А чем плох термин представление, который, как я заметил, обычно и применяется для перевода view?
                </зануда мод>

                  +1
                  По поводу перевода view согласен. Наверно, нужно было повнимательнее посмотреть какая терминология используется в русском языке, чтобы не плодить разные термины одного и того же.
                +1

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


                Я бы посоветовал вам добавить в начале статьи теорию о том, зачем вообще нужны все эти Model-View-Controller, обьяснить общими словами, как MVC структура имплементирована в Django (и куда делся контроллер), добавить cхему движения данных, например вот эту (в интернете можно найти и более детальные схемы, но для новичков данная картинка в самый раз):

                  0
                  Я бы все же рекомендовал начать изучение Django с того как она работает, а именно
                  детально по шагам разобрать цепочку:

                  Клиент > Запрос > web server > wsgi > формирование request > middleware > корневой urls > app.urls > app.views > context processors > template > response > wsgi > web server > Клиент


                  Запросов к БД у вас может и вовсе не быть, либо они могут быть раскиданы по данной цепочке (к примеру вo views, context proccessors или middleware).

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

                  Самое читаемое