- PVSM.RU - https://www.pvsm.ru -
В этом руководстве мы рассмотрим основные ошибки 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). Доступны многие стандартные фильтры и метки [1] (tags), также можно создавать свои собственные. Django поддерживает другие движки как собственные шаблоны, предоставляет API для лёгкой интеграции с другими движками посредством стандартных shortcut-функций для обработки шаблонов.
Фреймворк имеет и много других важных возможностей вроде URL-роутера, который парсит входящие запросы и генерирует новые URL на основе схемы роутинга. В целом Django приятен в работе, и, когда вам понадобится помощь, просто почитайте документацию [2].
Не используйте глобальное окружение Python для зависимостей вашего проекта, потому что это может привести к возникновению конфликтов зависимостей. Python не умеет работать с несколькими версиями пакетов одновременно. Это станет проблемой, если разным проектам нужны разные, несовместимые версии одного пакета.
Обычно такую ошибку допускают новички в Python- и Django-разработке, не знающие об особенностях изоляции окружения Python.
Есть много способов изолировать окружение, наиболее часто встречаются такие:
virtualenvwrapper
вместо virtualenv
.Каждый новый проект Python должен начинаться с файла requirements.txt [11] и нового изолированного окружения. Обычно вы с помощью pip/easy_install
устанавливаете все пакеты, не забывая о requirements.txt
. Обычно проще (возможно, правильнее) развёртывать проекты на серверах или на машинах членов команды.
Также важно в файле requirements.txt выполнять привязку (pin) конкретных версий ваших зависимостей. Обычно разные версии пакета предоставляют разные модули, функции и параметры функций. Даже в младших версиях изменения зависимостей могут оказаться такими, что это сломает ваш пакет. Это очень серьёзная проблема, если у вас живой проект и вы планируете регулярно его развёртывать, так как без системы управления версиями ваша сборочная система всегда будет устанавливать последнюю доступную версию пакета.
В production всегда выполняйте привязку пакетов! Я для этого использую очень хороший инструмент pip-tools [12]. Он предоставляет набор команд, помогающих управлять зависимостями. Инструмент автоматически генерирует requirements.txt
, в котором привязаны не просто ваши зависимости, а вообще всё дерево, т. е. и зависимости ваших зависимостей.
Иногда нужно обновить какие-то пакеты в списке зависимостей (например, только фреймворк или утилиту). Если вы прибегаете к pip freeze, то не знаете, какие зависимости используются какими пакетами, и поэтому не можете их обновить. Инструмент pip-tools автоматически привязывает пакеты в соответствии с привязанными вами зависимостями, и поэтому он автоматически решает, какие пакеты нужно обновить. А благодаря используемым комментариям в requirements.txt
вы всегда знаете, какой пакет пришёл из какой зависимости.
Если быть ещё более осторожным, то можно делать бекап исходных файлов ваших зависимостей. Храните копию в своей файловой системе, Git-папке, S3-папке, FTP, SFTP — где угодно, лишь бы под рукой. Бывают ситуации, когда исключение из списка относительно небольшого пакета ломает большое количество пакетов в npm [13]. Pip позволяет скачивать все необходимые зависимости в виде исходных файлов. Почитайте об этом подробнее, выполнив команду pip help download
.
Иногда целесообразно использовать в файле приложения views.py
маленькие Python-функции, особенно для тестовых или утилитарных представлений. Но обычно в приложениях нужно использовать представления на основе классов (CBV).
CBV — это представления общего назначения, предоставляющие абстрактные классы, реализующие распространённые задачи веб-разработки. CBV созданы профессионалами и покрывают большинство востребованных моделей поведения. У них есть прекрасно структурированный API, и CBV подарят вам возможность наслаждаться всеми преимуществами ООП. Ваш код будет чище и читабельнее. Забудьте о трудностях использования стандартных функций представления (view functions) Django для создания списков, CRUD-операций, обработки форм и т. д. Можно просто расширять подходящий CBV под ваше представление и переопределять (override) функции или свойства класса, конфигурирующие поведение представления (обычно функция возвращает свойство, и вы можете добавить в неё любую логику, которая способна превратить ваш код в спагетти, если вместо CBV вы прибегнете к функциям представления).
Например, можно использовать в проекте разные миксины, которые переопределяют основные модели поведения CBV: создание контекстов представлений, проверка авторизации на уровне строк (on the row level), автосоздание путей шаблонов на основе структур приложения, интегрирование умного кеширования и многое другое.
Я создал пакет Django Template Names [14], который стандартизирует имена шаблонов для ваших представлений на основе имени приложения и имени класса представления. Я пользуюсь им каждый день и экономлю кучу времени при выборе имён. Просто вставьте миксин в свой CBV — class Detail(TemplateNames, DetailView):
— и он начнёт работать! Конечно, можете переопределить мои функции и добавить мобильные адаптивные шаблоны, другие шаблоны для user-agent’ов или что-нибудь ещё.
Если у вас логика приложения перенесена из модели в представления, это означает, что в представлениях находится код, принадлежащий модели. То есть представления становятся «толстыми», а модель — «тонкой».
А нужно писать «толстые» модели и «тонкие» представления.
Разбейте логику по маленьким методам в модели. Это позволит использовать их многократно и из многочисленных источников (админский пользовательский интерфейс, пользовательский интерфейс фронтенда, конечные точки API, многочисленные представления). Это займёт всего несколько строк кода, и вам не придётся копипастить кучу строк. Когда в следующий раз будете писать функциональность отправки письма пользователю, расширьте модель с помощью email-функции, а не пишите логику в контроллере.
Это сделает ваш код более удобным для модульного тестирования, потому что вы сможете протестировать логику электронной почты в одном месте, а не делать это в каждом контроллере. Подробнее об этой проблеме почитайте в Django Best Practices [15]. Решение простое: пишите «толстые» модели и «тонкие» представления. Начните это делать уже в следующем проекте или рефакторьте текущий.
Даже в новом файле настроек Django-проекта [16] этих настроек содержится множество. А в реальных проектах файл разрастается до 700+ строк, которые трудно сопровождать, особенно когда окружениям разработки, продакшена и стейджинга нужны разные конфигурации.
Вы можете вручную разделить конфигурационный файл и создать отдельные загрузчики, но я хочу порекомендовать отличный, хорошо протестированный Python-пакет Django Split Settings [17], соавтором которого я являюсь.
Пакет предоставляет две функции — 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'),
)
Любой Django-проект состоит из нескольких приложений. В терминологии Django приложение [18] — это Python-проект, содержащий как минимум файлы __init__.py
и models.py
. В последних версиях Django models.py
больше не нужен, достаточно только __init__.py
.
Django-приложения могут содержать Python-модули, характерные для Django модули [19] (представления, URL’ы, модели, админскую панель, формы, метки шаблонов и т. д.), статичные файлы, шаблоны, миграции базы данных, команды управления, модульные тесты и пр. Нужно разбивать своё монолитное приложение на маленькие многократно используемые приложения [20] с простой логикой.
У вас должна быть возможность полностью описать назначение приложения одним-двумя короткими приложениями. Например: «Позволяет пользователю зарегистрировать и активировать по почте свой аккаунт».
Рекомендуется назвать папку проекта 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
│ └── commands
│ ├── __init__.py
│ └── update_portal_feeds.py
├── migrations
│ └── __init__.py
├── models.py
├── static
│ └── portal
│ ├── css
│ ├── img
│ └── js
├── templates
│ └── portal
│ └── index.html
├── templatetags
│ ├── __init__.py
│ └── portal.py
├── tests.py
├── urls.py
└── views.py
11 directories, 14 files
Благодаря такой структуре вы можете в любой момент экспортировать приложение в другой Python-пакет и снова его использовать. Можете даже опубликовать его в PyPi в качестве open source пакета или переместить в другую папку. У вас получится примерно такая структура проекта:
root@c5b96c395cfb:/test# tree -L 3
.
├── deploy
│ ├── chef
│ └── docker
│ ├── devel
│ └── production
├── docs
├── logs
├── manage.py
├── media
├── project
│ ├── __init__.py
│ ├── apps
│ │ ├── auth
│ │ ├── blog
│ │ ├── faq
│ │ ├── pages
│ │ ├── portal
│ │ └── users
│ ├── conf
│ ├── settings.py
│ ├── static
│ ├── templates
│ ├── urls.py
│ └── wsgi.py
└── static
└── admin
├── css
├── fonts
├── img
└── js
25 directories, 5 files
Конечно, реальный проект будет сложнее, но такая структура упрощает и делает более прозрачными многие аспекты.
Статичные файлы — это ресурсы (assets), которые не меняются по мере использования приложения. Например, JavaScript, CSS, изображения, шрифты и т. д. В Django они «накапливаются» в публичной директории в ходе развёртывания.
В режиме разработки — python manage.py runserver
— Django ищет статичные файлы с помощью настройки STATICFILES_FINDERS [21]. По умолчанию он пытается найти запрошенный файл в папках, перечисленных в STATICFILES_DIRS [22]. Если не находит, то ищет с помощью 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 [23] директорию. Это позволяет разрешать (resolution) ресурсы в виде статичных данных с помощью той же логики, что и у Django-сервера в режиме разработки, и собирать в одном месте для веб-сервера все статичные файлы.
Не забудьте выполнить collectstatic
в вашем production-окружении!
Давайте поговорим об управлении ресурсами (asset) production-окружения. Мы можем обеспечить наилучший UX, если воспользуемся политикой «у ресурсов не истекает срок действия» (assets never expire) (подробнее о ней можно почитать здесь [24]). Это означает, что все наши статичные файлы должны быть закешированы браузерами на недели, месяцы или даже годы. Иными словами, пользователи должны лишь единожды скачивать ресурсы!
Классная идея, и её можно реализовать всего в несколько строк в nginx-конфигурации для нашей папки со статичными файлами. Но что насчёт проверки актуальности кеша? Если пользователь лишь один раз скачивает наш ресурс, то что делать в том случае, если вы обновите логотип, шрифты, JavaScript или цвет текста в меню? Для решения этой задачи вам нужно при каждом развёртывании генерировать уникальные URL’ы и имена для каждого статичного файла!
Для этого можно использовать ManifestStaticFilesStorage [25] в качестве STATICFILES_STORAGE
(будьте осторожны, хеширование включается только в режиме DEBUG=false
) и выполнить команду collectstatic
. Это приведёт к снижению количества запросов ресурсов у вашего production-сайта и сделает его отрисовку гораздо быстрее.
Клёвая фича Django — закешированный загрузчик шаблона. Он не перезагружается и парсит файлы шаблона при каждой его отрисовке. Парсинг шаблона — очень дорогая операция, она требует много вычислительных ресурсов. По умолчанию Django-шаблоны парсятся при каждом запросе, а это плохо, особенно в production, где за короткий промежуток времени могут обрабатываться тысячи запросов.
В разделе конфигурации cached.Loader [26] можно найти хороший пример и подробности решения проблемы. Не используйте загрузчик в режиме разработки, потому что он не перезагружает отпарсенные шаблоны из файловой системы. Вам понадобится перезапускать свой проект, используя python manage.py startapp
, при каждом изменении шаблона. При разработке это может раздражать, зато идеально для production-окружения.
У Django есть отличная фича — команды управления [27]. Используйте их вместо изобретения велосипеда в виде написания скриптов на чистом Python для утилит вашего проекта.
Также обратите внимание на пакет Django Extensions [28], представляющий собой коллекцию кастомных расширений для Django. Возможно, кто-то уже реализовал ваши команды! Существует много распространённых целевых команд.
Для Django и Python есть тысячи готовых решений. Обратитесь к поисковикам, прежде чем писать что-то, что вовсе не уникально. Вероятно, уже есть подходящее решение.
Не надо усложнять. Сначала — гуглим! Установите найденный качественный пакет, сконфигурируйте, расширьте и интегрируйте в свой проект. И если есть возможность, внесите свой вклад в open source.
Вот вам для начала список моих собственных пакетов для Django:
Don’t repeat yourself (DRY)!
Я сторонник DRY-концепции, поэтому создал Django skeleton [30] — удобный инструмент с рядом приятных функций уже из коробки:
collectstatic
Django соберёт только папку dist.Это готовый к использованию Django-скелет для вашего следующего проекта, создаваемого с нуля. Надеюсь, он сэкономит вам кучу времени. Webpack имеет минимальную базовую конфигурацию, но также в него с помощью SASS установлены заранее сконфигурированные для обработки файлы .scss.
Автор: Mail.Ru Group
Источник [31]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/255061
Ссылки в тексте:
[1] фильтры и метки: https://docs.djangoproject.com/en/1.10/ref/templates/builtins/
[2] документацию: https://docs.djangoproject.com/
[3] virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/#virtualenv
[4] virtualenvwrapper: http://docs.python-guide.org/en/latest/dev/virtualenvs/#virtualenvwrapper
[5] VirtualBox: https://www.virtualbox.org/wiki/Downloads
[6] VMware: http://www.vmware.com/
[7] Parallels: http://www.parallels.com/
[8] Proxmox: https://www.proxmox.com/en/
[9] Vagrant: https://www.vagrantup.com/
[10] Docker: https://www.docker.com/
[11] requirements.txt: https://pip.readthedocs.io/en/1.1/requirements.html
[12] pip-tools: https://github.com/jazzband/pip-tools
[13] исключение из списка относительно небольшого пакета ломает большое количество пакетов в npm: https://www.reddit.com/r/programming/comments/4bjss2/an_11_line_npm_package_called_leftpad_with_only/d19vjcm/
[14] Django Template Names: https://github.com/phpdude/django-template-names
[15] Django Best Practices: http://django-best-practices.readthedocs.io/en/latest/applications.html#make-em-fat
[16] файле настроек Django-проекта: https://github.com/django/django/blob/master/django/conf/project_template/project_name/settings.py-tpl
[17] Django Split Settings: https://github.com/sobolevn/django-split-settings
[18] приложение: https://docs.djangoproject.com/en/1.10/ref/applications/
[19] характерные для Django модули: http://django-best-practices.readthedocs.io/en/latest/applications.html#code-organization
[20] многократно используемые приложения: https://docs.djangoproject.com/en/1.10/intro/reusable-apps/
[21] STATICFILES_FINDERS: https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-STATICFILES_FINDERS
[22] STATICFILES_DIRS: https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-STATICFILES_DIRS
[23] STATIC_ROOT: https://docs.djangoproject.com/en/1.10/ref/settings/#static-root
[24] здесь: https://css-tricks.com/strategies-for-cache-busting-css/
[25] ManifestStaticFilesStorage: https://docs.djangoproject.com/en/1.10/ref/contrib/staticfiles/#manifeststaticfilesstorage
[26] cached.Loader: https://docs.djangoproject.com/en/1.11/ref/templates/api/#django.template.loaders.cached.Loader
[27] команды управления: https://docs.djangoproject.com/en/1.10/howto/custom-management-commands/
[28] Django Extensions: https://github.com/django-extensions/django-extensions
[29] Django Macros URL: https://github.com/phpdude/django-macros-url
[30] Django skeleton: https://github.com/phpdude/docker-django-webpack-skeleton
[31] Источник: https://habrahabr.ru/post/328352/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.