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


Django — бесплатный сетевой open source Python-фреймворк, помогающий решать распространённые в разработке проблемы. Он позволяет создавать гибкие, хорошо структурированные приложения. В Django уже из коробки есть много современных возможностей. Например, для меня такие фичи, как Admin, инструмент Object Relational Mapping (ORM), Routing и Templating, делают Django первым кандидатом при выборе инструментария для разработки. Создание приложения требует много сил, и, наслаждаясь своим делом, как и любой разработчик, я хочу тратить как можно меньше времени на рутинные задачи. Django сильно в этом помогает, не заставляя жертвовать гибкостью приложения.


Киллер-фича Django — мощный конфигурируемый админский интерфейс, который автоматически (автомагически?) генерируется на основе схемы вашей модели и моделей админки. Чувствуешь себя прямо-таки волшебником. С помощью интерфейса Admin пользователь может конфигурировать много вещей, в их числе — список управления доступом (access control list, ACL), разрешения и действия на уровне строк (row-level), фильтры, порядки сортировки (orders), виджеты, формы, дополнительные URL-хелперы и многое другое. Я считаю, что админка нужна каждому приложению. Это лишь вопрос времени, когда такая панель понадобится вашему основному приложению. В Django она создаётся быстро и удобно.


Также в Django есть мощная ORM, из коробки работающая со всеми главными базами данных. Она «ленива»: в отличие от других ORM, обращается к БД только по мере необходимости. В ней есть поддержка основных SQL-инструкций (и функций), которые вы можете использовать из своего исходного Python-кода наряду со всеми остальными возможностями языка.
В Django очень гибкий и мощный шаблонизатор (templating engine). Доступны многие стандартные фильтры и метки (tags), также можно создавать свои собственные. Django поддерживает другие движки как собственные шаблоны, предоставляет API для лёгкой интеграции с другими движками посредством стандартных shortcut-функций для обработки шаблонов.


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


Ошибка № 1. Использование для проектных зависимостей глобального окружения Python


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


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


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


  • virtualenv: пакет Python, генерирующий папку с окружением. Содержит скрипт для (де)активации окружения и управления установленными в нём пакетами. Это мой любимый и самый простой метод. Обычно я создаю окружение поближе к папке проекта.
  • virtualenvwrapper: пакет Python, глобально устанавливающий набор инструментов для создания/удаления/активации и т. д. виртуальных окружений и предоставляющий доступ к этому набору. Все окружения хранятся в одной папке (её можно переписать с помощью переменной WORKON_HOME). Я не вижу преимуществ в использовании virtualenvwrapper вместо virtualenv.
  • Виртуальные машины: нет лучшей изоляции, чем целая виртуальная машина, выделенная под ваше приложение. Есть масса доступных инструментов, например VirtualBox (бесплатный), VMware, Parallels и Proxmox (мой фаворит, есть бесплатная версия). В сочетании с инструментом автоматизации виртуальных машин вроде Vagrant это может оказаться очень мощным решением.
  • Контейнеры: в последние годы я почти в каждом проекте использую Docker, особенно в новых проектах, начинаемых с нуля. Docker — невероятный инструмент с множеством возможностей. Для его автоматизации доступна куча сторонних инструментов. В Docker есть кеширование уровней (layer caching), позволяющее крайне быстро пересоздавать контейнеры. В них я использую глобальное окружение Python, потому что каждый контейнер имеет собственную файловую систему и проекты изолируются на высоком уровне. Docker позволяет новым членам команды быстрее начинать работу над проектом, особенно если у них есть опыт работы с этой технологией.

Ошибка № 2. Отсутствие привязки зависимостей в файле requirements.txt


Каждый новый проект Python должен начинаться с файла requirements.txt и нового изолированного окружения. Обычно вы с помощью pip/easy_install устанавливаете все пакеты, не забывая о requirements.txt. Обычно проще (возможно, правильнее) развёртывать проекты на серверах или на машинах членов команды.


Также важно в файле requirements.txt выполнять привязку (pin) конкретных версий ваших зависимостей. Обычно разные версии пакета предоставляют разные модули, функции и параметры функций. Даже в младших версиях изменения зависимостей могут оказаться такими, что это сломает ваш пакет. Это очень серьёзная проблема, если у вас живой проект и вы планируете регулярно его развёртывать, так как без системы управления версиями ваша сборочная система всегда будет устанавливать последнюю доступную версию пакета.


В production всегда выполняйте привязку пакетов! Я для этого использую очень хороший инструмент pip-tools. Он предоставляет набор команд, помогающих управлять зависимостями. Инструмент автоматически генерирует requirements.txt, в котором привязаны не просто ваши зависимости, а вообще всё дерево, т. е. и зависимости ваших зависимостей.


Иногда нужно обновить какие-то пакеты в списке зависимостей (например, только фреймворк или утилиту). Если вы прибегаете к pip freeze, то не знаете, какие зависимости используются какими пакетами, и поэтому не можете их обновить. Инструмент pip-tools автоматически привязывает пакеты в соответствии с привязанными вами зависимостями, и поэтому он автоматически решает, какие пакеты нужно обновить. А благодаря используемым комментариям в requirements.txt вы всегда знаете, какой пакет пришёл из какой зависимости.


Если быть ещё более осторожным, то можно делать бекап исходных файлов ваших зависимостей. Храните копию в своей файловой системе, Git-папке, S3-папке, FTP, SFTP — где угодно, лишь бы под рукой. Бывают ситуации, когда исключение из списка относительно небольшого пакета ломает большое количество пакетов в npm. Pip позволяет скачивать все необходимые зависимости в виде исходных файлов. Почитайте об этом подробнее, выполнив команду pip help download.


Ошибка № 3. Использование старомодных Python-функций вместо представлений-классов (Class-based Views)


Иногда целесообразно использовать в файле приложения views.py маленькие Python-функции, особенно для тестовых или утилитарных представлений. Но обычно в приложениях нужно использовать представления на основе классов (CBV).


CBV — это представления общего назначения, предоставляющие абстрактные классы, реализующие распространённые задачи веб-разработки. CBV созданы профессионалами и покрывают большинство востребованных моделей поведения. У них есть прекрасно структурированный API, и CBV подарят вам возможность наслаждаться всеми преимуществами ООП. Ваш код будет чище и читабельнее. Забудьте о трудностях использования стандартных функций представления (view functions) Django для создания списков, CRUD-операций, обработки форм и т. д. Можно просто расширять подходящий CBV под ваше представление и переопределять (override) функции или свойства класса, конфигурирующие поведение представления (обычно функция возвращает свойство, и вы можете добавить в неё любую логику, которая способна превратить ваш код в спагетти, если вместо CBV вы прибегнете к функциям представления).


Например, можно использовать в проекте разные миксины, которые переопределяют основные модели поведения CBV: создание контекстов представлений, проверка авторизации на уровне строк (on the row level), автосоздание путей шаблонов на основе структур приложения, интегрирование умного кеширования и многое другое.


Я создал пакет Django Template Names, который стандартизирует имена шаблонов для ваших представлений на основе имени приложения и имени класса представления. Я пользуюсь им каждый день и экономлю кучу времени при выборе имён. Просто вставьте миксин в свой CBV — class Detail(TemplateNames, DetailView): — и он начнёт работать! Конечно, можете переопределить мои функции и добавить мобильные адаптивные шаблоны, другие шаблоны для user-agent’ов или что-нибудь ещё.


Ошибка № 4. Написание «толстых» (fat) представлений и «тонких» (skinny) моделей


Если у вас логика приложения перенесена из модели в представления, это означает, что в представлениях находится код, принадлежащий модели. То есть представления становятся «толстыми», а модель — «тонкой».


А нужно писать «толстые» модели и «тонкие» представления.


Разбейте логику по маленьким методам в модели. Это позволит использовать их многократно и из многочисленных источников (админский пользовательский интерфейс, пользовательский интерфейс фронтенда, конечные точки API, многочисленные представления). Это займёт всего несколько строк кода, и вам не придётся копипастить кучу строк. Когда в следующий раз будете писать функциональность отправки письма пользователю, расширьте модель с помощью email-функции, а не пишите логику в контроллере.


Это сделает ваш код более удобным для модульного тестирования, потому что вы сможете протестировать логику электронной почты в одном месте, а не делать это в каждом контроллере. Подробнее об этой проблеме почитайте в Django Best Practices. Решение простое: пишите «толстые» модели и «тонкие» представления. Начните это делать уже в следующем проекте или рефакторьте текущий.


Ошибка № 5. Огромный, неповоротливый файл настроек


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


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


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


Вот пример минимальной конфигурации:


from split_settings.tools import optional, include

include(
    'components/base.py',
    'components/database.py',
    'components/*.py',

    # the project different envs settings
    optional('envs/devel/*.py'),
    optional('envs/production/*.py'),
    optional('envs/staging/*.py'),

    # for any local settings
    optional(‘local_settings.py'),
)

Ошибка № 6. Приложение всё-в-одном, плохая структура приложения и некорректное размещение ресурсов


Любой Django-проект состоит из нескольких приложений. В терминологии Django приложение — это Python-проект, содержащий как минимум файлы __init__.py и models.py. В последних версиях Django models.py больше не нужен, достаточно только __init__.py.


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


У вас должна быть возможность полностью описать назначение приложения одним-двумя короткими приложениями. Например: «Позволяет пользователю зарегистрировать и активировать по почте свой аккаунт».


Рекомендуется назвать папку проекта project и положить приложения в project/apps/. Затем положить все зависимости приложений в собственные подпапки.


Примеры:


  • Статичные файлы: project/apps/appname/static/appname/
  • Метки шаблона: project/apps/appname/templatetags/appname.py
  • Файлы шаблона: project/apps/appname/templates/appname/

Всегда добавляйте имена приложений в виде префиксов в названия подпапок, потому что все статические папки объединяются в одну. И если два или более приложений имеют файл js/core.js, то последнее приложение в settings.INSTALLED_APPLICATIONS переопределит все предыдущие. Однажды я столкнулся с таким багом в своём проекте и потратил около шести часов на отладку, пока не сообразил, что другой разработчик переопределил мой static/admin/js/core.js, потому члены команды реализовали кастомную админскую SPA-панель и дали своим файлам такие же имена.


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


root@c5b96c395cfb:/test# tree project/apps/portal/
project/apps/portal/
+-- __init__.py
+-- admin.py
+-- apps.py
+-- management
¦   +-- __init__.py
¦   L-- commands
¦       +-- __init__.py
¦       L-- update_portal_feeds.py
+-- migrations
¦   L-- __init__.py
+-- models.py
+-- static
¦   L-- portal
¦       +-- css
¦       +-- img
¦       L-- js
+-- templates
¦   L-- portal
¦       L-- index.html
+-- templatetags
¦   +-- __init__.py
¦   L-- portal.py
+-- tests.py
+-- urls.py
L-- views.py

11 directories, 14 files

Благодаря такой структуре вы можете в любой момент экспортировать приложение в другой Python-пакет и снова его использовать. Можете даже опубликовать его в PyPi в качестве open source пакета или переместить в другую папку. У вас получится примерно такая структура проекта:


root@c5b96c395cfb:/test# tree -L 3
.
+-- deploy
¦   +-- chef
¦   L-- docker
¦       +-- devel
¦       L-- production
+-- docs
+-- logs
+-- manage.py
+-- media
+-- project
¦   +-- __init__.py
¦   +-- apps
¦   ¦   +-- auth
¦   ¦   +-- blog
¦   ¦   +-- faq
¦   ¦   +-- pages
¦   ¦   +-- portal
¦   ¦   L-- users
¦   +-- conf
¦   +-- settings.py
¦   +-- static
¦   +-- templates
¦   +-- urls.py
¦   L-- wsgi.py
L-- static
    L-- admin
        +-- css
        +-- fonts
        +-- img
        L-- js

25 directories, 5 files

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


Ошибка № 7. STATICFILES_DIRS и STATIC_ROOT смущают новичков в Django-разработке


Статичные файлы — это ресурсы (assets), которые не меняются по мере использования приложения. Например, JavaScript, CSS, изображения, шрифты и т. д. В Django они «накапливаются» в публичной директории в ходе развёртывания.


В режиме разработки — python manage.py runserver — Django ищет статичные файлы с помощью настройки STATICFILES_FINDERS. По умолчанию он пытается найти запрошенный файл в папках, перечисленных в STATICFILES_DIRS. Если не находит, то ищет с помощью django.contrib.staticfiles.finders.AppDirectoriesFinder, которая проверяет папку static каждого установленного в проекте приложения. Такая схема позволяет писать многократно используемые приложения, поставляемые со своими собственными статичными файлами.


В production вы раздаёте статичные данные посредством отдельного веб-сервера, например nginx. Он ничего не знает о структуре приложений проекта Django или о том, по каким папкам распределены ваши статичные файлы. К счастью, Django предоставляет нам команду управления сбором статичных данных (collect static management command) — python manage.py collectstatic, которая проходит по STATICFILES_FINDERS и копирует все статичные файлы из папок static приложений, а также из папок, перечисленных в STATICFILES_DIRS, в заданную вами в STATIC_ROOT директорию. Это позволяет разрешать (resolution) ресурсы в виде статичных данных с помощью той же логики, что и у Django-сервера в режиме разработки, и собирать в одном месте для веб-сервера все статичные файлы.


Не забудьте выполнить collectstatic в вашем production-окружении!


Ошибка № 8. Использование в production STATICFILES_STORAGE по умолчанию и загрузчиков Django-шаблонов


Давайте поговорим об управлении ресурсами (asset) production-окружения. Мы можем обеспечить наилучший UX, если воспользуемся политикой «у ресурсов не истекает срок действия» (assets never expire) (подробнее о ней можно почитать здесь). Это означает, что все наши статичные файлы должны быть закешированы браузерами на недели, месяцы или даже годы. Иными словами, пользователи должны лишь единожды скачивать ресурсы!


Классная идея, и её можно реализовать всего в несколько строк в nginx-конфигурации для нашей папки со статичными файлами. Но что насчёт проверки актуальности кеша? Если пользователь лишь один раз скачивает наш ресурс, то что делать в том случае, если вы обновите логотип, шрифты, JavaScript или цвет текста в меню? Для решения этой задачи вам нужно при каждом развёртывании генерировать уникальные URL’ы и имена для каждого статичного файла!


Для этого можно использовать ManifestStaticFilesStorage в качестве STATICFILES_STORAGE (будьте осторожны, хеширование включается только в режиме DEBUG=false) и выполнить команду collectstatic. Это приведёт к снижению количества запросов ресурсов у вашего production-сайта и сделает его отрисовку гораздо быстрее.


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


В разделе конфигурации cached.Loader можно найти хороший пример и подробности решения проблемы. Не используйте загрузчик в режиме разработки, потому что он не перезагружает отпарсенные шаблоны из файловой системы. Вам понадобится перезапускать свой проект, используя python manage.py startapp, при каждом изменении шаблона. При разработке это может раздражать, зато идеально для production-окружения.


Ошибка № 9. Чистый Python для утилит или скриптов


У Django есть отличная фича — команды управления. Используйте их вместо изобретения велосипеда в виде написания скриптов на чистом Python для утилит вашего проекта.


Также обратите внимание на пакет Django Extensions, представляющий собой коллекцию кастомных расширений для Django. Возможно, кто-то уже реализовал ваши команды! Существует много распространённых целевых команд.


Ошибка № 10. Велосипедостроение


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


Не надо усложнять. Сначала — гуглим! Установите найденный качественный пакет, сконфигурируйте, расширьте и интегрируйте в свой проект. И если есть возможность, внесите свой вклад в open source.


Вот вам для начала список моих собственных пакетов для Django:


  • Django Macros URL: с помощью макросов облегчает написание (и чтение) URL-паттернов в Django-приложениях.
  • Django Templates Names: маленький миксин, помогает легко стандартизировать имена ваших CBV-шаблонов.
  • Django Split Settings: позволяет распределить Django-настройки по нескольким файлам и директориям. Легко переопределяет и модифицирует настройки. Использует подстановки (wildcards) в путях файлов и помечает файлы настроек как опциональные.

Don’t repeat yourself (DRY)!


Я сторонник DRY-концепции, поэтому создал Django skeleton — удобный инструмент с рядом приятных функций уже из коробки:


  • Docker-образы для разработки/production, управляемые docker-compose, что позволяет легко оркестрировать списком контейнеров.
  • Простой Fabric-скрипт для развёртывания в production.
  • Конфигурация для пакета Django Split Settings с настройками базы и локальных источников.
  • Интегрированный в проект Webpack — при выполнении команды collectstatic Django соберёт только папку dist.
  • Сконфигурированы все основные Django-настройки и фичи вроде кешируемых в production Django-шаблонов, хешированных статичных файлов, интегрированного тулбара для отладки, журналирования и т. д.

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

Поделиться с друзьями
-->

Комментарии (56)


  1. Jenyay
    12.05.2017 15:04
    -2

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


    1. andrewnester
      12.05.2017 15:17

      объясните, пожалуйста, почему логично?


      1. Jenyay
        12.05.2017 15:26
        +2

        Чтобы не смешивать данные и логику. Тогда каждый класс отвечает за одно дело — шаблон за отображение, Модель — за хранение данных, а Вид — за бизнес-логику. А так становится непонятным, зачем нужен Вид.


        1. andrewnester
          12.05.2017 15:34
          +1

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


          1. Jenyay
            12.05.2017 15:39
            +1

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


        1. freylis
          12.05.2017 16:19

          Размещая БЛ во view вы сами себя лишаете возможности повторно использовать свой же код. Сильно толстые модели тоже плохо, но никто не запрещает делать "бизнес-прослойку" между view и models


        1. foldr
          13.05.2017 00:30
          +1

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


    1. ilyaplot
      12.05.2017 15:48
      +3

      Логику внутрь View? Представьте, сколько времени займет редизайн проекта.


      1. Jenyay
        12.05.2017 15:51
        +2

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


        1. baldr
          13.05.2017 00:56

          Если модель получается слишком толстой — напишите рядом с ней контроллер или фабрику или любую другую подходящую структуру. Паттерны никто не отменял и наоборот они часто помогают даже в django.


        1. vechnoe
          16.05.2017 11:17
          +2

          Толстых моделей жалательно избегать по-возможности. Всю логику лучше держать в logic или utils. В моделях оставлять логику, относящуюся конкретно к данным, напр, какие-то property. Иначе модель может разрастись до 400-500 строк (это не редкость в каждом втором крупном проекте).


          1. baldr
            16.05.2017 13:08

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


      1. galaxy
        13.05.2017 01:51

        В джанге view = контроллер.

        Дизайн в шаблонах (templates)


    1. kuraga333
      12.05.2017 19:11

      Может, в Контроллер, а не в Вид?


      1. DaneSoul
        12.05.2017 22:55
        +1

        В Django исторически принято так, что:
        — то что в стандартной MVC модели называют «Controller» в Django называют «views»
        — а то что в стандартной MVC модели «View» в Django называют «templates».


        1. medvoodoo
          15.05.2017 14:25

          не совсем так. В джанге контроллеры и вьюхи объединили во views(можно сказать, что от самих контроллеров отказались). А шаблоны — они шаблоны и есть, текстовые файлы в специальной разметке для замены и подстановки в них данных.


    1. 3al
      12.05.2017 20:35
      -1

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

      Как именно предлагаете тестировать логику в видах? Юнит-тесты станут невозможны и нужно будет писать уйму интеграционных, да?


  1. mizhgun
    12.05.2017 15:39
    +2

    > Не надо усложнять. Сначала — гуглим! Установите найденный качественный пакет, сконфигурируйте, расширьте и интегрируйте в свой проект. И если есть возможность, внесите свой вклад в open source.

    > Вот вам для начала список моих собственных пакетов для Django

    Не стройте свой велосипед, вот вам список моих!

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


  1. andreymal
    12.05.2017 16:08
    +2

    Мне одному не нравятся Class-based Views? Неохота доказывать/объяснять почему (слишком субъективно), просто интересно, есть ли ещё такие


    1. artifex
      12.05.2017 16:15

      Я ниже немного накинул на вентилятор.


    1. Vaal
      12.05.2017 16:22
      +1

      не одному, и к оригинальной статье есть пара развернутых комментариев на счет этого от core django разработчика


    1. sazhyk
      12.05.2017 18:16

      В документации так и написано:


      Class-based views provide an alternative way to implement views as Python objects instead of functions.

      Этот способ не лучше и не хуже, он просто другой. И да, вы не один))


    1. justhabrauser
      13.05.2017 11:57
      +1

      Конечно не Вам одному.
      Но вообще посмотрите на дату публикации — вполне себе веселое пятничное чтиво.
      Меня вот начало веселить прямо с «ошибки» №1.


  1. artifex
    12.05.2017 16:14
    +7

    CBV — это худшее, что есть в Django. Совершенно дурацкое усложнение, которое на ровном месте ставит палки в колёса композиции логики, а исследовать поведение методов иногда совершенно нетривиально из-за необходимости ходить туда-сюда по всему дереву наследования и удерживать в голове весь контекст переопределения. В принципе, это общая проблема множественного наследования, но в Django идея возведена в абсолют. 100500 вьюх, базовых вьюх, миксинов… при том, что в любом случае приходится переопределять подавляющее большинство их методов. При разрастании кодовой базы это всё какой-то ад для поддержки и модификации.


    1. tema_sun
      12.05.2017 17:50
      +2

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


    1. Tanner
      12.05.2017 18:02
      +2

      Мне тоже так казалось сначала. Но после некоторой практики я понял, что CBV ? отличная вещь, просто у них есть некоторый порог вхождения. Нарисуйте на бумаге диаграммы вызовов в каком-нибудь DetailView, и через неделю этот класс станет для вас родным. Будете удивляться, как это вы раньше могли писать гору шаблонного кода вроде “if request.method == 'POST'”.

      Если вам пришлось переопределять большинство методов ? вы, скорее всего, наследуетесь не от того класса.


      1. artifex
        12.05.2017 18:24

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


        1. cocostru
          14.05.2017 10:54
          +1

          не поделитесь, что сейчас используете?


          1. artifex
            15.05.2017 17:10

            На текущем проекте, в зависимости от приложения, Pyramid или aiohttp. На предыдущем была Tornado. В Pyramid и Tornado вьюхи в общем-то тоже на классах, но на самом деле это только точка входа для дальнейшей композиции функций. В aiohttp всё честно сразу :)


    1. ArsenAbakarov
      12.05.2017 18:33
      +1

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


    1. immaculate
      12.05.2017 18:35
      +1

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


      Так они позволяют очень легко и наглядно переиспользовать код. На их основе, например, сделал недавно систему, которая генерирует HTML, PDF и Excel отчеты. Повторяющегося кода нет вообще благодаря CBV. Все детали конкретного формата обрабатываются миксинами.


    1. vechnoe
      13.05.2017 00:37

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


      Вообще, едиственная задача ООП — уменьшение сложности. Если в классе минимум 5 миксинов (обычная практика), переписать на обычные view — хорошая задача для рефакторинга.


    1. SerhiyRomanov
      13.05.2017 23:19

      А что тогда вы скажите про forms в Django?


    1. questpc
      16.05.2017 10:56

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


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


      https://github.com/Dmitri-Sintsov/django-jinja-knockout/tree/master/django_jinja_knockout/views


  1. arheops
    12.05.2017 21:27
    +2

    С моей точки зрения основная проблема django — среди 100500 пакетов «на все случаи жизни», которые надо «использовать вместо велосипедов» больше 50% — не работают в текущей версии django ВООБЩЕ, поскольку их авторам джанго уже неинтересен, а core team не особо следит за обратной совместимостью.

    А из оставшихся еще 30% очевидным образом не работают, демо проектов не имеют(или пункт 1) и как его использовать непонятно.


    1. baldr
      13.05.2017 00:26
      +1

      Вместо django подставьте %frameworkname% и идите в любой форум любого фреймворка.


      1. arheops
        13.05.2017 04:02
        -1

        У django это доведено до абсурда, в связи с непонятными несовместимостями ядра и РАЗНЫМИ requirments.txt


    1. vechnoe
      13.05.2017 00:30

      Очень часто проще реализовать самому функционал на 5 строчек, чем лепить непонятные зависимости в проект. Тут дело не в велосипедостроении, а в целесобразности. По настоящему хороших пакетов — единицы.


      1. immaculate
        13.05.2017 07:33
        +2

        Неправда. Я работаю много лет над двумя очень большими и посещаемыми проектами, и двумя поменьше. В каждом размер requirements.txt от 40 до 100+ строк.


        Существует масса отличных поддерживаемых пакетов. Бывает, что разработчики не успевают за версиями Django (причем не всегда при этом совместимость ломается, например, последний переход 1.10-1.11 практически безболезненный), тогда достаточно на GitHub поискать обновленный форк, чаще всего он находится. Если не находится, как правило, очень быстро можно допилить свой. И создать pull request.


        А вот те, кто пишут велосипеды. Ребята, за вами очень тяжело поддерживать код. В отличие от готовых пакетов с PyPI/GitHub, у вас нет ни документации, ни примеров, ни комментариев, ни архитектуры.


        Да и в большинстве случаев велосипедостроение возникает от незнания и неумения искать на djangopackages, pypi, github и просто google.


        1. vechnoe
          13.05.2017 10:51

          Вероятно мы с вами о разных вещах говорим. В стандартных проектах (обычный сайт, магазин) проще взять готовое решение. Если проект в логику CRUD вписывается лишь частично и проект использует из функционала джанги только ORM + DRF то написать 5 строчек предрпочтительнее.


          1. immaculate
            13.05.2017 19:44
            +2

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


            А так существует масса полезных батареек для Django. Например:


            • django-allauth
            • django-localflavor
            • django-braces
            • django-crispy-forms
            • django-taggit
            • django-extensions
            • django-select2

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


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


            Ну и по-моему опыту так получается почти всегда. В другом проекте написан свой модуль для работы с ElasticSearch. Абсолютно непонятный, кривой, и с ужасной производительностью. Но на него теперь завязано почти все, и переписать нормально нет возможности. :(


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


            1. Compadre
              16.05.2017 10:56

              Разрешите дополнить, очень часто использую в проектах:

              • django-imagekit
              • django-solo
              • django-leaflet
              • django-modeltranslation
              • django-ckeditor

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


              1. immaculate
                16.05.2017 11:09

                Я вместо django-solo использую django-constance.


          1. questpc
            16.05.2017 10:58

            А я до сих пор не могу понять зачем использовать Django как ORM + DRF без inline formsets, admin и прочего что так ценно в Django. Если нужно только серверное API для клиента на react / angular, не лучше ли использовать на сервере node.js?


            Я использую AJAX в своих модулях (у меня knockout.js) но подход смешанный — часть кода обычный серверный html, часть AJAX виджеты. Не SPA. Мне кажется что для SPA лучше next.js или что-то подобное.


            1. vechnoe
              16.05.2017 11:12

              Испоьзую ORM + DRF + admin, потому, что это удобно, и другие разработчики знают этот стек, документация по DRF отличная. Джанга только часть проекта (не самая важная кстати). Т.к. последний проект разделен на микросервисы, возможно эта часть на джанге будет заменена на что-то другое, возможно вообще не на python.


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


              Для чего использовать Node.js с его ассинхронностью, если мне нужен именно синхронный фремворк. Если мне будет необходима ассинхронность, я возьму aiohttp.


              1. questpc
                16.05.2017 11:25

                Да, admin в Django очень хороший, не спорю. Только это классическое приложение не REST и не AJAX. node.js позволяет использовать один язык на серверной стороне и клиентской, в случае AJAX / REST должно быть достаточно.


                inline formsets вообще одна из самых ценных фич Django, автоматический маппинг отношений один ко многим с уровня моделей на уровень форм. Вроде бы что-то похожее появилось в последних версиях Angular, массивы форм: https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html


                Поддержка множественных форм с отношениями до сих пор большая редкость во фреймворках.


            1. immaculate
              16.05.2017 11:12
              +1

              Я не хочу начинать языковой холивар, но мне кажется, что Javascript — плохой язык. Это костыль, который по роковому стечению обстоятельств зажил полноценной жизнью. А вся экосистема Javascript/Node.js — это какой-то сумасшедший калейдоскоп, где каждый день с утра уже надо переписывать проект, потому что поменялись все версии библиотек, поменялась парадигма, поменялась система сборки проекта. Чтобы в этом непрерывно меняющемся зоопарке библиотек и инструментов разбираться… Нет, я уж лучше что-нибудь другое изучу.


              1. questpc
                16.05.2017 11:28

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


                1. immaculate
                  16.05.2017 11:32

                  Надеюсь, WebAssembly и подобные проекты наконец-то переломят ситуацию. Когда появилась Mozilla, было несколько более-менее успешных попыток внедрить поддержку других языков (в частности, Python и Tcl/Tk). Были даже весьма интересные расширения на этих языках. К сожалению, все это не взлетело… Возможно потому что в то время, не было реальной необходимости в полноценном языке программирования в браузере. Сейчас ситуация другая, и, сильно надеюсь, что Javascript наконец с почестями похоронят. На мой взгляд, это такой PHP для frontend'а.


                  1. questpc
                    16.05.2017 11:42

                    Судя по тому что в Javascript (как и в PHP) добавляют все больше фич из Python, в том числе генераторы и variadic args, у них планы совсем другие.


                    Особенно преуспевают разработчики PHP, они вообще похоже изучают PEP и детали CPython. Только при этом основа языка остается кривой: из-за совместимости не поправить.


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


  1. ArtVol
    13.05.2017 00:30
    -1

    Люто плюсую за статью


  1. baldr
    13.05.2017 00:53
    +4

    Всегда будут люди, которым надо сделать что-то, не предусмотренное инструкцией. И всегда будут от них жалобы типа «в этом вашем джанго CBV г*вно, потому что мне неудобно сделать %specific_feature%».
    Конечно, статья немного субъективна, но думать в любом случае никто не запрещал. Перед началом проекта сядь и подумай — вообще подойдет тебе джанга или будет только камнем?

    У меня для многих проектов изначально не было смысла делать веб-интерфейс и я велосипедил с простых скриптов, слегка связанных общими модулями. Для простых одностраничных веб-приложений добавлял Flask. Но потом внезапно понял что структуру проекта я пытаюсь выдерживать в django-стиле, мне не хватает удобных settings, и волшебного django ORM с его моделями.
    С недавних пор я по-умолчанию начинаю проект с django-темплейта, выпиливая веб-часть, если не нужна.

    Что касается логики во View — достаточно один раз (ок, два — точно достаточно для большинства чтобы осознать) столкнуться с тем, что, внезапно, логика начинает использоваться не только с фронтенда, а еще и с бэкенда (например, из celery-тасков создание или обработка сложных объектов). После этого количество кода логики во view резко уменьшается и появляются либо контроллеры, либо распухают модели, либо появляются отдельные под-приложения, которые могут вызываться откуда угодно.
    Надо понимать что View (как бы кто ни говорил о том что в django это модифицированный Controller) предназначена, в первую очередь, для реализации схемы request-response и использовать ее с другой целью — смысла мало.

    Люди, которые не любят CBV и используют функции для вьюшек — я вам откровенно и честно завидую! У вас, наверное, достаточно времени на качественный рефакторинг и требования изначально четкие и не меняются.
    В противном случае я не представляю как можно не переиспользовать код в сложном проекте.
    Конечно, стандартные классы вьюшек — простейшие и даже для ListView приходится переопределять много методов чтобы сделать то, что нужно. Но это в любом фреймворке так. Для простого прототипа достаточно django admin, но упаси Нортон строить дальше логику на нем!

    Вот, кстати, я бы добавил 11ю ошибку — «использование django admin для построения интерфейса приложения». Можно сколько угодно обклеивать холодильник пенопластом, но море он не переплывет, и хорошо если затонет рядом с берегом, а не на полпути (обратно грести дольше).

    Конечно, если у вас предполагается NoSQL, высокие нагрузки или очень динамическая структура базы — то вам надо обязательно 7 раз задуматься и не торопиться с выбором. Но и в эту статью тогда не надо идти с комментариями типа «фу».


    1. chehov
      16.05.2017 12:07

      Люди, которые не любят CBV и используют функции для вьюшек — я вам откровенно и честно завидую! У вас, наверное, достаточно времени на качественный рефакторинг и требования изначально четкие и не меняются.

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

      Простите, но вы сами себе противоречите. Для меня CBV — отвратительное решение, которого лучше бы не было совсем, чтобы молодые специалисты не забивали себе голову. CBV в Django — это как ООП в Java (ООП ради ООП). Нет никакого смысла в этих ваших классах, когда для страницы, чуть более сложной чем My Personal Home Page нужно переопределить с пяток методов, зарыться в родительские классы или писать новые миксины.

      Да, миксины удобно, но не применимо IRL, к сожалению. Намного красивее и лаконичнее выглядят вью-декораторы. А когда у логика зависит от юзер-инпута — вообще пиши «пропало», CBV превращается в гомункула созданного Виктором Франкенштейном.

      Я уже не говорю, про абсолютно блевотную контрукцию

      url(r'^register/$', FormView.as_view(form_class=Register, success_url='/thanks/')
      

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


      1. baldr
        16.05.2017 13:23

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

        Я не сомневаюсь что вам, как и любому другому Python-разработчику, приходилось вынужденно залезать в исходники библиотек (django) для того, чтобы отладить что-то. Вы наверняка видели какое количество кода скрыто внутри и для обработки каких ситуаций оно предназначено.
        Не все вещи можно удобно использовать с декораторами, иногда ООП-подход очень спасает.

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

        ООП в Java — это для перфекционистов, но даже там подход по-своему красив (я не оч люблю Java если что).


  1. baldr
    16.05.2017 13:27

    Вот вместо CBV я бы поругал сигналы. Хорошо что ими не так много пользуются, а иначе отладка бы превратилась просто в БОЛЬ.
    Опять же, я понимаю почему это было сделано и это достаточно элегантная попытка отделить взаимодействие разных частей кода (или библиотек).
    Но вот как раз сигналы, на мой взгляд, совершенно не логичная именно для Python идеология.


    1. andreymal
      16.05.2017 13:44

      А что тогда использовать вместо сигналов? Если всё ещё хотеть отделить взаимодействие разных частей кода.