Веб програмування. Мова програмування Python: розробка веб-застосунків для нового покоління

У статті хотілося б порушити питання відмінності використання Python для web-розробки в порівнянні з ним на PHP. Сподіваюся, стаття не приведе до холіварів, оскільки вона зовсім не про те, яка мова краща чи гірша, а виключно про технічні особливості Python.

Трохи про самі мови

PHP- Веб-орієнтована мова, створена, щоб вмирати (у хорошому розумінні слова). З низькорівневої точки зору додаток на PHP є швидше набір окремих скриптів можливо з єдиною семантичною точкою входу.

Python - універсальна мовапрограмування, застосовний навіть у Інтернеті. З технічної точки зору web-додаток на Python - повноцінний додаток, завантажений в пам'ять, що має свій внутрішній стан, що зберігається від запиту до запиту.

Виходячи з вищеописаних особливостей випливають і відмінності у обробці помилок у web-додатках. У PHP існує цілий зоопарк типів помилок (errors, exceptions), далеко не кожну з яких можна перехопити, хоча це (неможливість перехоплення) і не має великого значення, оскільки програма живе рівно стільки, скільки обробляється один запит. Неперехоплена помилка просто призводить до дострокового виходу з обробника та видалення програми з пам'яті. Новий запит буде оброблятись новим «чистим» додатком. У Python програма постійно знаходиться в пам'яті, обробляючи безліч запитів без «перезавантаження». Таким чином, підтримувати правильний передбачуваний стан програми вкрай важливо. Всі помилки використовують стандартний механізм виключень і можуть бути перехоплені (хіба що, за винятком SyntaxError). Неперехоплена помилка призведе до завершення програми, яка буде потрібно перезапускати ззовні.

Існує безліч способів «приготувати» PHP та Python для Інтернету. Далі я зупинюся на двох найбільш мені знайомих (і здається найбільш популярних) – PHP + FastCGI (php-fpm) та Python + WSGI (uWSGI). Звичайно ж, перед обома цими зв'язками передбачається наявність фронтенд-сервера (наприклад, Nginx).

Підтримка багатопоточності Python

Запуск сервера програм (наприклад, uWSGI) призводить до завантаження інтерпретатора Python у пам'ять, а потім завантаження самого веб-додатку. Зазвичай bootstrap-модуль додатка імпортує необхідні йому модулі, здійснює виклики ініціалізації та у результаті експортує підготовлений callable об'єкт, відповідний специфікації WSGI. Як відомо, при першому імпорті Python-модулів код всередині них виконується, у тому числі створюються та ініціалізуються значеннями змінні. Між двома послідовними запитами HTTP стан інтерпретатора не скидається, отже зберігаються значення всіх змінних рівня модуля.

Напишемо найпростіший WSGI-додаток, який наочно продемонструє вищеописаний на прикладі:

N = 0 def app(env, start_response): global n n += 1 response = "%.6d" % n start_response("200 OK", [("Content-Type", "text/plain")]) return
Тут nє змінною модуля і вона буде створена зі значенням 0 при завантаженні програми на згадку наступною командою:

Uwsgi --socket 127.0.0.1:8080 --protocol http --single-interpreter --processes 1 -w app:app
Сама програма просто виводить на сторінку значення змінної n. Для затятих PHP програмістіввоно виглядає безглуздим, оскільки «має» щоразу виводити на сторінку рядок "000001".

Проведемо тест:

Ab-n 500 -c 50 http://127.0.0.1:8080/ curl http://127.0.0.1:8080
В результаті ми отримаємо рядок "000501", що підтверджує наше твердження, про те, що програма знаходиться завантаженою в пам'ять uwsgi і зберігає свій стан між запитами.

Якщо запустити uWSGI з параметром --processes 2і провести той же тест, кілька послідовних викликів curl покажуть, що ми маємо вже 2 різні зростаючі послідовності. Так як ab посилає 500 запитів, приблизно половина з них посідає один процес uWSGI, а решта - на другий. Очікувані значення, що обертаються curl будуть приблизно "000220"і "000280". Інтерпретатор Python, зважаючи на все, один на процес, і ми маємо 2 незалежних оточення і реальну паралельну обробку запитів (у випадку багатоядерного процесора).

Python підтримує потоки як частину мови. p align="justify"> Класична реалізація (CPython) використовує нативні потоки OS, але є GIL - в один момент часу виконується тільки один потік. У цьому однаково можливі проблеми race condition, оскільки навіть n += 1 є атомарної операцією.

Дизасемблуємо python код

«Дизасемблуємо» наш WSGI-додаток:
import dis n = 0 def app(env, start_response): global n n += 1 return if "__main__" == __name__: print(dis.dis(app))
8 0 LOAD_GLOBAL 0 (n) 3 LOAD_CONST 1 (1) 6 INPLACE_ADD 7 STORE_GLOBAL 0 (n) 10 10 LOAD_GLOBAL 1 (bytes) 13 LOAD_GLOBAL 2 (str) 16 LOAD_GLOBAL 0 (n) 19 CALL_FUNCTION 1 (1 positional, 0 keyword pair ) 22 LOAD_CONST 2 ("utf-8") 25 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 28 BUILD_LIST 1 31 RETURN_VALUE
Видно, що інкремент у нашій програмі займає 4 операції. Переривання GIL може наступити будь-який з них.


Збільшення кількості потоків при відсутності очікування IO в коді обробників HTTP-запитів не призводить до прискорення обробки (а скоріше навіть уповільнює, оскільки потоки можуть «штовхатися», перемикаючи контексти). Реальної паралельності потоки внаслідок обмеження GIL не створюють, хоч і є не green thread"ами, а реальними потоками OS.

Проведемо ще один тест. Запустимо uwsgi з 1 процесом, але 10 потоками-обробниками в ньому:

Uwsgi --socket 127.0.0.1:8080 --protocol http --single-interpreter --processes 1 --threads 10 -w app:app
та виконаємо за допомогою ab 5000 запитів до додатку.

Ab-n 5000-c 50 http://127.0.0.1:8080/
Наступні запити curl 127.0.0.1 :8080покажуть, що маємо лише одну зростаючу послідовність, значення якої<= 5000 (меньше 5000 оно может быть в случае race condition на инкременте).

Вплив мови на архітектуру програми

Кожен HTTP-запит обробляється в окремому потоці (справедливо і процесів, оскільки процес має мінімум 1 потік). При цьому кожен потік за час свого життя (який в ідеальних умовах збігається з часом життя всього uwsgi додатка) обробляє безліч HTTP-запитів, зберігаючи свій стан (тобто значення змінних рівня модулів) від запиту до запиту. У цьому полягає чи не основна відмінність від моделі обробки HTTP-запитів у PHP, де кожен запит приходить нове щойно проініціалізоване оточення та завантаження програми необхідно виконувати щоразу заново.

Типовим підходом у великих web-додатках на PHP є використання Dependency Injection Container для управління ініціалізацією та доступом до рівня сервісів програми. Наочним прикладом є Pimple. На кожен HTTP-запит насамперед виконується код ініціалізації, що реєструє всі доступні послуги в контейнері. Далі в міру потреби здійснюється доступ до об'єктом сервісів (lazy) у контролерах. Кожен сервіс може залежати від інших сервісів, залежність дозволяється знову ж таки через контейнер у коді ініціалізації сервісу-агрегату.

// Визначаємо послуги $container["session_storage"] = function ($c) ( return new SessionStorage("SESSION_ID"); ); $container["session"] = function ($c) ( return new Session($c["session_storage"]); ); // Використовуємо сервіси class MyController ( public function __construct() ( // get the session object $this->session = $container["session"]; // "тестування" страждає, але не суть) )
Завдяки контейнеру можна забезпечити одноразове створення об'єктів і повернення вже готових об'єктів на кожне наступне звернення до сервісу (якщо необхідно). Але ця магія працює тільки в рамках одного HTTP-запиту, тому послуги можна без проблем ініціалізувати специфічними для запиту значеннями. Такі значення найчастіше - це поточний авторизований користувач, сесія поточного користувача, власне HTTP-запит та ін. Наприкінці запиту послуги все одно будуть зруйновані, а на початку обробки наступного запиту - створені та проініціалізовані нові. До того ж можна практично не турбуватися про витоку пам'яті, якщо обробка одного HTTP-запиту вміщається у відведені для скрипту ліміти, оскільки створення сервісів відбувається на вимогу (lazy) і на один запит кожен необхідний сервіс, швидше за все, буде створений тільки в єдиному екземплярі.

Тепер, беручи до уваги вищеописану поточну модель Python, можна помітити, що використання аналогічного підходу в Python веб-додатку неможливо без додаткових зусиль. Якщо контейнер буде змінною рівня модуля (що виглядає цілком логічно), всі сервіси, які він містить, не можуть бути проініціалізовані специфічними для поточного запиту значеннями, так як сервіс буде ресурсом, що розділяється між декількома потоками, що випалюють обробку декількох HTTP-запитів псевдо- паралельно. На перший погляд існує два способи впоратися з цією проблемою - зробити об'єкти сервісів незалежними від поточного HTTP-запиту (залежними залишитися виклики методів сервісів, а стікові змінні, що використовуються в методах, не є ресурсами, що розділяються) або ж зробити контейнер - ресурсом потоку, а не процесу (тоді кожен потік буде спілкуватися тільки зі своїм незалежним набором сервісів, а одного моменту один потік може обробляти тільки один HTTP-запит).

Здається плюс першого підходу - сервіси ініціалізуються лише один раз за весь час життя uwsgi процесу. Можлива також економія пам'яті (оскільки маємо лише один набір сервісів на всі потоки). З іншого боку обробка конкретного HTTP-запиту вимагає лише якогось (швидше за все невеликого) підтримки всіх доступних сервісів. Якщо ж додаток досить великий і має значну кількість сервісів, то після обробки певної кількості HTTP-запитів, переважна більшість сервісів виявиться проініціалізованими і перебувають у пам'яті процесу. Виглядає так, що це може бути серйозною проблемою.

Другий підхід можна реалізувати з використанням

Ця стаття присвячена трьом важливим у розробці додатків поняттям: Python, а також веб-серверам та найважливішим їх відмінностям.

Ця досить довга стаття може здатися надто стомлюючою для тих, хто шукає короткий посібникчи хоче швидко отримати відповіді на свої запитання. На жаль, на відміну від більшості дій і завдань в Python, коли мова йдепро вибір сервера виробництва для розгортання програми, єдино вірного чи хоча б кращого способу зробити це немає.

Проте це не так страшно. Ця стаття докладно описує різні веб-сервери, їх функції та обробку програм на основі Python. Це допоможе ухвалити рішення, який із серверів більше відповідає вимогам того чи іншого проекту.

Python Web Server Gateway Interface v1.0 (WSGI)

На сьогоднішній день кількість взаємозамінних веб-серверів (або модулів для серверів), які спеціально розроблені або адаптовані для розміщення веб-додатків Python, постійно зростає. Але так не завжди. Раніше розробники не мали змоги легко змінити веб-сервер у разі потреби, і кожен перехід з одного сервера на інший означав нові залежності та обмеження. Також це стосувалося фреймворків для розробки програм. Це було пов'язано з відсутністю загальноприйнятих технічних характеристикінтерфейсу (тобто загальної основи, яку будуть використовувати для з'єднання схожі фреймворки та веб-сервери, що забезпечує взаємозамінність компонентів з мінімальними змінами коду).

Поява стандарту

На початку цього століття були докладені всі зусилля, щоб нарешті представити публіці Python Enhancement Proposal (PEP) 333.

З PEP (Python Enhancement Proposal) 333:

Цей документ specifies a proposed standard
interface between web servers and Python web
applications or frameworks, до promote web
application portability across a variety of
веб-сервери.

Як говориться в РЕР, цей новий стандарт забезпечує переносимість між веб-серверами та веб-додатками Python. Потужні функції цього стандартута його широке впровадження порівняно з попередніми серйозно вплинули на розвиток серверів: на сьогоднішній день існує багато (можливо навіть занадто багато) веб-серверів, готових зробити всю роботу самостійно.

Порівняння

У цьому розділі, присвяченому порівнянню веб-серверів для програм Python, йтиметься про деякі популярні доступних серверах. Мета цього розділу – дати читачеві чітке уявлення про сервери та допомогти підібрати відповідний вимогам програми сервер. У зв'язку з величезною кількістю веб-серверів, яку неможливо охопити в рамках однієї статті, далі будуть описані лише «видатні» сервери (тобто, найбільш популярні, найбільш надійні, або володіють більш потужними функціями порівняно з іншими машинами).

Примітка: рекомендується ставитись з обережністю до упереджених та оманливих тестів, що не прагнуть відобразити умови реального середовища виробництва. На жаль, подібні статті не допоможуть у виборі веб-сервера для виробництва, а, навпаки, тільки зб'ють з пантелику. Також рекомендується оцінити і зрозуміти власні потреби, а потім спробувати різні варіанти серверів.

Веб-сервери (за абеткою)

CherryPyWSGI-сервер

Що це таке?

Насправді CherryPy – це фреймворк. Тим не менш, він повністю автономен (тобто може працювати сам по собі, в тому числі на стадії виробництва, і не вимагає додаткового програмного забезпечення). Це здійснюється завдяки власному веб-серверу WSGI, сумісному з HTTP/1.1. Проект CherryPy описує його як «готовий до виробництва швидкісний загальний HTTP-сервер». Оскільки це WSGI-сервер, він може обслуговувати будь-яку іншу програму Python WSGI, не прив'язуючись до фреймворку для розробки програм CherryPy.

  • Він компактний та простий.
  • Він може обслуговувати будь-які веб-програми Python, що працюють на WSGI.
  • Він може обробляти статичні файли, а також обслуговувати файли та папки.
  • Пропонується за допомогою SSL.
  • Це легко адаптована, надійна та проста у використанні альтернатива Python.

Gunicorn

Що це таке?

Gunicorn – автономний веб-сервер з великою функціональністю, що надається у зручному вигляді. Він використовує pre-fork модель (це означає, що головний процес управляє ініційованими робочими процесами різного типу, які потім опрацьовують запити). І все це можна налаштувати та адаптувати щодо потреб проекту та різноманітних сценаріїв виробництва.

Чому слід розглянути можливість його використання?

  • Він підтримує WSGI і може бути використаний з будь-яким додатком та фреймворком Python на основі WSGI.
  • Це також повноцінна заміна Paster (наприклад, Pyramid), сервера розробки Django, web2py тощо.
  • Пропонує широкий вибір різних типів«виконавців»/конфігурацій та автоматичне керування робочими процесами.
  • Підтримує HTTP/1.0 та HTTP/1.1 за допомогою синхронних та асинхронних виконавців.
  • Постачається за допомогою SSL
    Розширюється за допомогою спеціальних точок входу.
    Інтуїтивно зрозуміла, має чітку архітектуру.

Tornado(HTTP-сервер)

Що це таке?

Tornado – це фреймворк для розробки додатків та мережева бібліотека, створена для обробки асинхронних операцій, що дозволяє таким серверам підтримувати безліч відкритих з'єднань. Поставляється з сервером WSGI, який можуть використовувати для запуску інші програми та фреймворки WSGI Python.

Чому слід розглянути можливість його використання?

  • Необхідний під час роботи з фреймворком Tornado;
  • Зручний для програм, яким потрібен асинхронний режим роботи.

Хоча, у таких випадках можна використовувати Gunicorn з асинхронними виконавцями Tornado.

Twisted Web

Що це таке?

Twisted Web – це веб-сервер, який постачається з мережевою бібліотекою Twisted. Тоді як сама бібліотека Twisted є «подійно-керованим мережевим механізмом», сервер Twisted Web працює на WSGI, отже, може містити інші веб-додатки Python.

Чому слід розглянути можливість його використання?

  • Це простий у використанні, надійний та зрілий продукт.
  • Може запускати програми WSGI Python.
  • Може використовуватися як фреймворк веб-сервера Python, що дозволяє запрограмувати його на обслуговування HTTP користувача.
  • Просте та швидке створення прототипу за допомогою Python Scrips (.rpy), що виконується за запитами HTTP.
  • Має можливості проксі та інвертованого проксі-сервера.
  • Підтримує Virtual Hosts.
  • Обслуговує Perl, PHP тощо. через програмний інтерфейс twisted.web.twcgi.

uWSGI

Що це таке?

Незважаючи на дуже заплутану угоду про імена, сам uWSGI є великим проектом, який складається з великої кількості компонентів, які надають повний набір програмного забезпечення для побудови хостингових послуг. Один із цих компонентів, сервер uWSGI, запускає програми Python WSGI. Він може використовувати різні протоколи, у тому числі і власний протокол uwsgi, ідентичний SCGI. Оскільки автономні HTTP-сервери в порівнянні з серверами додатків популярніші, веб-сервери NGINX і Cherokee розроблені як модульні, що дозволяє uWSGI використовувати власний протокол з високою продуктивністю та забезпечує прямий контроль над процесами.

Чому слід розглянути можливість його використання?

  • Поставляється з адаптером WSGI і повністю підтримує програми Python на WSGI.
  • Пов'язаний з libpython. Він завантажує код програми під час запуску та діє як інтерпретатор Python, а також аналізує вхідні запити та викликає Python.
  • Постачається із прямою підтримкою популярного веб-сервера NGINX (а також Cherokee та Lighttpd).
  • Написаний мовою C.
  • Різні компоненти можуть зробити набагато більше, ніж просто запуск програми, що може бути корисним для розширення функцій.
  • В даний час (станом на кінець 2013 року) uWSGI активно розвивається, регулярно з'являються нові версії.
  • Має різні механізми для запуску додатків (асинхронного та синхронного).
  • Невисоке споживання пам'яті під час запуску.

Waitress WSGI-сервер

Що це таке?

Waitress – сервер Python WSGI. На перший погляд може здатися, що він не має істотних відмінностей, проте його філософія розробки відокремлює його від інших серверів. Його метою є спрощення процесів розробки та виробництва досить складних на серверах Python. Для цього Waitress нейтралізує конфлікти, викликані відмінностями платформ (наприклад, Unix vs. Windows), інтерпретаторів (CPython vs. PyPy) та версій Python (версія 2 vs 3).

Чому слід розглянути можливість його використання?

  • Це дуже простий мінімалістський сервер Python.
  • Підтримує HTTP/1.0 та HTTP/1.1 (Keep-Alive).
  • Постачається готовим до розгортання виробництва із широким спектром підтримки платформи.
  • На відміну від CherryPy, він по суті незалежний від фреймворку.
  • Запускається на Windows та Unix, працює з інтерпретаторами CPython та PyPy (тільки на Unix).
  • Підтримує версії Python 2 та 3.

Модулі для автономних серверів

mod_ pythonз адаптеромWSGI (Apache) (вбудовуванняPython)

Простими словами, mod_python – це модуль Apache, який вбудовує Python безпосередньо на сервер. Хоча використовувати його не завжди рекомендується, він може запускати програми WSGI на Apache за допомогою оболонок.

Web-розробки на мові Python, Частина 1

Розробка для Web за допомогою Django та Python

Середовище Web-розробки Django мовою Python, від встановлення до завершеного Web-сайту

Серія контенту:

Проект Django являє собою середовище розробки, що настроюється користувачем. Він бере початок із Web-сайту однієї онлайн-газети і був випущений як система з відкритим вихідним кодому липні 2005 року. Головні компоненти середовища розробки Django:

  • Об'єктно-реляційне відображення (object-relational mapping) для створення моделей
  • Бездоганний інтерфейс адміністратора, спеціально створений для кінцевих користувачів
  • Елегантно спроектований механізм адресування (URL)
  • Мова шаблонів для дизайнерів
  • Система кешування

Ця стаття – перша з двох статей про середовище Web-розробки мовою Python. Друга стаття надасть вам середовище розробки TurboGears.

Щоб використовувати та розуміти код у цій статті, вам необхідно встановити Python та знати, як ним користуватися на початковому рівні. Перевірте, чи є у вас Python, і яка версія, набравши python -V . Мінімальною вимогою Django є версія 2.3.5, яка доступна вам на веб-сайті Python (див. посилання в розділі наприкінці статті). Вам також слід хоч трохи бути знайомим з архітектурою MVC.

Установка Django

У цій статті використовується версія Django для того, щоб скористатися перевагами останніх поліпшень, внесених в середу розробки Django. Я рекомендую використовувати цю версію до появи версії 0.95. Дізнайтесь про наявність останньої версії Django на його веб-сайті (знову див. посилання).

Щоб скачати та встановити Django вам потрібно:

Лістинг 1. Завантаження та встановлення Django
~/downloads# svn co http://code.djangoproject.com/svn/django/trunk/ django_src ~/downloads# cd django_src ~/downloads# python setup.py install

Інструмент адміністрування Django

Після встановлення Django вам необхідний інструмент адміністрування Django, а саме django-admin.py, доданий до змінної оточення path. Лістинг 2 показує деякі з команд, доступних інструменту адміністратора:

Лістинг 2. Використання інструменту адміністрування Django
~/dev$ django-admin.py usage: django-admin.py дії: adminindex Prints admin-index template snippet for given model module name(s). ... snip ... startapp Створити Django app directory structure для given app name в current directory. startproject Creates a Django проектна директорія структури для провідних проектних назв в поточній директорії. validate Validates all installed models. options: -h, --help show this help message and exit --settings=SETTINGS Python path to settings module, e.g. "myproject.settings.main". Якщо це не передбачено, DJANGO_SETTINGS_MODULE довжина навколишнього середовища буде використана.

Проекти та програми в Django

Щоб розпочати проект Django, використовуйте команду програми django-admin startproject ось так:

Лістинг 3. Створення проекту
~/dev$ django-admin.py startproject djproject

Наведена вище команда створює директорію під назвою djproject, яка містить файли базової конфігурації, необхідні для роботи проекту Django:

Лістинг 4. Зміст директорії djproject
__init__.py manage.py settings.py urls.py

У цьому проекті ви створите програму для відділу зайнятості, яка називається "jobs." Для створення програми використовуйте скрипт manage.py, який є спеціальним для проекту скриптом в django-admin.py, в якому автоматично задається файл settings.py:

Лістинг 5. Використання manage.py з параметром startapp
~/dev$ cd djproject ~/dev/djproject$ python manage.py startapp jobs

Це створює кістяк програми з одним модулем Python для ваших моделей та іншим для ваших видів (view). Директорія jobs буде створювати такі файли:

Лістинг 6. Зміст директорії програми jobs
__init__.py models.py views.py

Розташування програми всередині проекту є чистою умовністю, створеною для нових розробників у Django, а не вимогою. Як тільки ви почнете перемішувати та зіставляти програми в кількох проектах, ви можете помістити програми у власні простори імен для модулів і зв'язати їх разом, використовуючи налаштування та головні файли URL. Але поки що, слідуйте наведеним нижче етапам.

Щоб Django знав про нову програму, необхідно додати рядок у поле INSTALLED_APPS у файлі settings.py. Для цього додатка для відділу зайнятості потрібно додати рядок djproject.jobs:

Лістинг 7. Додавання рядка до settings.py
INSTALLED_APPS = ("django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "djproject.jobs",)

Створення моделі

Django має власну бібліотеку object-relational mapper (ORM), яка підтримує динамічний доступ до баз даних через об'єктний інтерфейс Python. Інтерфейс Python є дуже зручним і ефективним, але ви також можете відмовитися від нього і безпосередньо використовувати SQL за необхідності.

На даний момент ORM надає підтримку баз даних PostgreSQL, MySQL, SQLite, а також Microsoft® SQL.

Це приклад використовує SQLite як інтерфейс до бази даних. SQLite є легковажною базою даних, що не потребує конфігурації і розміщується на диску як простий файл. Щоб використовувати SQLite, просто встановіть бібліотеку pysqlite за допомогою інструментів інсталяції (setuptools) (див. додаткову інформаціюпро інструменти установки (setuptools) і, зокрема, про інструмент easy_install , який необхідно встановити окремо, в розділі ):

easy_install pysqlite

Перед тим, як працювати з моделлю, налаштуйте базу даних у файлі налаштувань. Для SQLite необхідно вказувати лише двигун та ім'я бази даних.

Лістинг 8. Налаштування бази даних у settings.py
DATABASE_ENGINE = "sqlite3" DATABASE_NAME = "/path/to/dev/djproject/database.db" DATABASE_USER = "" DATABASE_PASSWORD = "" DATABASE_HOST = "" DATABASE_PORT = ""

Ця програма для відділу зайнятості буде мати два типи об'єктів: Розташування (Locations) та Роботи (Jobs). Розташування включає поля міста, штату (за бажанням), а також країни. Робота має місце розташування, посаду, опис та дату публікації.

Лістинг 9. Модуль jobs/models.py
з django.db import models class Location(models.Model): city = models.CharField(maxlength=50) state = models.CharField(maxlength=50, null=True, blank=True) country = models.CharField(maxlength= 50) def __str__(self): if self.state: return "%s, %s, %s" % (self.city, self.state, self.country) else: return "%s, %s" % ( self.city, self.country) class Job(models.Model): pub_date = models.DateField() job_title = models.CharField(maxlength=50) job_description = models.TextField() location = models.ForeignKey(Location) (self): return "%s (%s)" % (self.job_title, self.location)

Метод __str__ - це спеціальний метод класу у мові Python, який повертає рядкову виставу об'єкта. Django широко використовує цей метод, коли відображає об'єкти в інструменті адміністратора.

Щоб побачити схему бази даних моделі, запустіть manage.py з командою sql . Схема поки що не буде задіяна.

Лістинг 10. Перегляд схеми бази даних за допомогою команди manage.py sql
~/dev/djproject$ python manage.py sql jobs BEGIN; CREATE TABLE "jobs_job" ("id" integer NOT NULL PRIMARY KEY, "pub_date" date NOT NULL, "job_title" varchar(50) NOT NULL, "job_description" text NOT NULL, "location_id" integer NOT NULL CREATE TABLE "jobs_location" ("id" integer NOT NULL PRIMARY KEY, "city" varchar(50) NOT NULL, "state" varchar(50) NULL, "country" varchar(50) NOT NULL); COMMIT;

Для ініціалізації та встановлення моделі запустіть команду синхронізації бази даних syncdb:

~/dev/djproject$ python manage.py syncdb

Зазначимо, що команда syncdb просить вас створити обліковий записсуперкористувача. Це відбувається тому, що програма django.contrib.auth, яка надає базові засоби автентифікації користувача, встановлено за замовчуванням у налаштуваннях INSTALLED_APPS. Ім'я суперкористувача та пароль будуть використовуватися для реєстрації в інструменті адміністратора, описаному в наступному розділі. Пам'ятайте, що це ім'я та пароль суперкористувача Django, а не системи.

Набори запитів

Моделі Django звертаються до бази даних через початковий керуючий (Manager) клас, званий objects. Наприклад, щоб вивести список всіх робіт, вам потрібно використовувати метод all , що належить менеджеру objects:

Лістинг 11. Виведення всіх робіт (jobs)
>>> з jobs.models import Job >>> for job in Job.objects.all(): ... print job

Керуючий клас також має методи фільтрації, які називають filter (фільтрація) і exclude (виключення). Фільтрація отримує всі об'єкти, що підходять під умови, тоді як виняток дає всі об'єкти, які не підходять під них. Нижченаведені запити повинні дати такі ж результати ("gte" означає "greater than or equal," (більше або одно) і "lt" означає "less than") (менше ніж).

Лістинг 12. Виключення та фільтрація робіт (jobs)
>>> from jobs.models import Job >>> from datetime import datetime >>> q1 = Job.objects.filter(pub_date__gte=datetime(2006, 1, 1)) >>> q2 = Job.objects.exclude(pub_date__lt = Datetime (2006, 1, 1))

Методи filter і exclude повертають об'єкти QuerySet, які можна зв'язати в ланцюжок і які навіть можуть представляти з'єднання. Запит q4, наведений нижче, буде знаходити роботи, починаючи з 1 січня 2006 року, в м. Клівленд (Cleveland), штат Огайо:

Лістинг 13. Знову виключення та фільтрація робіт (jobs)
>>> from jobs.models import Job >>> from datetime import datetime >>> q3 = Job.objects.filter(pub_date__gte=datetime(2006, 1, 1)) >>> q4 = q3.filter(location__city__exact=" Cleveland", ... location__state__exact="Ohio")

Це добре, що QuerySets ліниві. Це означає, що вони не виконуються в базі даних, доки не відбулася їх оцінка, і завдяки чому працюють набагато швидше, ніж миттєві запити.

Ця лінощі є дуже практичною в поєднанні з розсікаючою(slicing) функціональністю мови Python. Замість запитувати всі записи, а потім відсікати потрібні записи, нижченаведений код використовує зміщення (OFFSET), що дорівнює 5, і межа (LIMIT), рівний 10, в цьому запит SQL, дуже покращуючи якість виконання.

Лістинг 14. Python-розсічення
>>> з jobs.models import Job >>> for job in Job.objects.all() ... print job

Примітка: Використовуйте метод count , щоб зрозуміти, скільки записів у QuerySet. Метод len у Python робить повну оцінку, а потім підраховує кількість рядів, повернутих у вигляді записів, тоді як метод count робить дійсний Підрахунок (COUNT) SQL, який набагато швидше. І ваш адміністратор баз даних подякує вам.

Лістинг 15. Підрахунок записів
>>> з jobs.models import Job >>> print "Count = ", Job.objects.count() # GOOD! >>> print "Count = ", len(Job.objects.all()) # BAD!

Для отримання докладнішої інформації див. розділ "Довідник з інтерфейсу API бази даних" для Django.

Інструмент адміністратора

Одна з найбільших переваг у Django - це відточений інтерфейс адміністратора. Цей інструмент був створений з урахуванням кінцевого користувача, завдяки чому надає вашим проектам чудовий засіб для введення даних.

Інструмент адміністратора є програмою, яка йде разом з Django. Перед використанням його необхідно встановити так само, як і додаток jobs . Спочатку потрібно додати модуль програми (django.contrib.admin) у налаштування INSTALLED_APPS:

Лістинг 16. Зміни у settings.py
INSTALLED_APPS = ("django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "djproject.jobs", "django.contrib.admin",)

Щоб зробити інструмент адміністратора доступним з адреси (URL) /admin, просто розкрийте дужки коментаря в рядку у наданому вам файлі urls.py вашого проекту. У наступному розділі буде розглядатися конфігурація URL докладніше.

Лістинг 17. Використання urls.py, щоб інструмент адміністратора став доступним
from django.conf.urls.defaults import * urlpatterns = patterns("", (r"^admin/", include("django.contrib.admin.urls.admin")),)

Додаток адміністратора має свою модель бази даних, яку необхідно встановити. Знову скористайтеся командою syncdb , щоб завершити наступне:

python manage.py syncdb

Щоб побачити інструмент адміністратора, можна використовувати тест-сервер, який додається до Django.

Лістинг 18. Використання тест-сервера, щоб побачити інструмент адміністратора
~/dev/djproject$ python manage.py runserver Validating models... 0 errors found. Django version 0.95 (post-magic-removal), використовуючи налаштування "djproject.settings" Розробка сервера є керування на http://127.0.0.1:8000/ Quit the server with CONTROL-C (Unix) або CTRL-BREAK (Windows) .

Тепер ви можете переміститися в інструмент адміністратора за адресою http://localhost:8000/admin і зареєструватися, використовуючи дані адміністратора, які ви створили раніше. Ви помітите, що жодна з ваших моделей не доступна для користування.

Щоб відкрити доступ до класу через інструмент адміністратора, створіть для нього підклас Admin (Адміністратор). Потім можна настроїти те, як їх можна адмініструвати додаванням характеристик класів до даного підкласу. Лістинг 19 показує, як додавати клас Location (Місцезнаходження) до інструменту адміністратора.

Лістинг 19. Додаток класу Location за допомогою інструмента адміністратора
class Location(meta.Model): ... class Admin: list_display = ("city", "state", "country")

Тепер ви можете створювати, оновлювати та видаляти записи Місцезнаходження (Location) через інтерфейс адміністратора.

Малюнок 1. Зміна розташування за допомогою інструмента адміністратора

Ви можете виводити та сортувати Записи (Records) за містами, штатами та країнами, залежно від характеристики класу list_display .

Малюнок 2. Виведення розташування за допомогою інструмента адміністратора

Інструмент адміністратора має велику кількість опцій для керування кожним із класів моделей. Лістинг 20 показує кілька прикладів, застосованих до класу Job:

Лістинг 20. Опції для керування класами моделей
class Job(meta.Model): ... class Admin: list_display = ("job_title", "location", "pub_date") ordering = ["-pub_date"] search_fields = ("job_title", "job_description") list_filter = ("location",)

Відповідно до вищезазначених налаштувань, при виведенні записів робіт (job) будуть використовуватися: заголовок роботи (job), місцезнаходження та дата видання. Роботи розташовуватимуться в порядку дати видання, починаючи з останнього (знак мінус означає - в порядку убування). Користувачі можуть знайти роботи за заголовком та описом, а адміністратори можуть фільтрувати записи, виходячи з їх місцезнаходження.

Малюнок 3. Виведення робіт за допомогою інструмента адміністратора

Проектування своєї схеми URL

Система передачі адрес URL у Django використовує модулі конфігурацій регулярних виразів, які відображають рядкові шаблони URL у методах мови Python, які називаються видами. Ця система дозволяє чітко відокремлювати адреси URL від коду, що робить їх максимально легкими для керування.

Модуль urls.py створюється і визначається як початок відліку конфігурації URL (за допомогою значення ROOT_URLCONF у модулі settings.py). Єдина вимога для конфігураційного файлу URL полягає в тому, що він повинен містити об'єкт, що визначає шаблони, звані urlpatterns .

Додаток керування роботою (job) буде починатися з index-видом і детальним (detail) видом, які доступні через справжні відображення URL:

  • /jobs index-вид: Відображає останні 10 робіт
  • /jobs/1 детальний вигляд: Відображає роботи з одним ідентифікаційним номером (ID)

Обидва ці види (index і детальний вид) будуть виконані в модулі, званому views.py у додатку робіт. Виконання цієї конфігурації у файлі urls.py проекту виглядатиме так:

Лістинг 21. Виконання конфігурації видів у djproject/urls.py
from django.conf.urls.defaults import * urlpatterns = patterns("", (r"^admin/", include("django.contrib.admin.urls.admin")), (r"^jobs/$", "djproject.jobs.views.index"), (r"^jobs/(?P

Позначте шматочок коду . Це знадобиться пізніше.

На практиці найкраще - витягувати шаблони URL, спеціальні для додатків, і поміщати їх у саму програму. Це відокремлює програму від проекту і дозволяє повторно його використовувати. Файл конфігурації URL рівня додатків для робіт будуть виглядати так:

Лістинг 22. Файл конфігурації URL рівня додатків, urls.py
від django.conf.urls.defaults import * urlpatterns = patterns("", (r"^$", "djproject.jobs.views.index"), (r"^(?P \d+)/$", "djproject.jobs.views.detail"),)

З того часу, як методи видів виходять з одного модуля, для налаштування djproject.jobs.views як кореневе ім'я модуля можна використовувати перший аргумент, і Django використовує його для того, щоб шукати методи index і detail:

Лістинг 23. jobs/urls.py: пошук index та detail
від django.conf.urls.defaults import * urlpatterns = patterns("djproject.jobs.views", (r"^$", "index"), (r"^(?P) \d+)/$", "detail"),)

Щоб зв'язати одну адресу (URL), перерахованих вище робіт (job), у проект, необхідно використовувати функцію include . Адреси (URL) рівня програм тепер пов'язані в розділі /jobs:

Лістинг 24. djproject/urls.py: Зв'язування адрес (URL) назад до проекту
from django.conf.urls.defaults import * urlpatterns = patterns("", (r"^admin/", include("django.contrib.admin.urls.admin")), (r"^jobs/", include ("djproject.jobs.urls")),)

Якщо ви спробуєте увійти на початкову (index) сторінку (http://localhost:8000/jobs) на цьому етапі, використовуючи тест-сервер, ви отримаєте помилку, оскільки вид, який називатиметься (djproject.jobs.views.index) ще не існує.

Виконання видів

Вигляд являє собою простий метод мови Python, який приймає об'єкт відгуку та відповідає за:

  • Будь-яку бізнес-логіку (явно чи неявно)
  • Контекстний словник з даними для шаблону
  • Рендеринг шаблону з контекстом
  • Об'єкт відгуку, який пропускає відрендеровані (rendered) результати назад у середу розробки

У Django викликаний метод мови Python, коли запитується URL, називається виглядом, а сторінка, закачена та відрендерена, виглядом називається шаблоном. З цієї причини команда розробників Django говорила про Django як про середовище розробки з MVT (model-view-template - модель-вид-шаблон). З іншого боку, TurboGears називає свої методи контролерами, а відрендеровані шаблони видамищо дійсно дозволяє використовувати абревіатуру MVC. Різниця лише у використанні слів, але зміст залишається тим самим.

Найпростіший із можливих видів повертає об'єкт HttpResponse (http відгук), ініціалізований у рядковий тип. створіть наступний метод і зробіть з /jobs HTTP-запит, щоб перевірити, чи ваші файли urls.py і views.py правильно налаштовані.

Лістинг 25. jobs/views.py (v1)
from django.http import HttpResponse def index(request): return HttpResponse("Job Index View")

Наступний код отримує останні 10 робіт (job), рендерує їх через шаблон і повертає відгук. Це небуде працювати без файлу шаблону з .

Лістинг 26. jobs/views.py (v2)
з django.template import Context, loader from django.http import HttpResponse from jobs.models import Job def index(request): object_list = Job.objects.order_by("-pub_date")[:10] t = loader.get_template(" jobs/job_list.html") c = Context(( "object_list": object_list, )) return HttpResponse(t.render(c))

У наведеному вище коді шаблон названий, як рядковий jobs/job_list.html Рендеринг шаблону відбувається в контексті списку робіт (job), названому object_list . Отрендеренний шаблон рядка потрапляє до конструктора HTTPResponse, який відправляється назад у клієнт запиту за допомогою середовища розробки.

Етапи завантаження шаблону, що створюють контекст і повертають новий об'єкт відгуку, переміщені нижче за допомогою зручного методу render_to_response . Також новим є метод детального виду, який використовує зручний для цього метод get_object_or_404 , щоб здійснити вибірку об'єкта Job, використовуючи наведені аргументи. Якщо об'єкт не знайдено, видається помилка 404. Ці два методи позбавляють програму від великої кількості стандартних кодів, що використовуються в більшості Web-додатків.

Лістинг 27. jobs/views.py (v3)
з django.shortcuts import get_object_or_404, render_to_response з jobs.models import Job def index(request): object_list = Job.objects.order_by("-pub_date")[:10] return render_to_response("job. object_list": object_list)) def detail(request, object_id): job = get_object_or_404(Job, pk=object_id) return render_to_response("jobs/job_detail.html", ("object": job))

Зазначимо, що detail бере object_id як аргумент. Цей номер згадувався раніше, після URL-дороги /jobs/ до файлу робіт (job) urls.py. Він проходить далі в метод get_object_or_404 як первинний ключ (primary key - pk).

Наведені вище види все ще не працюватимуть, тому що шаблони, які вони завантажують та рендерують (jobs/job_list.html та jobs/job_detail.html) ще не існують.

Створення шаблонів

Django надає просту мову шаблонів, спроектований для швидкого рендерингу та простоти використання. Шаблони Django створюються з простого тексту, вбудованого в ((змінні)) і (% теги %). Потім відбувається заміна змінних значення, які вони несуть. Теги використовуються як основа логіки управління. Шаблони можна використовувати для створення будь-якого формату текстів, включаючи HTML, XML, CSV і простий текст.

Перше, що потрібно зробити – це визначити місцезнаходження шаблонів. Для простоти, створіть директорію шаблонів всередині директорії djproject і додайте його шлях у рядку TEMPLATE_DIRS файлу settings.py:

Лістинг 28. Створення директорії шаблонів у settings.py
TEMPLATE_DIRS = ("/path/to/devdir/djproject/templates/",)

Шаблони в Django підтримують концепцію успадкуванням шаблонів, яка дозволяє дизайнерам сайтів створювати однорідний вигляд та функціональність без повторення змісту у кожному із шаблонів. Ви можете використовувати успадкування, визначаючи документ скелета, або основи з блоковими тэгами. Ці блокові теги заповнюються шаблонами сторінок із змістом. Цей приклад показує скелет коду HTML з блоками, званими title , extrahead і content:

Лістинг 29. Документ скелета, templates/base.html
Company Site: (% block title %) Page (% endblock %)(% block extrahead %) (% endblock %) (% block content %)(% endblock %)

Щоб програма була окрема від проекту, використовуйте допоміжний файл основи як основу для всіх файлів сторінок програми Job. Для цього прикладу, помістіть CSS-додатки у файл основи для спрощення. У реальному додатку, з добре налаштованим Web-сервером, візьміть даний CSS і помістіть його в статичний файл, що обслуговується Web-сервером.

Лістинг 30. Допоміжний файл основи, templates/jobs/base.html
(% extends "base.html" %) (% block extrahead %)(% endblock %)

Спочатку тест-сервер Django не обслуговує статичні файли, тому що цю роботу повинен виконувати Web-сервер. Якщо під час розробки ви захочете, щоб Django працював з картинками, аркушами стилів і так далі, тоді див. посилання про те, як включити цю можливість.

Тепер створіть 2 шаблони сторінок для завантаження та рендерингу. Шаблон jobs/job_list.html просто виконує ітерацію через object_list(список об'єктів), який він отримує через контекст, що видається index-видом, а також відображає посилання на кожну сторінку з детальною інформацією про запис.

Лістинг 31. Шаблон templates/jobs/job_list.html
(% extends "jobs/base.html" %) (% block title %) Job List (% endblock %) (% block content %)

Job List

    (% for job in object_list %)
  • ((job.job_title))
  • (% endfor %)
(% endblock %)

Сторінка jobs/job_detail.html показує один запис, званий job(робота):

Лістинг 32. Сторінка templates/jobs/job_detail.html
(% extends "jobs/base.html" %) (% block title %)Job Detail(% endblock %) (% block content %)

Job Detail

((job.job_title)) - ((job.location))
Posted: (( job.pub_date|date:"d-M-Y" ))
(( job.job_description ))
(% endblock %)

Мова шаблонів Django була спроектована з обмеженими функціональними можливостями. Ця обмеженість дозволяє зберігати простоту шаблонів для непрограмістів і захищає програмістів від використання бізнес-логіки там, де її має бути, у шарі представлення. посилання на документацію з мови шаблонів у .

Універсальні види

Django йде разом із чотирма наборами універсальних видів, які дозволяють розробникам створювати додатки, такі типовим шаблонам:

  • Сторінки зі списком/подробицями (як наведений вище приклад)
  • Дроблення записів за датою (корисно для сайтів новин і блогів)
  • Створення, оновлення та видалення - СОУ (Creation, update, and deletion - CRUD) об'єктів
  • Простий прямий рендеринг шаблонів або простий HTTP-перенаправлення

Замість створення методів стандартних видів, вся бізнес-логіка знаходиться у файлі urls.py і керується за допомогою методів універсальних видів підтримуваних Django.

Лістинг 33. Універсальні види у jobs/urls.py
з django.conf.urls.defaults import * з jobs.models import Job info_dict = ("queryset": Job.objects.all(), ) urlpatterns = patterns("django.views.generic.list_detail", (r"^ $", "object_list", info_dict), (r"^(?P \d+)/$", "object_detail", info_dict),)

Три важливі зміни в даному файлі urls.py:

  • Об'єкт відображення info_dict проходить через набір запитів, щоб відкрити доступ для Jobs.
  • Він використовує django.views.generic.list_detail замість djproject.jobs.views.
  • Справді викликані види - це object_list та object_detail.

Цей проект дотримується деяких вимог, щоб перехід до універсальних видів відбувся автоматично:

  • Універсальний детальний (detail) вигляд чекає на аргумент з ім'ям object_id .
  • Шаблони слідують найменуванню: app_label/model_name _list.html (jobs/job_list.html) app_label/model_name _detail.html (jobs/job_detail.html)
  • Шаблон списку керує списком з ім'ям object_list.
  • Детальний шаблон керує об'єктом з ім'ям object.

Більше опцій можна встановити через info_dict , включаючи значення paginate_by , яке визначає кількість об'єктів на сторінці.

Висновок

Наступна стаття цього випуску буде розглядати TurboGears, інше середовище Web-розробки мовою Python, і порівнюватиме його з Django.

Програмування на Python

Частина 11. Web-програмування: Django

Серія контенту:

Однією з найбільш фундаментальних архітектур для програм є так звана архітектура модель-представлення-контролер (Model-View-Controller) або MVC, яка розділяє базовий функціонал програми на ряд окремих компонентів. У цьому досягається головна мета: одна модель багато додатків.

Джанго - це високорівневий пітонівський Web-фреймворк, який реалізований на основі архітектури MVC. Джанго має прозорий дизайн, дає можливість оперативної розробки Web-додатків, дозволяє розробляти динамічні Web-сайти.

Відмінні риси джанго:

  • будь-який запит обробляється програмно та перенаправляється на свою адресу (url);
  • поділ контенту та подання за допомогою шаблонів;
  • абстрагування від низького рівня бази даних.

Джанго-додаток складається з чотирьох основних компонентів.

  1. Модель даних: дані є серцевиною будь-якої сучасної Web-додатки. Модель – найважливіша частина програми, яка постійно звертається до даних за будь-якого запиту з будь-якої сесії. Будь-яка модель є стандартним пітонівським класом. Об'єктно-орієнтований мапер (ORM) забезпечує таким класам доступ безпосередньо до баз даних. Якби не було ORM, програмісту довелося писати запити безпосередньо на SQL. Модель забезпечує полегшений механізм доступу до шару даних, інкапсулює бізнес-логіку. Модель не залежить від конкретної програми. Даними можна маніпулювати навіть із командного рядка, не використовуючи Web-сервер.
  2. Подання (view): завірюхи в джангу виконують різноманітні функції, у тому числі контролюють запити користувача, видають контекст залежно від його ролі. View - це звичайна функція, яка викликається у відповідь на запит якоїсь адреси (url) та повертає контекст.
  3. Шаблони: вони є формою представлення даних. Шаблони мають свою власну просту метамову і є одним з основних засобів виведення на екран.
  4. URL: це лише механізм зовнішнього доступу до уявлень (view). Вбудовані в урли Регулярні виразироблять механізм досить гнучким. При цьому одне уявлення може бути налаштовано до кількох урл, надаючи доступ різним додаткам. Тут підтримується філософія закладок: урли стають як би самодостатніми і починають жити незалежно від уявлення.

Для новачків, які вперше стикаються з джанго та пітоном, може здатися, що тут використовується якась особлива метамова. Але це не так. Все, що можна зробити в пітоні, можна зробити і в джанго: тут є доступ до всіх стандартних і не тільки пітонівських бібліотек плюс вбудований функціонал джанго. Проекти, реалізовані на джанго:

Сьогодні ми розглянемо такі теми.

  1. Як встановити джанго.
  2. Перший додаток.
  3. Підключення бази даних.
  4. Перше уявлення.
  5. Шаблони.
  6. Адміністрація.

1. Як встановити джанго

Перш за все, у вас має бути встановлений пітон не нижче версії 2.3. Встановити джанго можна по-різному: можна взяти його з локального репозитарію, можна взяти офіційний реліз - у цьому випадку шукайте його:

Після розархівації в каталозі проекту запустіть команду:

setup.py install > import django

Перевірка версії джанго:

> django.VERSION

На даний момент офіційна стабільна версія – 1.1.1.

2. Перший додаток

Для створення першої програми зайдемо у свій домашній каталог і запустимо команду:

django-admin.py startproject mysite

Буде створено підкаталог mysite, де і лежатиме програма. Заходимо до створеного каталогу і бачимо наступну файлову структуру:

mysite/__init__.py manage.py settings.py urls.py

Init__.py – порожній файл, який підключає поточний каталог як стандартний пітонівський пакет.

manage.py - утиліта, що керує сайтом.

settings.py – конфігурація сайту

urls.py - таблиця урлів або таблиця для контенту сайту.

Для того, щоб завантажити наш Web-додаток, запустимо команду:

python manage.py runserver Validating models... 0 errors found. Django version 1.1, using settings "mysite.settings" Розробка сервера є керування на http://127.0.0.1:8000/ Quit the server with CONTROL-C.

Якщо тепер у браузері відкрити адресу http://127.0.0.1:8000/, то переконаємося в тому, що програма запущена: з'явиться стандартне запрошення.

3. Підключення бази даних

Джанго підтримує такі сервери баз даних: PostgreSQL, SQLite, MySQL, Microsoft SQL Server, Oracle.

У нашому прикладі ми вибираємо постгрес – не тому, що він кращий, це не має принципового значення. Переконайтеся, що постгрес вже встановлений на вашій системі, інакше його потрібно встановити. Після цього у файлі settings.py пропишемо налаштування для постгресу:

DATABASE_ENGINE = "postgresql_psycopg2" DATABASE_NAME = "mysite" DATABASE_USER = "postgres" TIME_ZONE = "Europe/Moscow" LANGUAGE_CODE = "ru-ru"

Запускаємо дві команди, які створюють базу даних mysite – постгрес перед цим, природно, має бути вже запущений:

psql -d template1 -U postgres -c "DROP DATABASE mysite;" psql -d template1 -U postgres -c "CREATE DATABASE mysite WITH OWNER postgres ENCODING="UNICODE";"

Запускаємо команду, яка створює у цій базі близько 10 системних таблиць:

python manage.py syncdb

4. Перше уявлення

Тепер згенеруємо каркас для нашого Web-додатку:

python manage.py startapp People

Усередині каталогу mysite з'явиться підкаталог People з наступною структурою:

People/ __init__.py models.py tests.py views.py

Додамо до settings.py останній рядок, який додає новий шлях:

INSTALLED_APPS = ("django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "mysite.People")

В settings.py в якості кореневої адреси має бути:

ROOT_URLCONF = "mysite.urls"

Створимо модель Person – стандартний пітонівський клас із двома атрибутами – ім'я, і-мейл. Файл models.py:

з django.db import models class Person(models.Model): name = models.CharField("name", max_length=200) email = models.EmailField("Email", blank=True) def __str__(self): return " %s" % (self.name)

Після того, як додається нова модель, її потрібно синхронізувати з базою даних. Для цього із командного рядка виконуємо команду:

python manage.py syncdb

Після цього у базі даних з'явиться нова таблиця. Потрібно зауважити, що якщо ви після цього вноситимете зміни в вже існуючу модель і намагатиметеся синхронізувати ці зміни з базою даних, то джанго ніяк не буде на це реагувати: до бази додаються тільки нові об'єкти. Для внесення змін вже доведеться скористатися sql-скриптами на апдейт конкретних таблиць.

Тепер напишемо найпростіше уявлення, яке виведе запрошення-файл views.py:

з django.shortcuts import HttpResponse from mysite.People.models import Person def index(request): html = "

People !!!


" return HttpResponse(html)

Додамо урл – файл urls.py:

від django.conf.urls.defaults import * urlpatterns = patterns("", (r"^People/$", "mysite.People.views.index"))

Знову запускаємо Web-додаток:

python manage.py runserver

Відкриваємо адресу в браузері та бачимо запрошення:

http://127.0.0.1:8000/People/

5. Шаблони

У корені каталогу mysite створимо каталог templates. У файлі settings.py додамо рядок:

TEMPLATE_DIRS = ("/home/mysite/templates")

Доданий шлях має бути абсолютним. У доданий каталог покладемо файл person.htnl такого змісту:

People !!!


Personal Information
  • Name: ((p.name))
  • Email: ((p.email))
  • У нашому єдиному поданні index ми зробимо зміни: створимо об'єкт Person і передамо його в шаблон, що викликається в якості параметра. У шаблоні ми беремо атрибути цього параметра за допомогою префікса подвійних лапок:

    ((p.name)) ((p.email))

    Файл views.py після підключення шаблону тепер виглядатиме так:

    від mysite.People.models import Person from django.shortcuts import render_to_response, get_object_or_404 def index(request): person = Person() person.name = "Alex" person.email = " [email protected]" return render_to_response("person.html", ("p": person))

    Після перезавантаження Web-сторінки у браузері ми побачимо ці атрибути.

    6. Адміністрація

    Адміністраторська частина джанго за замовчуванням відключена. Для її включення додамо останній рядок у settings.py:

    INSTALLED_APPS = ("django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "mysite.People", "django.contrib.admin")

    Для роботи з адмінською частиною потрібно мати рутові права, тому потрібно спочатку створити суперкористувача:

    python manage.py createsuperuser

    Після цього запустимо команду:

    python manage.py syncdb

    Файл urls.py з доданим адмінським функціоналом буде мати такий вигляд:

    from django.conf.urls.defaults import * from django.contrib import admin admin.autodiscover() urlpatterns = patterns("", (r"^admin/", include(admin.site.urls)), (r"^ People/$", "mysite.People.views.index"))

    Запускаємо Web-додаток:

    python manage.py runserver

    Набираємо у браузері адресу: http://127.0.0.1:8000/admin/. І потрапляємо на сторінку логіну. Після введення логіну та пароля суперкористувача відкривається адмінська частина. У ній можна додавати та редагувати об'єкти.

    Висновок

    Отже, сьогодні ми дізналися, що таке Джанго і чому варто зупинити свій вибір. Це безкоштовний фреймворк, який підтримує модель MVCмає кілька програмних інтерфейсів до різних баз даних, інтуїтивно зрозумілий інтерфейс адміністрування з можливістю розширення, підтримку багатомовності і т.д. Цей Web-фреймворк має низку переваг: джанго добре документований - сайт розробників http://www.djangoproject.com/ тому підтвердження. Легкість встановлення та налаштування допомагають значно економити час. Невелика кількість зовнішніх залежностей зручна для користування: все, що нам потрібно – це пітон. На цьому наш цикл статей вважатимуться завершеним. Звичайно, автор не ставив перед собою завдання написати докладний підручник чи довідник, скоріше тут йдеться про короткий вступний курс для розробників, які бажають освоїти новий для себе інструмент.

    Код прикладів перевірявся на версії пітона 2.6.

    Ресурси для скачування

    static.content.url=http://www.сайт/developerworks/js/artrating/

    Zone = Linux, Open source

    ArticleID=517107

    ArticleTitle=Програмування на Python: Частина 11. Web-програмування: Django

  • PHP ,
  • Python
  • Вступ

    У статті хотілося б порушити питання відмінності використання Python для web-розробки в порівнянні з ним на PHP. Сподіваюся, стаття не приведе до холіварів, оскільки вона зовсім не про те, яка мова краща чи гірша, а виключно про технічні особливості Python.

    Трохи про самі мови

    PHP- Веб-орієнтована мова, (в хорошому сенсі слова). З низькорівневої точки зору додаток на PHP є швидше набір окремих скриптів можливо з єдиною семантичною точкою входу.

    Python- Універсальна мова програмування, застосовна навіть у Інтернеті. З технічної точки зору web-додаток на Python - повноцінний додаток, завантажений в пам'ять, що має свій внутрішній стан, що зберігається від запиту до запиту.

    Виходячи з вищеописаних особливостей випливають і відмінності у обробці помилок у web-додатках. У PHP існує цілий зоопарк типів помилок (errors, exceptions), далеко не кожну з яких можна перехопити, хоча це (неможливість перехоплення) і не має великого значення, оскільки програма живе рівно стільки, скільки обробляється один запит. Неперехоплена помилка просто призводить до дострокового виходу з обробника та видалення програми з пам'яті. Новий запит буде оброблятись новим «чистим» додатком. У Python програма постійно знаходиться в пам'яті, обробляючи безліч запитів без «перезавантаження». Таким чином, підтримувати правильний передбачуваний стан програми вкрай важливо. Всі помилки використовують стандартний механізм виключень і можуть бути перехоплені (хіба що, за винятком SyntaxError). Неперехоплена помилка призведе до завершення програми, яка буде потрібно перезапускати ззовні.

    Існує безліч способів «приготувати» PHP та Python для Інтернету. Далі я зупинюся на двох найбільш мені знайомих (і здається найбільш популярних) – PHP + FastCGI (php-fpm) та Python + WSGI (uWSGI). Звичайно ж, перед обома цими зв'язками передбачається наявність фронтенд-сервера (наприклад, Nginx).

    Підтримка багатопоточності Python

    Запуск сервера програм (наприклад, uWSGI) призводить до завантаження інтерпретатора Python у пам'ять, а потім завантаження самого веб-додатку. Зазвичай bootstrap-модуль додатка імпортує необхідні йому модулі, здійснює виклики ініціалізації та у результаті експортує підготовлений callable об'єкт, відповідний специфікації WSGI. Як відомо, при першому імпорті Python-модулів код всередині них виконується, у тому числі створюються та ініціалізуються значеннями змінні. Між двома послідовними запитами HTTP стан інтерпретатора не скидається, отже зберігаються значення всіх змінних рівня модуля.

    Напишемо найпростіший WSGI-додаток, який наочно продемонструє вищеописаний на прикладі:

    N = 0 def app(env, start_response): global n n += 1 response = "%.6d" % n start_response("200 OK", [("Content-Type", "text/plain")]) return
    Тут nє змінною модуля і вона буде створена зі значенням 0 при завантаженні програми на згадку наступною командою:

    Uwsgi --socket 127.0.0.1:8080 --protocol http --single-interpreter --processes 1 -w app:app
    Сама програма просто виводить на сторінку значення змінної n. Для затятих PHP програмістів воно виглядає безглуздим, тому що «повинно» щоразу виводити на сторінку рядок "000001".

    Проведемо тест:

    Ab-n 500 -c 50 http://127.0.0.1:8080/ curl http://127.0.0.1:8080
    В результаті ми отримаємо рядок "000501", що підтверджує наше твердження, про те, що програма знаходиться завантаженою в пам'ять uwsgi і зберігає свій стан між запитами.

    Якщо запустити uWSGI з параметром --processes 2і провести той же тест, кілька послідовних викликів curl покажуть, що ми маємо вже 2 різні зростаючі послідовності. Так як ab посилає 500 запитів, приблизно половина з них посідає один процес uWSGI, а решта - на другий. Очікувані значення, що обертаються curl будуть приблизно "000220"і "000280". Інтерпретатор Python, зважаючи на все, один на процес, і ми маємо 2 незалежних оточення і реальну паралельну обробку запитів (у випадку багатоядерного процесора).

    Python підтримує потоки як частину мови. p align="justify"> Класична реалізація (CPython) використовує нативні потоки OS, але є GIL - в один момент часу виконується тільки один потік. У цьому однаково можливі проблеми race condition, оскільки навіть n += 1 є атомарної операцією.

    Дизасемблуємо python код

    «Дизасемблуємо» наш WSGI-додаток:
    import dis n = 0 def app(env, start_response): global n n += 1 return if "__main__" == __name__: print(dis.dis(app))
    8 0 LOAD_GLOBAL 0 (n) 3 LOAD_CONST 1 (1) 6 INPLACE_ADD 7 STORE_GLOBAL 0 (n) 10 10 LOAD_GLOBAL 1 (bytes) 13 LOAD_GLOBAL 2 (str) 16 LOAD_GLOBAL 0 (n) 19 CALL_FUNCTION 1 (1 positional, 0 keyword pair ) 22 LOAD_CONST 2 ("utf-8") 25 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 28 BUILD_LIST 1 31 RETURN_VALUE
    Видно, що інкремент у нашій програмі займає 4 операції. Переривання GIL може наступити будь-який з них.


    Збільшення кількості потоків при відсутності очікування IO в коді обробників HTTP-запитів не призводить до прискорення обробки (а скоріше навіть уповільнює, оскільки потоки можуть «штовхатися», перемикаючи контексти). Реальної паралельності потоки внаслідок обмеження GIL не створюють, хоч і є не green thread"ами, а реальними потоками OS.

    Проведемо ще один тест. Запустимо uwsgi з 1 процесом, але 10 потоками-обробниками в ньому:

    Uwsgi --socket 127.0.0.1:8080 --protocol http --single-interpreter --processes 1 --threads 10 -w app:app
    та виконаємо за допомогою ab 5000 запитів до додатку.

    Ab-n 5000-c 50 http://127.0.0.1:8080/
    Наступні запити curl 127.0.0.1 :8080покажуть, що маємо лише одну зростаючу послідовність, значення якої<= 5000 (меньше 5000 оно может быть в случае race condition на инкременте).

    Вплив мови на архітектуру програми

    Кожен HTTP-запит обробляється в окремому потоці (справедливо і процесів, оскільки процес має мінімум 1 потік). При цьому кожен потік за час свого життя (який в ідеальних умовах збігається з часом життя всього uwsgi додатка) обробляє безліч HTTP-запитів, зберігаючи свій стан (тобто значення змінних рівня модулів) від запиту до запиту. У цьому полягає чи не основна відмінність від моделі обробки HTTP-запитів у PHP, де кожен запит приходить нове щойно проініціалізоване оточення та завантаження програми необхідно виконувати щоразу заново.

    Типовим підходом у великих web-додатках на PHP є використання Dependency Injection Container для управління ініціалізацією та доступом до рівня сервісів програми. Наочним прикладом є Pimple. На кожен HTTP-запит насамперед виконується код ініціалізації, що реєструє всі доступні послуги в контейнері. Далі в міру потреби здійснюється доступ до об'єктом сервісів (lazy) у контролерах. Кожен сервіс може залежати від інших сервісів, залежність дозволяється знову ж таки через контейнер у коді ініціалізації сервісу-агрегату.

    // Визначаємо послуги $container["session_storage"] = function ($c) ( return new SessionStorage("SESSION_ID"); ); $container["session"] = function ($c) ( return new Session($c["session_storage"]); ); // Використовуємо сервіси class MyController ( public function __construct() ( // get the session object $this->session = $container["session"]; // "тестування" страждає, але не суть) )
    Завдяки контейнеру можна забезпечити одноразове створення об'єктів і повернення вже готових об'єктів на кожне наступне звернення до сервісу (якщо необхідно). Але ця магія працює тільки в рамках одного HTTP-запиту, тому послуги можна без проблем ініціалізувати специфічними для запиту значеннями. Такі значення найчастіше - це поточний авторизований користувач, сесія поточного користувача, власне HTTP-запит та ін. До того ж можна практично не турбуватися про витоку пам'яті, якщо обробка одного HTTP-запиту вміщається у відведені для скрипту ліміти, оскільки створення сервісів відбувається на вимогу (lazy) і на один запит кожен необхідний сервіс, швидше за все, буде створений тільки в єдиному екземплярі.

    Тепер, беручи до уваги вищеописану поточну модель Python, можна помітити, що використання аналогічного підходу в Python веб-додатку неможливо без додаткових зусиль. Якщо контейнер буде змінною рівня модуля (що виглядає цілком логічно), всі сервіси, які він містить, не можуть бути проініціалізовані специфічними для поточного запиту значеннями, так як сервіс буде ресурсом, що розділяється між декількома потоками, що випалюють обробку декількох HTTP-запитів псевдо- паралельно. На перший погляд існує два способи впоратися з цією проблемою - зробити об'єкти сервісів незалежними від поточного HTTP-запиту (залежними залишитися виклики методів сервісів, а стікові змінні, що використовуються в методах, не є ресурсами, що розділяються) або ж зробити контейнер - ресурсом потоку, а не процесу (тоді кожен потік буде спілкуватися тільки зі своїм незалежним набором сервісів, а одного моменту один потік може обробляти тільки один HTTP-запит).

    Здається плюс першого підходу - сервіси ініціалізуються лише один раз за весь час життя uwsgi процесу. Можлива також економія пам'яті (оскільки маємо лише один набір сервісів на всі потоки). З іншого боку обробка конкретного HTTP-запиту вимагає лише якогось (швидше за все невеликого) підтримки всіх доступних сервісів. Якщо ж додаток досить великий і має значну кількість сервісів, то після обробки певної кількості HTTP-запитів, переважна більшість сервісів виявиться проініціалізованими і перебувають у пам'яті процесу. Виглядає так, що це може бути серйозною проблемою.

    Другий підхід можна реалізувати з використанням