Автоматическая установка Django (APS-пакет). Разворачиваем Django приложение в production на примере Telegram бота

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

Почему сборка пакетов так важна?

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

  • простота использования;
  • стабильность (при управлении версиями);
  • распространение.

При выборе приложения простота его установки учитывается пользователями далеко не в последнюю очередь, поэтому следует максимально упростить этот процесс. Сборка пакетов позволяет сделать программное обеспечение более доступным и простым в установке. Если установка не сложная, значит, пользователям будет проще приступить к работе с вашим программным обеспечением. Повысить доступность своего пакета при его публикации в каталоге пакетов Python (Python Package Index — PyPI) можно посредством таких утилит как pip или easy_install . (См. для получения дополнительной информации по данным средствам.)

Кроме того, управление версиями пакетов позволяет пользователям «закрепить» свои проекты, использующее ваше программное обеспечение, за его определенной версией. Например, закрепление за Pinax версии 0.9a2.dev1017 будет выглядеть таким образом:

Pinax==0.9a2.dev1017

Это принудительно свяжет проект с Pinax версии 0.9a2.dev1017.

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

Наиболее популярным методом публикации пакетов на PyPI (или на вашем собственном дистрибутивном сервере) является создание дистрибутива исходного кода для его свободной загрузки. Дистрибутив исходного кода — это стандартный способ сборки кода вашего проекта в качестве распространяемого модуля. Можно также создавать бинарные дистрибутивы, но для целей открытого использования имеет смысл также распространять свой код. Создавая дистрибутивы исходного кода, вы облегчаете пользователям процесс нахождения программного обеспечения в сети Интернет с помощью автоматизированных средств, а также его загрузки и установки. Этот процесс помогает не только в разработке на локальных системах, но и в развертывании вашего программного обеспечения.

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

Анатомия файла setup.py

Одно из предназначений сценария setup.py — это исполнение сборки пакетов вашего программного обеспечения и загрузки его на дистрибутивные серверы. На различных популярных репозиториях Python вы можете найти сценарии setup.py различного содержания. В данной статье рассмотрены основные из них. См. раздел для получения дополнительной информации.

Файл setup.py может использоваться для различных целей, но ниже мы приводим вариант, который позволяет выполнить следующие команды:

python setup.py register python setup.py sdist upload

Первая команда, register , получает информацию, которую выдает функция setup() сценария setup.py, и создает запись в PyPI для вашего пакета. Она ничего не загружает, вместо этого она создает метаданные о вашем проекте с тем, чтобы впоследствии вы могли выгрузить и разместить там свои версии ПО. Следующие две команды связаны: sdist upload создает дистрибутив исходного кода, а затем выгружает его в PyPI. Тем не менее, существуют несколько необходимых условий, таких как настройка собственного конфигурационного файла.pypirc и собственно запись содержимого в файл setup.py.

Прежде всего, сконфигурируйте файл.pypirc. Он должен находиться в основном каталоге, который может быть разным в зависимости от используемой операционной системы. В операционных системах UNIX®, Linux® и Mac OS Xзайти в этот каталог можно, набрав cd ~/ . Содержимое файла должно включать ваши учетные данные PyPI, как показано в.

Листинг 1. Типовой файл.pypirc
index-servers = pypi username:xxxxxxxxxxxxx password:xxxxxxxxxxxxx

Далее переходите в PyPI и зарегистрируйтесь для создания учетной записи (не волнуйтесь — это бесплатно). Введите такое же имя пользователя и пароль, что и в файле.pypirc каталога PyPI, и убедитесь, что файл называется именно ~/.pypirc .

Теперь при написании своего сценария setup.py вам необходимо решить, что будет отображаться на индексной странице PyPI, и определиться с названием проекта. Начнем с копирования шаблона для setup.py, который я использую для проектов (см. ). Пропустив импортирование и функции, взгляните на нижнюю часть шаблона и подумайте, что вам нужно изменить с учетом особенностей вашего проекта. См. для получения ссылки на полный сценарий.

Листинг 2. Шаблон setup.py
PACKAGE = "" NAME = "" DESCRIPTION = "" AUTHOR = "" AUTHOR_EMAIL = "" URL = "" VERSION = __import__(PACKAGE).__version__ setup(name=NAME, version=VERSION, description=DESCRIPTION, long_description=read("README.rst"), author=AUTHOR, author_email=AUTHOR_EMAIL, license="BSD", url=URL, packages=find_packages(exclude=["tests.*", "tests"]), package_data=find_package_data(PACKAGE, only_in_packages=False), classifiers=[ "Development Status:: 3 - Alpha", "Environment:: Web Environment", "Intended Audience:: Developers", "License:: OSI Approved:: BSD License", "Operating System:: OS Independent", "Programming Language:: Python", "Framework:: Django", ], zip_safe=False,)

Во-первых, следует помнить, что этот шаблон подразумевает наличие в вашем проекте двух различных файлов. Первый используется для длинного описания (long_description). Он считывает содержимое файла README.rst, находящегося в той же директории, что и setup.py, и передает это содержимое как строку в параметр long_description . Этот файл заполняет целевую страницу в PyPI, поэтому желательно дать в нем краткое описание проекта и привести несколько примеров его использования. Второй файл — файл пакетов __init__.py . Мы не приводим здесь его развернутого описания, однако когда строка, которая определяет переменную VERSION, импортирует ваш пакет, Python требуется файл __init__.py, поэтому в данном модуле следует определить переменную с названием __version__ . Пока достаточно записать это строкой:

# __init__.py __version__ = "0.1"

Теперь взглянем на остальные исходные параметры.

  • PACKAGE — это пакет Python в вашем проекте. Это папка верхнего уровня, содержащая модуль __init__.py, который должен находиться в той же директории, что и файл setup.py, например: /- |- README.rst |- setup.py |- dogs |- __init__.py |- catcher.py

    Итак, в данном случае вашим пакетом будет dogs .

  • Параметр NAME обычно схож или совпадает с названием пакета PACKAGE, но вы можете задать любое имя в зависимости от ваших предпочтений. NAME — это параметр, по которому пользователи будут ссылаться на ваше программное обеспечение, имя, под которым ваше программное обеспечение будет отображаться в PyPI и, что более важно, под которым пользователи будут его устанавливать (например, pip install NAME).
  • DESCRIPTION — краткое описание вашего проекта. Одного предложения будет достаточно.
  • AUTHOR и AUTHOR_EMAIL — это соответственно: ваше имя и адрес электронной почты. Эта информация не обязательна, но указать свой адрес электронной почты для пользователей, которые пожелают обратиться к вам с вопросами по проекту, это правило хорошего тона.
  • URL — это URL-адрес проекта. Этот параметр может быть как Web-сайтом проекта или репозиторием Github, так и любым другим URL-адресом. Эта информация также не обязательна.

Возможно, вы пожелаете указать лицензионные условия и классификаторы. Более детальная информация о создании файла setup.py приведена в документации по Python. (См. .)

Управление версиями

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

Стандарт по управлению версиями пакетов Python можно найти в PEP-386 (Предложение по развитию Python) (см. .) В нем прописаны правила практического характера. Даже если вы не читали, не поняли или даже не согласны с PEP, все же разумнее придерживаться установленных в нем правил, так как именно они все чаще применяются при создании разработчиками приложений Python.

Кроме того, управление версиями необходимо не только для разработки стабильных версий, загружаемых в PyPI, но также полезно для разработки версий с суффиксом devNN . Как правило, размещение разрабатываемых версий в PyPI не поощряется, но можно сделать их общедоступными посредством настройки собственного общедоступного (или закрытого) дистрибутивного сервера. В этом случае пользователи, которые желают воспользоваться последней версией, могут указать это в своем pip -файле requirements.txt. Ниже приведено несколько примеров управления версиями:

1.0.1 # 1.0.1 final release 1.0.2a # 1.0.2 Alpha (for Alpha, after Dev releases) 1.0.2a.dev5 # 1.0.2 Alpha, Dev release #5

Публикация

Пользователям не удастся отыскать и установить ваше программное обеспечение, если оно не будет опубликовано. Обычно пакеты публикуются в PyPI. Настроив свой конфигурационный файл.pypirc и передав команду upload в setup.py, вы передадите пакет в PyPI. Как правило, эта операция производится совместно с созданием дистрибутива исходного кода:

python setup.py sdist upload

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

python setup.py sdist upload -r mydist

Настройка собственного дистрибутивного сервера

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

pip install MyPackage

Установить последнюю стабильную версию в PyPI. Но если вы позднее добавите разрабатываемые версии, то эта команда остановит выполнение и установит последнюю версию, т. е. разрабатываемую версию. Как правило, полезно всегда закреплять проекты за определенной версией, но это делают не все пользователи. Поэтому следует убедиться в том, что, если номер версии не указан, по умолчанию возвращается последняя стабильная версия.

Одним из способов убить одним выстрелом двух зайцев (предоставлять только стабильные версии для использования pip по умолчанию и обеспечить пользователям возможность устанавливать пакеты разрабатываемых версий) является создание собственного дистрибутивного сервера. Проект Pinax применяет эту технологию для всех своих разрабатываемых версий на сайте http://dist.pinaxproject.com. (См. .)

Дистрибутивный сервер представляет собой каталог, работающий по протоколу HTTP и предоставляющий доступ к файлам на вашем сервере. Он должен иметь следующую файловую структуру:

/index-name/package-name/package-name-version.tar.gz

В дальнейшем при желании сервер можно сделать закрытым путем конфигурирования Basic-Auth на своем Web-сервере. Вы также можете добавить некоторые другие средства для выгрузки дистрибутивов исходного кода. Для этого необходимо добавить код управления выгрузкой, разобрать название файла и создать пути по каталогу, которые бы соответствовали приведенной выше схеме. Такая структура принята для проектов Pinax с использованием нескольких репозиториев.

pip и virtualenv

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

Инструмент pip можно установить напрямую, однако я рекомендую использовать его в рамках функциональности virtualenv . (См. .) Я также советую прибегать к virtualenv во всех случаях, когда вы имеете дело с Python, так как окружение Python при этом остается незагроможденным. Подобно тому, как виртуальная машина позволяет запускать несколько операционных систем одновременно, virtualenv позволяет запускать несколько окружений Python одновременно. Я ничего не устанавливаю в своей системе Python, а просто создаю новое виртуальное окружение для каждого нового проекта или утилиты, над которыми работаю.

Теперь, когда инструмент virtualenv установлен, можно и немного поиграть:

$ mkvirtualenv -no-site-packages testing $ pip install Pinax $ pip freeze|grep Pinax $ pip uninstall Pinax $ pip install -extra-index-url=http://dist.pinaxproject.com/fresh-start/ Pinax==0.9a2.dev1017 $ pip freeze|grep Pinax

Обратите внимание, что первая установка pip загружается и устанавливается с PyPI. Команда pip freeze выводит все версии пакетов, установленных в текущем virtualenv . Команда pip uninstall делает именно то, о чем говорит ее название: удаляет себя из virtualenv . Затем мы устанавливаем разрабатываемую версию из репозитория перезагрузки по адресу http://dist.pinaxproject.com, чтобы получить разрабатываемую версию Pinax 0.9a2.dev1017.

Переходить на Web-сайт, загружать tar-архивы и создавать коды символических ссылок (symlink) на каталог site-packages не требуется. (Раньше я так и делал, и это вызывало много проблем.) Все это ваши пользователи получают благодаря хорошей сборке пакетов, публикации и управлению версиями созданного проекта.

Заключение

Надеюсь, материала, изложенного в данной статье, достаточно для того, чтобы вы смогли приступить к работе. В разделе приведены ссылки на документацию, которая может помочь вам глубже разобраться в упомянутых вопросах. Если у вас возникнут какие-либо вопросы, заходите на Freenode и ищите меня в таких разделах чата как #pinax и #django-social (по псевдониму "paltman") или в Twitter (@paltman).

  • Tutorial

Введение

Многие начинающие веб разработчики размышляют о том, где бы разместить свое творение. Обычно для этих целей применяются машины под управлением *NIX подобных систем. Мой выбор остановился на Raspberry PI, поскольку малинка:
  • работает под управлением полноценного Linux,
  • долгое время лежит на столе и пылится.
Я хочу рассказать о том, как настроить сервер, работающий в сети с динамическим внешним IP адресом. Для запуска крупных проектов такое решение не годится, а для демонстрации своего портфолио и персонального применения вполне подойдет.

Нам понадобятся

  1. Raspberry PI модели B, B+ или Raspberry PI 2 (поскольку на платах этих моделей имеется Ethernet) с установленной Raspbian и активированным SSH сервером. О настройке можно почитать , или . Помимо Raspian для малинки существует большое количество альтернативных дистрибутивов . Тут, как говорится, «на любой вкус и цвет».
  2. Рабочее Django приложение.
  3. Роутер с поддержкой DDNS. Этот пункт не обязателен, поскольку DDNS можно настроить на самой малинке.
Я буду работать с малинкой модели B+.

Подготовка

На малинке установлена Raspbian 7.8.
Для начала необходимо найти малинку в сети, чтобы подключиться к ней по ssh.
nmap -sP 192.168.1.1/24 | grep raspberry

В моем случае в сети две малинки, одна из которых моя с IP адресом 192.168.1.100. В некоторых сетях nmap не показывает сетевые имена устройств.


В этом случае найти raspberry pi можно по MAC-адресу, который имеет префикс B8:27:EB.
sudo nmap -sP -n 192.168.1.1/24 | grep -B 2 B8:27:EB


Параметр -B для grep определяет какое количество предшествующих строк следует вывести.

Подключаемся к малинке по ssh.
ssh [email protected]


Для начала разгоним малинку до 1 ГГц с помощью raspi-config .
Устанавливаем питоновский менеджер пакетов
sudo apt-get install python-pip

Переходим к установке необходимых пакетов. В моем web приложении используется СУБД MySQL. В качестве Frontend и Backend используется nginx и gunicorn соответственно.
sudo apt-get install nginx gunicorn mysql-client mysql-server python-mysqldb


В процессе установки mysql необходимо ввести данные для root пользователя СУБД. python-mysqldb - драйвер, необходимый при работе с моделями в Django. Django установим из питоновских репозиториев.
sudo pip install django

На момент написания статьи актуальные версии nginx и gunicorn в репозиториях для малинки 1.2.1 и 0.14.5 соответственно. Версия MySQL для малинки 5.5. Так же для работы с Django необходимо установить SciPy .
sudo apt-get install python-scipy

Nginx 1.2.1 устарел. Более новый можно собрать из исходников . Свежий gunicorn можно установить из питоновских репозиториев.

Настройка сервера

Размещаем web-приложение на малинке (например в /home/pi).
Если у Вас есть рабочие конфиги, то достаточно их скопировать в соответствующие директории:
  • для nginx /etc/nginx/sites-enabled/
  • для gunicorn /etc/gunicorn.d/
C nginx ничего сложного нет. Я бы хотел обратить внимание на настройки для gunicorn.
CONFIG = { "mode": "wsgi", "working_dir": "/home/pi/project", #"working_dir": "/home/pi/project/project", "user": "www-data", "group": "www-data", "python": "/usr/bin/python", "args": ("--bind=127.0.0.1:8081", "--workers=5", # 5 достаточно для малинки "--graceful-timeout=60", "--timeout=60", #"--debug", #"wsgi:application", "project.wsgi",), }

Если working_dir (путь к файлу wsgy.py) указать "/home/pi/project/project" , а в args указать "wsgi:application" , то на малинке воркеры сначала стартуют, потом умирают без указания причины (под Ubuntu, например, gunicorn работает с обоими вариантами настроек).

Перенос MySQL

Дамп имеющейся БД можно сделать с помощью утилиты mysqldump .
mysqldump -u root -p dbname > dbname.sql

Полученный файл состоит из набора SQL-инструкций, которые восстанавливают структуру, а так же информацию, хранимую в базе данных.


На малинке создаем базу данных. Запускаем mysql shell.
mysql -u root -p

Добавляем новую базу данных.
mysql> create database dbname character set utf8 collate utf8_general_ci; mysql> grant all privileges on dbname.* to someusr@localhost identified by "somepassword";
Восстанавливаем данные с дампа. При размере дампа в 162 Мб время восстановления составило около 10 минут.
mysql -u root -p dbname < dbname.sql

Следует отметить, что базы данных лучше хранить на внешнем накопителе, иначе micro SD карта может быстро придти в негодность из-за частых операций записи. Как это сделать можно почитать . Конфиг mysql расположен по пути /etc/mysql/my.cnf

Проверка

Перезапускаем nginx и gunicorn. Если все настроено верно, можно открыть главную страницу.


Переходим к нагрузочному тестированию. Установим apache benchmark.
sudo apt-get install apache2-utils

Протестируем в 4 потока 1000 запросами Raspberry PI модель B+.
ab -c 4 -n 1000 http://192.168.1.100/

Vladislav@vladislav-N53SV:~$ ab -c 4 -n 1000 http://192.168.1.100/ This is ApacheBench, Version 2.3 <$Revision: 1528965 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 192.168.1.100 (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests Completed 1000 requests Finished 1000 requests Server Software: nginx/1.8.0 Server Hostname: 192.168.1.100 Server Port: 80 Document Path: / Document Length: 24839 bytes Concurrency Level: 4 Time taken for tests: 1309.607 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 25018000 bytes HTML transferred: 24839000 bytes Requests per second: 0.76 [#/sec] (mean) Time per request: 5238.429 (mean) Time per request: 1309.607 (mean, across all concurrent requests) Transfer rate: 18.66 received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 0.1 1 1 Processing: 4924 5237 91.4 5227 6419 Waiting: 4919 5227 91.3 5217 6403 Total: 4925 5238 91.4 5228 6420 Percentage of the requests served within a certain time (ms) 50% 5228 66% 5245 75% 5255 80% 5265 90% 5296 95% 5335 98% 5382 99% 5667 100% 6420 (longest request)
Запросы идут медленно, поскольку большую часть времени запроса занимает работа с БД. Ко мне недавно пришла Raspberry PI 2 модель B. Посмотрим на что она способна c теми же настройками и данными.
vladislav@vladislav-N53SV:~$ ab -c 4 -n 1000 http://192.168.1.14/ This is ApacheBench, Version 2.3 <$Revision: 1528965 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 192.168.1.14 (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests Completed 1000 requests Finished 1000 requests Server Software: nginx/1.8.0 Server Hostname: 192.168.1.14 Server Port: 80 Document Path: / Document Length: 24838 bytes Concurrency Level: 4 Time taken for tests: 170.083 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 25017000 bytes HTML transferred: 24838000 bytes Requests per second: 5.88 [#/sec] (mean) Time per request: 680.330 (mean) Time per request: 170.083 (mean, across all concurrent requests) Transfer rate: 143.64 received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 0.1 1 1 Processing: 569 678 104.6 650 1338 Waiting: 567 676 104.1 647 1334 Total: 569 679 104.6 651 1338 Percentage of the requests served within a certain time (ms) 50% 651 66% 682 75% 708 80% 727 90% 796 95% 890 98% 1045 99% 1138 100% 1338 (longest request)
Raspberry PI 2 обрабатывает запросы в среднем в 6,16 раз быстрее. Разработчики малинки не обманули.

Настройка DDNS

Настроить DDNS можно на роутере или на самой малинке. Я выбираю No IP , поскольку пользуюсь им несколько лет. Рассмотрим бесплатное использование.

Регистрация хоста

Если у вас есть учетная запись - проходим авторизацию, иначе регистрируемся . После авторизации попадаем сюда .


Кликаем AddHost и заполняем форму.


Внизу кликаем кнопку AddHost
Хост добавлен. Справа от имени хоста отображается внешний IP адрес Вашей сети.


Настраиваем DDNS на роутере
Для примера я настрою DDNS на ASUS RT-N56U с прошивкой от padavan версии 3.4.3.9-091. Открываем в страницу меню роутера
(например 192.168.1.1). WAN->DDNS.


Выбираем сервис no-ip.com, указываем регистрационные данные, а так же наш добавленный хост (technopark-test.ddns.net).
Остальные параметры выставляем по собственному желанию.


Теперь при смене внешнего IP адреса наше приложение остается доступным в сети.
Настройка переадресации портов
Нам нужно, чтобы при обращении к хосту малинка отдавала веб приложение. Роутер занимается перенаправлением входящих пакетов, пришедших из вне с порта X на внутренный порт Y. В меню роутера переходим WAN->Переадресация портов. Необходимо перенаправлять внешний 80 порт на 80 порт малинки. Добавим новое правило и применим изменения.

Теперь малинка обрабатывает все приходящие пакеты на 80 порту. Проверим, введя в адресной строке браузера хост, полученный в No IP.


Теперь наше веб приложение доступно для пользователей сети Интернет.
Настраиваем DDNS на малинке
Этот вариант не подходит, если малинка имеет частный IP , поскольку она будет оправлять свой локальный IP адрес на на сервис No IP. Это еще один способ узнать IP адрес малинки локальной сети. Установим DDNS клиент.
sudo apt-get install ddclient

Во время установки необходимо выбрать сервис. Выбираем other и вводим dynupdate.no-ip.com, протокол dyndns2, имя пользователя, пароль, интерфейс - eth0, имя хоста.


Для проверки я выставил интервал обновления IP в 60 секунд. В файле /etc/default/ddclient необходимо выставить значение daemon_interval=«60».

Десерт

Моя малинка давно лежала на столе и пылилась, вместе с tm1638 и , выводя показания температуры и влажности в помещении и прочей информации.
Все же мне было интересно попробовать управлять GPIO Raspberry PI из django. Я разработал простое web приложение, которое объединило мои ранние наработки. Оно позволяет посмотреть температуру и влажность, измеренные с помощью DHT11, некоторую полезную информацию, управляет 8-ми релейным модулем (который может быть использован для управления электроприборами) и отправляет текст на tm1638.

Для управления GPIO необходимо запускать сервер с правами root. Это потенциальная уязвимость.
Полноценное использования web приложения предполагает работу сервера без прав суперпользователя, настройку https, добавление возможности администрирования учетных записей, ведение логов, разделение доступа к управляемым устройствам, работу электроприборов по расписанию и многое другое.
Впрочем это уже совсем другая история статья.

Заключение

Имея Raspberry PI моделей B, B+ или Raspberry PI 2, power bank , а так же «open» Ethernet jack получаем компактный сервер, который можно использовать для демонстрации своих наработок. Настройка сервера для Django приложений на Raspberry PI под управлением Raspbian мало чем отличится от любой другой сборки Linux. Пакеты в репозиториях могут быть устаревшими. Для работы с новыми версиями можно вручную собирать программы из исходников.
Добавить метки

Описание процесса быстрой установки Django из Панели управления Plesk.

Статья устаревшая, относится к панели Plesk, которая больше не будет поддерживаться.

"Профи" (с подключенным SSH-доступом), "Плюс", "VIP", а так же во время тестового доступа по этим тарифам, и предлагает быстро создать новый проект Django. Для того чтобы это сделать необходимо:

  1. Войти в Панель управления (учетные данные для доступа вы получаете сразу после .)
  2. В главном меню панели управления выбрать пункт "Домены".
  3. Выбрать домен, на который планируется установить Django.
  4. В группе "Приложения и услуги" выбрать пункт "Веб-приложения".
  5. В строке поиска ввести django, после чего начать установку.
  6. Внимательно прочитать лицензионное соглашение, после согласия возможна дальнейшая установка.
  7. В разделе "параметры установки" выбрать целевой каталог для установки приложения.
  8. В разделе "Настройки базы данных" необходимо выбрать тип (На данный момент поддерживается MySQL и PostgreSQL), указать имя пользователя и пароль базы данных - данные параметры будут автоматически внесены в файл settings.py.
  9. Далее необходимо ввести логин, адрес электронной почты и пароль администратора, данный пароль будет использован далее.
  10. В разделе "Личные настройки" при установке пакета предоставляется доступ к настройкам Django для нового проекта, в частности:
    • "Имя Django проекта" - имя проекта Django, который будет создан.
    • "Имя директории с проектами Django" - имя директории где будут располагатьcя проекты django, данная директория будет располагатьcя в папке private на вашей площадке.
    • "Активировать интерфейс администратора" - при выборе данной опции django разворачивается с включенным админ-разделом, производится синхронизация базы данных, логин и пароль администратора устанавливается в значения, указанные выше.
    • "Свой virtualenv " - данная опция подразумевает создание собственной "инстанции" python, при этом вы получаете возможность устанавливать свои модули, которые могут отличаться от установленных на хостинг, при включении, дисковое пространство, занимаемое приложением, несколько увеличивается.

После установки данного приложения, по адресу http://имя_домена/директория_установки/ будет доступна стандартная страница установленной Django последней стабильной версии с Python 2.6.2, для дальнейших операций и выполнения стандартных действий, например, управление проектом через manage.py, необходим полный ssh доступ. Данная услуга доступна по-умолчанию для тарифных планов "Плюс" и "VIP", на тарифе "Профи" существует возможность получить полный ssh доступ, расширив тариф дополнительной опцией через систему биллинга . Если данный пакет по каким-либо причинам не удовлетворяет требованиям Вашего проекта, Вы можете создать Django-проект

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

Мы создадим веб-приложение, у которого будет панель администратора и возможность загружать загадки, а у пользователей, соответственно, возможность отвечать на них. Во время разработки будут использоваться Python 3.4.3 и Django 1.9.1.

Устанавливаем Django

Делается это очень просто, в командной строке нужно написать: pip install Django==1.9.1 .

Создаем проект

Если вы правильно установили Django, то после запуска django-admin --version вы увидите текущую версию фреймворка. Теперь создадим проект. Это можно сделать следующим образом: django-admin startproject django_example .

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

  • django_example/__init__.py - пустой файл, который говорит Python, что данная директория должна восприниматься в качестве пакета.
  • django_example/settings.py содержит конфигурацию нашего проекта.
  • django_example/urls.py - здесь объявляются URL.
  • django_example/wsgi.py - с помощью него приложение может работать с веб-сервером по протоколу WSGI.
  • manage.py позволяет взаимодействовать с проектом.

Теперь пришло время запустить наше приложение. Для этого в командной строке нужно написать python manage.py runserver . После этого в адресной строке браузера нужно написать: http://127.0.0.1:8000/ . Если вы увидели «You have unapplied migrations; your app may not work properly until they are applied.», то не волнуйтесь, мы вернемся к этому чуть позже.

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

Определим различие между проектом и приложением. Приложение - это программа, которая что-то делает, а проект - это группа приложений.

Итак, приступим к созданию приложения. Это делается следующим образом: python manage.py startapp riddles .
Как только приложение создано, давайте напишем простой вид, по правилам Django все виды должны храниться в файле views.py .

riddles/views.py

From django.http import HttpResponse def index(request): return HttpResponse("Hello, World!")

Теперь, чтобы привязать наш вид к URL, создадим файл urls.py .

riddles/urls.py

From django.conf.urls import url from . import views app_name = "riddles" urlpatterns = [ url(r"^$", views.index, name="index"), ]

В urls.py мы должны написать следующее:

django_example/urls.py

From django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r"^riddles/", include("riddles.urls")), url(r"^admin/", admin.site.urls), ]

Теперь, если мы запустим наше приложение http://127.0.0.1:8000/riddles/ , мы увидим «Hello, World!».

Установка базы данных

По умолчанию в Django используется SQLite, если она вас не устраивает, то вы с нашей статьей, в которой мы рассказываем, как безболезненно перейти с SQLite на MySQL.

Теперь откроем django_example/settings.py и взглянем на переменную INSTALLED_APPS , она хранит все приложения, которые активны в текущем проекте. По умолчанию она содержит:

  • django.contrib.admin - админка, скоро мы ей воспользуемся.
  • django.contrib.auth - система аутентификации.
  • django.contrib.contenttypes - фреймворк для content types.
  • django.contrib.sessions - сессионный фреймворк.
  • django.contrib.messages - фреймворк для отправки сообщений.
  • django.contrib.staticfiles - фреймворк для работы со статичными файлами.

Некоторые из этих приложений используют базы данных, но они еще не установлены, поэтому мы и видели «You have unapplied migrations; your app may not work properly until they are applied.». Поправить это можно следующим образом: python manage.py migrate . Вы должны увидеть следующее:

Operations to perform: Apply all migrations: admin, sessions, auth, contenttypes Running migrations: Rendering model states... DONE Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying sessions.0001_initial... OK

Теперь создадим нашу модель. Для начала создадим Riddle и Option . В Riddle будет содержаться загадка, в Option - один из возможных ответов на нее.

riddles/models.py

From django.db import models class Riddle(models.Model): riddle_text = models.CharField(max_length=255) pub_date = models.DateTimeField("date published") class Option(models.Model): riddle = models.ForeignKey(Riddle, on_delete=models.CASCADE) text = models.CharField(max_length=255) correct = models.BooleanField(default=False)

Данная модель обеспечивает Django информацией, необходимой для создания схемы базы данных и database-access API для доступа к объектам. Теперь нам нужно привязать наше приложение к нашему проекту, делается это следующим образом:

django_example/settings.py

INSTALLED_APPS = [ "riddles.apps.RiddlesConfig", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", ]

После этого нужно сделать миграцию: python manage.py makemigrations riddles . Вы должны увидеть следующее:

Migrations for "riddles": 0001_initial.py: - Create model Option - Create model Riddle - Add field riddle to option

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

Проверить, что сделает миграция, можно так: python manage.py sqlmigrate riddles 0001 (0001 - версия миграции, которую мы хотим проверить). На выходе мы получим:

BEGIN; -- -- Create model Option -- CREATE TABLE "riddles_option" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(255) NOT NULL, "correct" bool NOT NULL); -- -- Create model Riddle -- CREATE TABLE "riddles_riddle" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "riddle_text" varchar(255) NOT NULL, "pub_date" datetime NOT NULL); -- -- Add field riddle to option -- ALTER TABLE "riddles_option" RENAME TO "riddles_option__old"; CREATE TABLE "riddles_option" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(255) NOT NULL, "correct" bool NOT NULL, "riddle_id" integer NOT NULL REFERENCES "riddles_riddle" ("id")); INSERT INTO "riddles_option" ("riddle_id", "id", "text", "correct") SELECT NULL, "id", "text", "correct" FROM "riddles_option__old"; DROP TABLE "riddles_option__old"; CREATE INDEX "riddles_option_a7c97949" ON "riddles_option" ("riddle_id"); COMMIT;

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

Теперь мы можем начать пользоваться панелью администратора. Но для этого нам нужен пользователь. Создать его можно следующим образом: python manage.py createsuperuser . После этого запускаем сервер, если он не запущен, и переходим на http://127.0.0.1:8000/admin/ . Вы увидите следующее:

Теперь дадим админу возможность изменять наши модели. Делается это так:

riddles/admin.py

From django.contrib import admin from .models import Option, Riddle admin.site.register(Riddle) admin.site.register(Option)

Вот что получится в итоге:

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

Главная страница

Что нам нужно для создания главной страницы?

  • Templates: скелет нашей страницы.
  • Views: функция на Python для отображения контента.

Начнем с шаблонов. Создадим папку templates внутри папки riddle , а в ней создадим index.html .

riddles/templates/index.html

Available Riddles

{% if message %}

{{ message }}

  • {{ riddle.riddle_text }}
  • {% endfor %}
{% else %} {% endif %}

Теперь создадим макет для ответов:

{{ riddle.riddle_text }}

{% if error_message %}

{{ error_message }}

{% endif %}

{% endfor %}

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

riddles/views.py

From django.http.response import HttpResponse from django.shortcuts import get_object_or_404, render from .models import Riddle, Option def index(request): return render(request, "index.html", {"latest_riddles": Riddle.objects.order_by("-pub_date")[:5]}) def detail(request, riddle_id): return render(request, "answer.html", {"riddle": get_object_or_404(Riddle, pk=riddle_id)}) def answer(request, riddle_id): riddle = get_object_or_404(Riddle, pk=riddle_id) try: option = riddle.option_set.get(pk=request.POST["option"]) except (KeyError, Option.DoesNotExist): return render(request, "answer.html", {"riddle": riddle, "error_message": "Option does not exist"}) else: if option.correct: return render(request, "index.html", {"latest_riddles": Riddle.objects.order_by("-pub_date")[:5], "message": "Nice! Choose another one!"}) else: return render(request, "answer.html", {"riddle": riddle, "error_message": "Wrong Answer!"})

Давайте пройдемся по каждой функции отдельно:

  • index: Index использует функцию render . На вход она получает HttpRequest, местонахождение шаблона и его содержимое, а возвращает HttpResponse с окончательным html.
  • detail: Detail делает практически то же самое, но только функция get_object_or_404 возвращает HttpResponse404, если нужный объект не был найден.
  • answer: Answer ищет предоставленную загадку (и возвращает 404, если она не найдена) и проверяет правильность ответа.

Теперь добавим наши функции в urls.py:

riddles/urls.py

From django.conf.urls import url from . import views app_name = "riddles" urlpatterns = [ url(r"^$", views.index, name="index"), url(r"^(?P+)/$", views.detail, name="detail"), url(r"^(?P+)/answer/$", views.answer, name="answer") ]

Добавим немного стилей

Для начала создадим директорию static , а в ней создадим файл main.css .

riddles/static/main.css

Body{ margin:40px auto; max-width:650px; line-height:1.6; font-size:18px; color:#444; padding:0 10px; } h1,h2,h3{ line-height:1.2; text-align: center; } a { color: blue; } form { margin: 0 auto; padding: 1em; border: 1px solid #CCC; border-radius: 1em; } form div + div { margin-top: 1em; } label { display: inline-block; text-align: center; width: 40%; } input { font: 1em sans-serif; -moz-box-sizing: border-box; box-sizing: border-box; border: 1px solid #999; width: 50%; } input:focus { border-color: #000; } p, div.button { text-align: center; } p.error-message { color: lightcoral; }

Немного изменим наши шаблоны:

riddles/templates/index.html

{% load staticfiles %}

Available Riddles

{% if message %}

{{ message }}

{% endif %} {% if latest_riddles %}
    {% for riddle in latest_riddles %}
  • {{ riddle.riddle_text }}
  • {% endfor %}
{% else %}

No riddles are available right now.

{% endif %}

riddles/templates/answer.html

{% load staticfiles %}

{{ riddle.riddle_text }}

{% if error_message %}

{{ error_message }}

{% endif %}
{% csrf_token %} {% for option in riddle.option_set.all %}
{% endfor %}

Первая строка загружает статические файлы, потом мы используем {% static "#" %} , где # - путь к вашему файлу. Аналогичная процедура проводится и для JavaScript.

Теперь вы можете создавать свои собственные приложения на Django.

В этом руководстве мы рассмотрим основные ошибки 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), автосоздание путей шаблонов на основе структур приложения, интегрирование умного кеширования и многое другое.