<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>klen.github.io</title><link href="http://klen.github.io/" rel="alternate"></link><link href="http://klen.github.io/feeds/all.atom.xml" rel="self"></link><id>http://klen.github.io/</id><updated>2015-06-15T00:00:00+03:00</updated><entry><title>Измерение производительности популярных python web-фреймворков</title><link href="http://klen.github.io/python-web-benchmarks.html" rel="alternate"></link><published>2015-06-15T00:00:00+03:00</published><updated>2015-06-15T00:00:00+03:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2015-06-15:python-web-benchmarks.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id6"&gt;Участники тестирования&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id7"&gt;Методика тестирования&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id8"&gt;Результаты&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id9"&gt;Выводы&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;На праздниках решил протестировать популярные python фреймворки для web на
производительность. В отличии от других тестов не стал сосредотачиваться на
деплойменте, тестируя разнообразные связки
uwsgi/gunicorn/chaussette/waitress/nginx и тд. Но на тестировании именно самих
платформ. В частности меня интересовала «плата за асинхронность» — насколько
набирающие популярность асинхронные фреймворки превосходят/уступают синхронным
WSGI-решениям.&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id6"&gt;Участники тестирования&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Описания взяты с оригиналов&lt;/p&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/KeepSafe/aiohttp"&gt;Aiohttp&lt;/a&gt; 0.16.3 -- Асинхронный фреймворк базирующийся на &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-3156/"&gt;Asyncio&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/bottlepy/bottle"&gt;Bottle&lt;/a&gt; 0.12.8  -- Быстрый, простой и легковесный WSGI микрофреймвок;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/django/django"&gt;Django&lt;/a&gt; 1.8.2   -- Веб-фреймворк для перфекционистов с горящими сроками;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/falconry/falcon"&gt;Falcon&lt;/a&gt; 0.3.0   -- Высоко-производительный фреймворк для построения облачных API;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/mitsuhiko/flask"&gt;Flask&lt;/a&gt; 0.10.1   -- Микрофреймворк базирующийся на Werkzeug, Jinja2 и хороших намерениях;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/klen/muffin"&gt;Muffin&lt;/a&gt; 0.0.88  -- Асинхронный фреймворк базирующийся на &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-3156/"&gt;Asyncio&lt;/a&gt; и &lt;a class="reference external" href="https://github.com/KeepSafe/aiohttp"&gt;Aiohttp&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/Pylons/pyramid"&gt;Pyramid&lt;/a&gt; 1.5.7  -- Небольшой, быстрый и понятный веб-фреймворк;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/tornadoweb/tornado"&gt;Tornado&lt;/a&gt; 4.2    -- Асинхронная сетевая библиотека и веб-фреймворк;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Методика тестирования&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Тесты прогонялись на &lt;strong&gt;Amazon EC2&lt;/strong&gt; &lt;a class="reference external" href="http://aws.amazon.com/ec2/instance-types/"&gt;t2.medium&lt;/a&gt; сервере.
Для создания нагрузки использовалась утилита &lt;a class="reference external" href="https://github.com/wg/wrk"&gt;WRK&lt;/a&gt; запущенная на том же сервере с параметрами:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
wrk -d30s -t12 -c400 [URL]
&lt;/pre&gt;
&lt;p&gt;Все приложения (кроме Tornado) запускались при помощи &lt;a class="reference external" href="http://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; (2 процесса на
каждое). Для синхронных WSGI библиотек использовался &lt;a class="reference external" href="http://meinheld.org/"&gt;Meinheld&lt;/a&gt; worker.&lt;/p&gt;
&lt;p&gt;Приложение на Tornado запускало 2 процесса, используя средства самого
фреймворка.&lt;/p&gt;
&lt;p&gt;Все тесты производились с использованием &lt;strong&gt;Python 3.4&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Каждое приложение тестировалось по трем основным сценариям:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;JSON-тест&lt;/strong&gt; -- закодировать небольшой объект в JSON и вернуть клиенту.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote-тест&lt;/strong&gt; -- требуется загрузить ответ от другого сервера и вернуть его
клиенту.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complete-тест&lt;/strong&gt; -- Используя ORM загрузить коллекцию объектов из базы,
добавить к ней еще один и отрендерить список в шаблоне.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Первый сценарий это своеобразный «Hello World!» лишь несколько усложненный
процессом кодирования в JSON, что практически не влияет на результаты.
Показывает именно быстродействие в плане обработке HTTP-запросов.&lt;/p&gt;
&lt;p&gt;Второй сценарий несколько синтетический, тк его результаты весьма предсказуемы.
Тем неменее он должен показывать насколько хорошо платформа справляется с
длительными операциями ожидания во время обработки запроса.&lt;/p&gt;
&lt;p&gt;Третий сценарий предпалагается как комплексный тест и имитирует реальную жизнь,
а именно использование базы данных, ORM, движка шаблонов.&lt;/p&gt;
&lt;p&gt;В качестве базы данных использовался Postgresql с дефолтными настройками.&lt;/p&gt;
&lt;p&gt;Исходники приложений можно найти &lt;a class="reference external" href="https://github.com/klen/py-frameworks-bench/tree/develop/frameworks"&gt;на Github&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Результаты&lt;/a&gt;&lt;/h2&gt;
&lt;link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-bootgrid/1.2.0/jquery.bootgrid.min.css" media="screen"&gt;
&lt;style type="text/css" media="screen"&gt;
    .chart {
        width: 100%;
        height: 400px;
        margin: 3em 0 1.5em;
    }
    .table {
        border-collapse: collapse !important;
        font-size: .9rem;
        width: 100%;
        max-width: 100%;
        margin-bottom: 20px;
    }
    .table &gt; thead &gt; tr &gt; th,
    .table &gt; tbody &gt; tr &gt; th,
    .table &gt; tfoot &gt; tr &gt; th,
    .table &gt; thead &gt; tr &gt; td,
    .table &gt; tbody &gt; tr &gt; td,
    .table &gt; tfoot &gt; tr &gt; td {
        padding: 8px;
        line-height: 1.42857143;
        vertical-align: top;
        border-top: 1px solid #ddd;
    }
    .table &gt; thead &gt; tr &gt; th {
        vertical-align: bottom;
        border-bottom: 2px solid #ddd;
    }
    .table &gt; caption + thead &gt; tr:first-child &gt; th,
    .table &gt; colgroup + thead &gt; tr:first-child &gt; th,
    .table &gt; thead:first-child &gt; tr:first-child &gt; th,
    .table &gt; caption + thead &gt; tr:first-child &gt; td,
    .table &gt; colgroup + thead &gt; tr:first-child &gt; td,
    .table &gt; thead:first-child &gt; tr:first-child &gt; td {
        border-top: 0;
    }
    .table &gt; tbody + tbody {
        border-top: 2px solid #ddd;
    }
    .table .table {
        background-color: #fff;
    }
    .table-striped &gt; tbody &gt; tr:nth-of-type(odd) {
        background-color: #f9f9f9;
    }
    .table-hover &gt; tbody &gt; tr:hover {
        background-color: #f5f5f5;
    }
    .table &gt; thead &gt; tr &gt; td.active,
    .table &gt; tbody &gt; tr &gt; td.active,
    .table &gt; tfoot &gt; tr &gt; td.active,
    .table &gt; thead &gt; tr &gt; th.active,
    .table &gt; tbody &gt; tr &gt; th.active,
    .table &gt; tfoot &gt; tr &gt; th.active,
    .table &gt; thead &gt; tr.active &gt; td,
    .table &gt; tbody &gt; tr.active &gt; td,
    .table &gt; tfoot &gt; tr.active &gt; td,
    .table &gt; thead &gt; tr.active &gt; th,
    .table &gt; tbody &gt; tr.active &gt; th,
    .table &gt; tfoot &gt; tr.active &gt; th {
        background-color: #f5f5f5;
    }
    .table-hover &gt; tbody &gt; tr &gt; td.active:hover,
    .table-hover &gt; tbody &gt; tr &gt; th.active:hover,
    .table-hover &gt; tbody &gt; tr.active:hover &gt; td,
    .table-hover &gt; tbody &gt; tr:hover &gt; .active,
    .table-hover &gt; tbody &gt; tr.active:hover &gt; th {
        background-color: #e8e8e8;
    }
    .table &gt; thead &gt; tr &gt; td.danger,
    .table &gt; tbody &gt; tr &gt; td.danger,
    .table &gt; tfoot &gt; tr &gt; td.danger,
    .table &gt; thead &gt; tr &gt; th.danger,
    .table &gt; tbody &gt; tr &gt; th.danger,
    .table &gt; tfoot &gt; tr &gt; th.danger,
    .table &gt; thead &gt; tr.danger &gt; td,
    .table &gt; tbody &gt; tr.danger &gt; td,
    .table &gt; tfoot &gt; tr.danger &gt; td,
    .table &gt; thead &gt; tr.danger &gt; th,
    .table &gt; tbody &gt; tr.danger &gt; th,
    .table &gt; tfoot &gt; tr.danger &gt; th {
        background-color: #f2dede;
    }
    .table-hover &gt; tbody &gt; tr &gt; td.danger:hover,
    .table-hover &gt; tbody &gt; tr &gt; th.danger:hover,
    .table-hover &gt; tbody &gt; tr.danger:hover &gt; td,
    .table-hover &gt; tbody &gt; tr:hover &gt; .danger,
    .table-hover &gt; tbody &gt; tr.danger:hover &gt; th {
        background-color: #ebcccc;
    }
&lt;/style&gt;&lt;div id="json" class="chart"&gt;&lt;/div&gt;
&lt;table class="table table-stripped table-condensed"&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th data-column-id="name"&gt;Name&lt;/th&gt;
            &lt;th data-column-id="p50" data-type="numeric"&gt;50% (ms)&lt;/th&gt;
            &lt;th data-column-id="p75" data-type="numeric"&gt;75% (ms)&lt;/th&gt;
            &lt;th data-column-id="avg" data-type="numeric"&gt;Avg (ms)&lt;/th&gt;
            &lt;th data-column-id="req" data-type="numeric" data-order="desc"&gt;Req/s&lt;/th&gt;
            &lt;th data-column-id="errors"&gt;Non 200-x&lt;/th&gt;
            &lt;th data-column-id="timeouts"&gt;Timeouts&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
            &lt;tr&gt; &lt;td&gt;Aiohttp&lt;/td&gt; &lt;td&gt;91.67 &lt;/td&gt; &lt;td&gt;103.1 &lt;/td&gt; &lt;td&gt;108.01&lt;/td&gt; &lt;td&gt;4093.41 &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Bottle&lt;/td&gt;  &lt;td&gt;24.77 &lt;/td&gt; &lt;td&gt;26.23 &lt;/td&gt; &lt;td&gt;25.06 &lt;/td&gt; &lt;td&gt;15761.45&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Django&lt;/td&gt;  &lt;td&gt;103.2 &lt;/td&gt; &lt;td&gt;112.19&lt;/td&gt; &lt;td&gt;103.36&lt;/td&gt; &lt;td&gt;3696.90 &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Falcon&lt;/td&gt;  &lt;td&gt;19.24 &lt;/td&gt; &lt;td&gt;19.81 &lt;/td&gt; &lt;td&gt;19.19 &lt;/td&gt; &lt;td&gt;20677.13&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Flask&lt;/td&gt;   &lt;td&gt;64.32 &lt;/td&gt; &lt;td&gt;71.59 &lt;/td&gt; &lt;td&gt;65.68 &lt;/td&gt; &lt;td&gt;6023.40 &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Muffin&lt;/td&gt;  &lt;td&gt;108.07&lt;/td&gt; &lt;td&gt;115.09&lt;/td&gt; &lt;td&gt;171.56&lt;/td&gt; &lt;td&gt;3575.36 &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Pyramid&lt;/td&gt; &lt;td&gt;41.75 &lt;/td&gt; &lt;td&gt;43.49 &lt;/td&gt; &lt;td&gt;41.63 &lt;/td&gt; &lt;td&gt;9402.69 &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Tornado&lt;/td&gt; &lt;td&gt;138.24&lt;/td&gt; &lt;td&gt;149.84&lt;/td&gt; &lt;td&gt;136.87&lt;/td&gt; &lt;td&gt;2829.72 &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;&lt;p&gt;В первом простом тесте с хорошим отрывом победили синхронные фреймворки. Не
считая &lt;strong&gt;Django&lt;/strong&gt;, но в оправдание последнего, можно сказать, что по-умолчанию этот
фреймворк делает множество работы (middlewares by default). Асинхронные
фреймворки делят места аутсайдеров и неожиданно для меня на последнем месте
оказался &lt;strong&gt;Tornado&lt;/strong&gt;. Очень хорошие результаты у &lt;strong&gt;Falcon&lt;/strong&gt; и &lt;strong&gt;Bottle&lt;/strong&gt;.&lt;/p&gt;
&lt;div id="remote" class="chart"&gt;&lt;/div&gt;
&lt;table class="table table-stripped"&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th data-column-id="name"&gt;Name&lt;/th&gt;
            &lt;th data-column-id="p50" data-type="numeric"&gt;50% (ms)&lt;/th&gt;
            &lt;th data-column-id="p75" data-type="numeric"&gt;75% (ms)&lt;/th&gt;
            &lt;th data-column-id="avg" data-type="numeric"&gt;Avg (ms)&lt;/th&gt;
            &lt;th data-column-id="req" data-type="numeric" data-order="desc"&gt;Req/s&lt;/th&gt;
            &lt;th data-column-id="errors"&gt;Non 200-x&lt;/th&gt;
            &lt;th data-column-id="timeouts"&gt;Timeouts&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
            &lt;tr&gt; &lt;td&gt;Aiohttp&lt;/td&gt; &lt;td&gt;358.08 &lt;/td&gt; &lt;td&gt;369.08  &lt;/td&gt; &lt;td&gt;338.94 &lt;/td&gt; &lt;td&gt;1120.27&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;     &lt;/td&gt;  &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Bottle&lt;/td&gt;  &lt;td&gt;3363.74&lt;/td&gt; &lt;td&gt;9911.84 &lt;/td&gt; &lt;td&gt;6403.92&lt;/td&gt; &lt;td&gt;19.09  &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;335  &lt;/td&gt;  &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Django&lt;/td&gt;  &lt;td&gt;3317.64&lt;/td&gt; &lt;td&gt;12954.23&lt;/td&gt; &lt;td&gt;6918.64&lt;/td&gt; &lt;td&gt;18.96  &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;300  &lt;/td&gt;  &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Falcon&lt;/td&gt;  &lt;td&gt;3196.23&lt;/td&gt; &lt;td&gt;12976.84&lt;/td&gt; &lt;td&gt;6696.17&lt;/td&gt; &lt;td&gt;19.28  &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;328  &lt;/td&gt;  &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Flask&lt;/td&gt;   &lt;td&gt;3306.88&lt;/td&gt; &lt;td&gt;11690.8 &lt;/td&gt; &lt;td&gt;6824.88&lt;/td&gt; &lt;td&gt;19.16  &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;363  &lt;/td&gt;  &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Muffin&lt;/td&gt;  &lt;td&gt;372.95 &lt;/td&gt; &lt;td&gt;428.75  &lt;/td&gt; &lt;td&gt;376.98 &lt;/td&gt; &lt;td&gt;1019.76&lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;     &lt;/td&gt;  &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Pyramid&lt;/td&gt; &lt;td&gt;3295.1 &lt;/td&gt; &lt;td&gt;10518.92&lt;/td&gt; &lt;td&gt;6673.78&lt;/td&gt; &lt;td&gt;19.35  &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;338  &lt;/td&gt;  &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Tornado&lt;/td&gt; &lt;td&gt;1994.39&lt;/td&gt; &lt;td&gt;2069.25 &lt;/td&gt; &lt;td&gt;1928.31&lt;/td&gt; &lt;td&gt;194.37 &lt;/td&gt; &lt;td&gt;&lt;/td&gt; &lt;td&gt;     &lt;/td&gt; &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;&lt;p&gt;Для понимания результатов следующего теста необходимо пояснить, что приложения
обращались к nginx настроенному на ответ с задержкой 100ms. Из-за этого
результаты синхронных фреймворков очень близки, практически вся их работа после
определенного момента сводилась к ожиданию. Опять неожиданные результаты от
&lt;strong&gt;Tornado&lt;/strong&gt;, я предполагал, что он будет близок к &lt;strong&gt;Aiohttp&lt;/strong&gt; и &lt;strong&gt;Muffin&lt;/strong&gt;. Но тем неменее
&lt;strong&gt;Tornado&lt;/strong&gt; в 10 раз эффективнее в этом кейсе чем ближайший синхронный фреймворк.&lt;/p&gt;
&lt;div id="complete" class="chart"&gt;&lt;/div&gt;
&lt;table class="table table-stripped"&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th data-column-id="name"&gt;Name&lt;/th&gt;
            &lt;th data-column-id="p50" data-type="numeric"&gt;50% (ms)&lt;/th&gt;
            &lt;th data-column-id="p75" data-type="numeric"&gt;75% (ms)&lt;/th&gt;
            &lt;th data-column-id="avg" data-type="numeric"&gt;Avg (ms)&lt;/th&gt;
            &lt;th data-column-id="req" data-type="numeric" data-order="desc"&gt;Req/s&lt;/th&gt;
            &lt;th data-column-id="errors"&gt;Non 2xx&lt;/th&gt;
            &lt;th data-column-id="timeouts"&gt;Timeouts&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
            &lt;tr&gt; &lt;td&gt;Aiohttp&lt;/td&gt; &lt;td&gt;151.78 &lt;/td&gt; &lt;td&gt;156.9  &lt;/td&gt; &lt;td&gt;254.75 &lt;/td&gt; &lt;td&gt;1004.82&lt;/td&gt; &lt;td&gt;68%&lt;/td&gt; &lt;td&gt;236  &lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Bottle&lt;/td&gt;  &lt;td&gt;613.5  &lt;/td&gt; &lt;td&gt;630.17 &lt;/td&gt; &lt;td&gt;1062.86&lt;/td&gt; &lt;td&gt;451.34 &lt;/td&gt; &lt;td&gt;&lt;/td&gt;    &lt;td&gt;178  &lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Django&lt;/td&gt;  &lt;td&gt;1610.46&lt;/td&gt; &lt;td&gt;1976.44&lt;/td&gt; &lt;td&gt;2632.36&lt;/td&gt; &lt;td&gt;88.57  &lt;/td&gt; &lt;td&gt;&lt;/td&gt;    &lt;td&gt;42   &lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Falcon&lt;/td&gt;  &lt;td&gt;766.75 &lt;/td&gt; &lt;td&gt;805.35 &lt;/td&gt; &lt;td&gt;1457.99&lt;/td&gt; &lt;td&gt;350.26 &lt;/td&gt; &lt;td&gt;&lt;/td&gt;    &lt;td&gt;81   &lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Flask&lt;/td&gt;   &lt;td&gt;1032.63&lt;/td&gt; &lt;td&gt;1649.89&lt;/td&gt; &lt;td&gt;1465.25&lt;/td&gt; &lt;td&gt;222.78 &lt;/td&gt; &lt;td&gt;&lt;/td&gt;    &lt;td&gt;496  &lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Muffin&lt;/td&gt;  &lt;td&gt;420.14 &lt;/td&gt; &lt;td&gt;485.4  &lt;/td&gt; &lt;td&gt;1552.7 &lt;/td&gt; &lt;td&gt;819.62 &lt;/td&gt; &lt;td&gt;&lt;/td&gt;    &lt;td&gt;     &lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Pyramid&lt;/td&gt; &lt;td&gt;562.44 &lt;/td&gt; &lt;td&gt;601.49 &lt;/td&gt; &lt;td&gt;812.43 &lt;/td&gt; &lt;td&gt;248.42 &lt;/td&gt; &lt;td&gt;&lt;/td&gt;    &lt;td&gt;235  &lt;/td&gt; &lt;/tr&gt;
            &lt;tr&gt; &lt;td&gt;Tornado&lt;/td&gt; &lt;td&gt;937.37 &lt;/td&gt; &lt;td&gt;988.86 &lt;/td&gt; &lt;td&gt;910.06 &lt;/td&gt; &lt;td&gt;418.36 &lt;/td&gt; &lt;td&gt;&lt;/td&gt;    &lt;td&gt;     &lt;/td&gt; &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;&lt;p&gt;И последний тест. Результаты Aiohttp можно игнорировать тк к сожалению более двух третей запросов вернули 502 ошибки.
На первом месте неожиданно, но приятно, оказался мой фреймворк &lt;a class="reference external" href="https://github.com/klen/muffin"&gt;Muffin&lt;/a&gt; достаточно быстро обрабатывающий этот тест.
&lt;a class="reference external" href="https://github.com/django/django"&gt;Django&lt;/a&gt; значительно проигрывает, лишь подтверждая медлительность стандартного движка шаблонов и ORM.&lt;/p&gt;
&lt;script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"&gt;&lt;/script&gt;
&lt;script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/4.1.5/highcharts.js"&gt;&lt;/script&gt;
&lt;script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-bootgrid/1.2.0/jquery.bootgrid.min.js"&gt;&lt;/script&gt;
&lt;script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-bootgrid/1.2.0/jquery.bootgrid.fa.min.js"&gt;&lt;/script&gt;
&lt;script language="javascript" type="text/javascript"&gt;
    (function($){
        $(function () {

            var data = {
                "pyramid": [[0.26, 41.75, 43.49, 45.49, 48.83, 204.31, 412.87, 41.63, 30.04, 282457.0, 0.0, 0.0, 0.0], [123.16, 3295.1, 10518.92, 17411.24, 23569.44, 24190.6, 24190.6, 6673.78, 30.07, 582.0, 0.0, 338.0, 0.0], [44.73, 562.44, 601.49, 872.25, 9289.18, 14654.82, 14714.02, 812.43, 30.09, 7475.0, 0.0, 235.0, 0.0]],
                "aiohttp": [[1.96, 91.67, 103.1, 107.48, 848.42, 1892.42, 2347.08, 108.01, 30.05, 123007.0, 0.0, 0.0, 0.0], [104.16, 358.08, 369.08, 380.07, 402.21, 703.7, 904.4, 338.94, 30.04, 33653.0, 0.0, 0.0, 0.0], [2.58, 151.78, 156.9, 161.79, 5906.94, 12441.32, 13993.53, 254.75, 30.05, 30195.0, 20442.0, 236.0, 0.0]],
                "flask": [[1.13, 64.32, 71.59, 74.38, 78.11, 328.78, 551.97, 65.68, 30.04, 180943.0, 0.0, 0.0, 0.0], [118.92, 3306.88, 11690.8, 16981.66, 22278.62, 29043.58, 29043.58, 6824.88, 30.06, 576.0, 0.0, 363.0, 0.0], [35.75, 1032.63, 1649.89, 2726.3, 8822.6, 10403.23, 10478.41, 1465.25, 30.07, 6699.0, 0.0, 496.0, 0.0]],
                "muffin": [[2.41, 108.07, 115.09, 120.97, 2554.97, 4252.73, 4996.43, 171.56, 30.04, 107404.0, 0.0, 0.0, 0.0], [108.61, 372.95, 428.75, 449.83, 487.94, 1300.74, 1564.79, 376.98, 30.05, 30644.0, 0.0, 0.0, 0.0], [9.17, 420.14, 485.4, 4964.92, 16890.25, 21367.9, 23015.05, 1552.7, 30.06, 24638.0, 0.0, 0.0, 0.0]],
                "django": [[5.75, 103.2, 112.19, 114.23, 117.67, 445.56, 681.68, 103.36, 30.04, 111055.0, 0.0, 0.0, 0.0], [121.64, 3317.64, 12954.23, 18790.94, 21321.65, 27579.58, 27579.58, 6918.64, 30.06, 570.0, 0.0, 300.0, 0.0], [155.24, 1610.46, 1976.44, 6974.99, 14514.59, 20437.6, 20478.44, 2632.36, 30.1, 2666.0, 0.0, 42.0, 0.0]],
                "tornado": [[1.61, 138.24, 149.84, 156.4, 161.76, 169.15, 311.63, 136.87, 30.04, 85005.0, 0.0, 0.0, 0.0], [178.48, 1994.39, 2069.25, 2087.02, 2126.76, 2239.36, 2300.74, 1928.31, 30.05, 5841.0, 0.0, 0.0, 0.0], [19.12, 937.37, 988.86, 1015.01, 1049.07, 1576.72, 1603.96, 910.06, 30.06, 12576.0, 0.0, 0.0, 0.0]],
                "bottle": [[0.19, 24.77, 26.23, 27.45, 31.91, 103.28, 295.81, 25.06, 30.04, 473474.0, 0.0, 0.0, 0.0], [122.9, 3363.74, 9911.84, 16793.91, 22170.11, 25412.56, 25412.56, 6403.92, 30.06, 574.0, 0.0, 335.0, 0.0], [37.44, 613.5, 630.17, 1866.8, 8491.27, 12824.83, 14535.27, 1062.86, 30.05, 13563.0, 0.0, 178.0, 0.0]],
                "falcon": [[0.13, 19.24, 19.81, 20.45, 22.3, 100.12, 273.87, 19.19, 30.09, 622175.0, 0.0, 0.0, 0.0], [123.16, 3196.23, 12976.84, 16166.13, 21823.98, 24082.81, 24082.81, 6696.17, 30.07, 580.0, 0.0, 328.0, 0.0], [26.47, 766.75, 805.35, 3142.8, 14607.38, 17632.72, 17883.95, 1457.99, 30.1, 10543.0, 0.0, 81.0, 0.0]]
            };

            var frameworks = [
                'aiohttp',
                'bottle',
                'django',
                'falcon',
                'flask',
                'muffin',
                'pyramid',
                'tornado',
            ],
                categories = ["min", "50%", "75%", "90%", "99.9%", "max"],
                series = [],
                common_options = {
                    xAxis: {
                        categories: categories,
                        startOnTick: true
                    },
                    yAxis: {
                        title: { text: 'Values (ms)' },
                        min: 0
                    },
                    tooltip: {
                        valueSuffix: 'ms'
                    },
                    legend: {
                        layout: 'vertical',
                        align: 'right',
                        verticalAlign: 'middle',
                        borderWidth: 0
                    },
                    chart: {
                        zoomType: 'y',
                        type: 'spline'
                    }
                };

            for (i in [0, 1, 2]) {
                var cur = series[i] = [], pie = {
                    name: 'mid', type: 'pie', dataLabels: { enabled: true, format: '{point.name} {point.y:.2f} ms'},
                    data: [], center: [100, 80], size: 100};

                for (n in data) {
                    if (data[n][i] instanceof Array) {
                        pie.data.push({name: n, y: data[n][i][2]})
                        cur.push({
                            name: n,
                            data: data[n][i].slice(0, 4)
                        });
                    }
                };
                cur.push(pie);
            };

            $('#json').highcharts($.extend({
                title: { text: 'Encode a object to JSON and return as response' },
                subtitle: { text: 'lower is better' },
                series: series[0]}, common_options));

            $('#remote').highcharts($.extend({
                title: { text: 'Load a response from remote server and return as response' },
                subtitle: { text: 'lower is better' },
                series: series[1]}, common_options));

            $('#complete').highcharts($.extend({
                title: { text: 'Load data from database with ORM and render to template' },
                subtitle: { text: 'lower is better' },
                series: series[2]}, common_options));

            $('table').bootgrid({ navigation: 0 });

        });

    })(jQuery);
&lt;/script&gt;&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Выводы&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Я предлагаю читателю сделать их самостоятельно. Данное тестирование
производилось мой чтобы понять выгоду использования синхронных/асинхронных
библиотек и показатели производительности популярных решений.&lt;/p&gt;
&lt;p&gt;В дальнейшем планирую проводить данные измерения регулярно.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="benchmarks"></category><category term="aiohttp"></category><category term="muffin"></category><category term="django"></category><category term="tornado"></category><category term="flask"></category><category term="falcon"></category><category term="pyramid"></category><category term="bottle"></category></entry><entry><title>Graphite-beacon — простая система оповещений для Graphite</title><link href="http://klen.github.io/graphite-beacon.html" rel="alternate"></link><published>2014-10-30T00:00:00+03:00</published><updated>2014-10-30T00:00:00+03:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2014-10-30:graphite-beacon.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;img alt="" src="/static/img/beacon/grafana.jpg" /&gt;
&lt;p&gt;Для мониторинга своих проектов я использую связку из &lt;a class="reference external" href="http://graphite.wikidot.com"&gt;Graphite&lt;/a&gt; + &lt;a class="reference external" href="http://grafana.org"&gt;Grafana&lt;/a&gt;.
Данные с серверов собирает &lt;a class="reference external" href="http://collectd.org"&gt;Collectd&lt;/a&gt;, метрики приложений принимает &lt;a class="reference external" href="https://github.com/etsy/statsd"&gt;Statsd&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Недавно в очередной раз поднимая эту инфраструктуру, я понял, что мне не
хватает сигналов на основе данных метрик из &lt;a class="reference external" href="http://graphite.wikidot.com"&gt;Graphite&lt;/a&gt;. Например
оповещения при падении свободной памяти ниже критического значения и тп.&lt;/p&gt;
&lt;p&gt;Разумеется я не первый у кого возникла такая необходимость и существует ряд
OpenSource приложений для данной задачи. Вот некоторые их них: &lt;a class="reference external" href="https://github.com/scobal/seyren"&gt;Seyren&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/arachnys/cabot"&gt;Cabot&lt;/a&gt;,
&lt;a class="reference external" href="https://github.com/seatgeek/graphite-pager"&gt;graphite-pager&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/ybrs/graphite-alerts"&gt;graphite-alerts&lt;/a&gt;. Вооружившись системой конфигурирования
серверов я уже начал писать роли для развертывания одного из перечисленных
решений, но через некоторое время осознал, что хочу сделать еще одно.&lt;/p&gt;
&lt;p&gt;Итак, что же мне не понравилось?&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/scobal/seyren"&gt;Seyren&lt;/a&gt; — Java приложение (требует установленного MongoDB) при настройке
возникли проблемы (я уже не помню какого рода) и я вернулся к другим вариантам.&lt;/p&gt;
&lt;p&gt;Поддерживает множество типов оповещений: SMTP, Hubit, IRC, HipChat и прочее.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/seatgeek/graphite-pager"&gt;graphite-pager&lt;/a&gt; — Python-приложение (требует установленного Redis). При
установке лично у меня возникли конфликты в python-зависимостях, в целом
решаемые. Главная для меня проблема: нет поддержки SMTP.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/ybrs/graphite-alerts"&gt;graphite-alerts&lt;/a&gt; — Форк &lt;a class="reference external" href="https://github.com/seatgeek/graphite-pager"&gt;graphite-pager&lt;/a&gt;, как я понимаю добавлен веб-интерфейс
для настройки. Также требует Redis, плюс судя по зависимостям БД для
SQLAlchemy. Понятно, что можно использовать SQLLite, но тем не менее.
Установка описана очень поверхностно и желания разбираться не вызывает. Плюс
наследует от прародителя главный для меня недостаток: нет поддержки SMTP.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/arachnys/cabot"&gt;Cabot&lt;/a&gt; — Подозреваю самое мощное решение из перечисленных. Для установки
предлагается использовать поставляемый с исходниками fabfile, требования и
зависимости нигде не описаны. Судя по исходникам запускает Django +
Gunicorn для веб-интерфейса и Celery для переодических задач. Хочет
Postgres и Redis для базы данных и брокера соответственно.&lt;/p&gt;
&lt;p&gt;Не понравился прежде всего мутной процедурой установки (в принципе все ясно), но
могли бы и прямо написать. Но это не главное, прежде всего я не хочу запускать
столько процессов чтобы иметь оповещения об изменении метрик из &lt;a class="reference external" href="http://graphite.wikidot.com"&gt;Graphite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Я хочу легко устанавливаемое и конфигурируемое приложение, потребляющее минимум
ресурсов, без дополнительных зависимостей и с поддержкой SMTP.&lt;/p&gt;
&lt;img alt="" src="https://raw.githubusercontent.com/klen/graphite-beacon/develop/beacon.png" /&gt;
&lt;p&gt;Поэтому на свет появился &lt;a class="reference external" href="https://github.com/klen/graphite-beacon"&gt;Graphite-beacon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Это простое python-приложение не требуещее для работы базы данных или очереди
задач. Работает на основе &lt;a class="reference external" href="http://www.tornadoweb.org/en/stable"&gt;Tornado&lt;/a&gt; и написано в асинхронном стиле. На текущий
момент для оповещений поддерживается SMTP и HipChat. При необходимости несложно
добавить новые.&lt;/p&gt;
&lt;p&gt;Конфигурируется «маяк» при помощи JSON файлов и имеет интуитивно понятный
формат:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Да здесь даже можно использовать комментарии&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;graphite_url&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;http://g.server.org&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;smtp&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;from&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;beacon@server.org&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;to&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;me@gmail.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;alerts&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MEM&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;format&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bytes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;aliasByNode(sumSeriesWithWildcards(collectd.*.memory.{memory-free,memory-cached}, 3), 1)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;rules&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;critical: &amp;lt; 200MB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;warning: &amp;lt; 400MB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;CPU&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;format&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;percent&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;interval&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;30m&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;aliasByNode(sumSeriesWithWildcards(collectd.*.cpu-*.cpu-user, 2), 1)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;rules&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;critical: &amp;gt;= 80%&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;warning: &amp;gt;= 70%&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В данном примере «маяк» получает информацию по всем нодам, и высылает
предупреждения при низком количестве памяти или высокой загрузки процессора на
каком либо из подключенных серверов. Включая вложенные в письма графики из
&lt;a class="reference external" href="http://graphite.wikidot.com"&gt;Graphite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Подробнее смотрите в описании &lt;a class="reference external" href="https://github.com/klen/graphite-beacon"&gt;Graphite-beacon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Для Debian систем существует репозиторий с готовыми пакетами:
&lt;a class="reference external" href="http://dl.bintray.com/klen/deb"&gt;http://dl.bintray.com/klen/deb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Возможно для вас этот простой инструмент будет также полезен как и для меня.
Если у вас возникли вопросы, не стесняйтесь их задавать в комментариях.&lt;/p&gt;
&lt;p&gt;Задачи на будущее:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Анализ аномальных пиков на метриках;&lt;/li&gt;
&lt;li&gt;Прикручивание поддержки Hubot;&lt;/li&gt;
&lt;li&gt;Веб-интерфейс, но зачем?&lt;/li&gt;
&lt;/ul&gt;
</summary><category term="python"></category><category term="graphite"></category><category term="tornado"></category><category term="monitoring"></category></entry><entry><title>Mixer -- удобная генерация данных для тестирования</title><link href="http://klen.github.io/mixer.html" rel="alternate"></link><published>2013-06-17T00:00:00+04:00</published><updated>2013-06-17T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2013-06-17:mixer.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id9"&gt;Пример работы&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id10"&gt;Продвинутое использование&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id11"&gt;Указание моделей через путь&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id12"&gt;mixer.fake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id6" id="id13"&gt;mixer.select&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#mixer-mix" id="id14"&gt;mixer.mix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#mixer-sequence" id="id15"&gt;mixer.sequence&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id7" id="id16"&gt;Преимущества по сравнению с другими библиотеками&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;&lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; — это удобная библиотека для генерации данных для тестов. Из коробки
обладает поддержкой &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; ORM, &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; ORM, &lt;a class="reference external" href="http://mongoengine.org/"&gt;Mongoengine&lt;/a&gt; ODM и простым
синтаксисом для интеграции любых бэкэндов.
Работает в версиях python 2-х и 3-х версий.&lt;/p&gt;
&lt;p&gt;Обычно при разработке тестов, программисты нуждаются в генерации данных для них.
Тут можно выделить два пути, первый — подготовить данные заранее
(используя так называемые &lt;strong&gt;fixtures&lt;/strong&gt;) и второй — генерировать данные для
тестов непосредственно в момент выполнения.&lt;/p&gt;
&lt;p&gt;Первый обладает рядом минусов. Во-первых данные необходимо готовить заранее.
Во-вторых при изменении структуры данных необходимо тратить ресурсы на
поддержку актуальности &lt;strong&gt;fixtures&lt;/strong&gt;. В третьих вы не можете получить больше
данных чем приготовили.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; предлагает второй способ. Вы получаете данные непосредственно в тестах,
в любом необходимом количестве и не зависите от изменений в их структуре.&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Пример работы&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Несколько примеров для &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; как для наиболее распространненного сейчас
&lt;strong&gt;python-фреймворка&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Создадим стандартного пользователя используя &lt;tt class="docutils literal"&gt;django.contrib.auth.models.User&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mixer.backend.django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Мы только что создали и сохранили в тестовой базе пользователя, со всеми
необходимыми полями. Разумеется &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; правильно обработает отношения и при
необходимости автоматически создаст и объекты зависимых моделей.
Вам нет необходимости настраивать параметры создавамого объекта, вы просто
указываете модель и остальное &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; делает самостоятельно.&lt;/p&gt;
&lt;p&gt;По-умолчанию &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; старается генерировать данные не только учитывая тип полей,
но и анализируя их названия. Например при проверке примеров библиотека
сгенерировала пользователя с логином &lt;cite&gt;mixcookie1957&lt;/cite&gt;, адресом электронной
почты: &lt;cite&gt;limb_candy&amp;#64;wordpress.br&lt;/cite&gt; и именем: &lt;cite&gt;Robinson Wolverhampton&lt;/cite&gt;. Но такое
поведение разумеется можно отключить и получать абсолютно случайные данные.&lt;/p&gt;
&lt;p&gt;Если вам нужно сгенерировать несколько объектов, имеется короткий синтаксис.
Создадим еще 5 пользователей.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Зачастую возникает неоходимость предопределить некоторые значения генерируемого
объекта. Синтаксис &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; прост и интуитивно понятен. Создадим пользователя
с логином &lt;cite&gt;testuser&lt;/cite&gt; и группой &lt;cite&gt;admin&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;testuser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Но &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; позволяет сделать это еще быстрее. Вышеприведенный код генерации
можно заменить одной строчкой:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;testuser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups__name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Синтаксис знаком пользователям &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt;, &lt;cite&gt;&amp;quot;__&amp;quot;&lt;/cite&gt; позволяет указывать в фильтрации
запросов, поля зависимых объектов. Так и в &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; разделитель &lt;cite&gt;&amp;quot;__&amp;quot;&lt;/cite&gt; позволяет
предзадавать поля зависимых объектов не заботясь об их генерации. Использование
может быть очень гибким.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author__name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Duck Nukem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author__age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author__clan__title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;crazy beaches&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;По-умолчанию &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; пропускает поля имеющие значение по умолчанию или
позволяющие &lt;cite&gt;None&lt;/cite&gt; значение. Вы всегда можете предзадать их вручную как в
вышеприведенном примере или позволить &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; сделать это за вас используя
волшебный атрибут &lt;cite&gt;mixer.random&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DecimalField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;food&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;drinks&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Здесь price=0 и type=None&lt;/span&gt;
&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# А здесь price это какой то decimal, а type равен `food` или `drinks`&lt;/span&gt;
&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p id="generators"&gt;В качестве предзаданных данных &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; поддерживает также функции и генераторы.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Пример с функцией&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_super&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;super&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Superman&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;get_super&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# Пример с генератором&lt;/span&gt;

&lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Еще один&lt;/span&gt;
&lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Синтаксис для работы &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;, &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt;, &lt;a class="reference external" href="http://mongoengine.org/"&gt;Mongoengine&lt;/a&gt; схож и в данной статье
рассматриваться не будет. Импортируете необходимый бэкэнд, указываете модель
и получаете данные.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Продвинутое использование&lt;/a&gt;&lt;/h2&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id11"&gt;Указание моделей через путь&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Вам необязательно импортировать модели для генерации тестовых данных. &lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt;
способен сделать это за вас. Для &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; достаточно указать строку вида:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;имя_приложения&amp;gt;.&amp;lt;имя_модели&amp;gt;&lt;/span&gt;&lt;/tt&gt;. Для других бэкэндов придется указать python
путь для импорта. Пример из начала статьи можно изменить так:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth.user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p id="mixer-fake"&gt;Другие волшебные атрибуты:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id12"&gt;mixer.fake&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Поведение &lt;tt class="docutils literal"&gt;mixer.fake&lt;/tt&gt; похоже на &lt;a class="reference internal" href="#mixer-random"&gt;mixer.random&lt;/a&gt;, но в отличии от последнего,
генерирующего случайные данные, данный атрибут генерирует данные «фейковые
данные» анализируя имя поля. Например для строкового поля &lt;tt class="docutils literal"&gt;login&lt;/tt&gt; это будет
строка имитирующая логин пользователя, а не просто случайный набор символов.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;app.message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;И &lt;a class="reference internal" href="#mixer-random"&gt;mixer.random&lt;/a&gt; и &lt;a class="reference internal" href="#mixer-fake"&gt;mixer.fake&lt;/a&gt; позволяют принудительно указать тип
генерируемого значения: mixer.random(int), mixer.fake(models.CharField)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;span id="mixer-select"&gt;&lt;/span&gt;&lt;h3&gt;&lt;a class="toc-backref" href="#id13"&gt;mixer.select&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;mixer.select&lt;/tt&gt; похож на &lt;a class="reference internal" href="#mixer-fake"&gt;mixer.fake&lt;/a&gt; и &lt;a class="reference internal" href="#mixer-random"&gt;mixer.random&lt;/a&gt;, но работает с
уже существующими данными. В приведенном ниже примере пользователю присваивается
одна из существующих в базе данных групп.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth.user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;mixer.select&lt;/tt&gt; может принимать параметры фильтрации, например для &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth.user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="mixer-mix"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id14"&gt;mixer.mix&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;mixer.mix&lt;/tt&gt; это волшебный атрибут указывающий на будущее сгенерированное
значение. Например создадим пользователя с одинаковыми &lt;cite&gt;username&lt;/cite&gt; и
&lt;cite&gt;last_name&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth.user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Как мы помним &lt;tt class="docutils literal"&gt;mixer.mix&lt;/tt&gt; указывает на объект который будет сгенерирован.
Поэтому &lt;tt class="docutils literal"&gt;mixer.mix.first_name&lt;/tt&gt; будет указывать на поле этого объекта и цель
достигнута.&lt;/p&gt;
&lt;p&gt;Использование &lt;tt class="docutils literal"&gt;mixer.mix&lt;/tt&gt; может быть и более интересным.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ship&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Ship&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;captain__country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;captain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nick&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;mixer.mix&lt;/tt&gt; может принимать функцию от одного аргумента, которая получит
при вызове сгенерированный объект.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="mixer-sequence"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id15"&gt;mixer.sequence&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt;, как уже указывалось, умеет принимать в качестве значений полей генераторы.
&lt;tt class="docutils literal"&gt;mixer.sequence&lt;/tt&gt; это помощник для создания генераторов из функций. Он принимает
функцию от одного аргумента и на ее основе создает генератор.&lt;/p&gt;
&lt;p&gt;Например несколько пользователей с логинами вида: 'test0', 'test1' и тд.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth.user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В функцию будет приходить счетчик итераций. Вышеприведенная операция встречается
довольно часто, поэтому &lt;tt class="docutils literal"&gt;mixer.sequence&lt;/tt&gt; поддерживает короткий синтаксис.
При передачи строки он трансформирует ее в функцию: &lt;tt class="docutils literal"&gt;lambda c: value.format(c)&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Результат кода аналогичен предыдущему.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;auth.user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test{0}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id16"&gt;Преимущества по сравнению с другими библиотеками&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; поддерживает &lt;strong&gt;python&lt;/strong&gt; второй и третьей версии.&lt;/p&gt;
&lt;p&gt;Библиотека обладает интеграцией с &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; ORM, &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; ORM, &lt;a class="reference external" href="http://mongoengine.org/"&gt;Mongoengine&lt;/a&gt;
ODM и легко расширяется для других бэкэндов. Независимо от проекта вы получаете
единый интерфейс для генерации данных.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; из коробки умеет генерировать не только случайные, но и фейковые данные,
с которыми приятно работать.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://mixer.readthedocs.org/"&gt;Mixer&lt;/a&gt; интуитивно понятен, гибок в настройке и прост.&lt;/p&gt;
&lt;p&gt;Надеюсь с данным модулем генерация данных станет для вас легче и приятнее.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="flask"></category><category term="sqlalchemy"></category><category term="django"></category><category term="orm"></category><category term="tests"></category><category term="mongoengine"></category></entry><entry><title>Ускорение python тестов. Конкурентный запуск.</title><link href="http://klen.github.io/concurrency-tests.html" rel="alternate"></link><published>2013-06-11T00:00:00+04:00</published><updated>2013-06-11T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2013-06-11:concurrency-tests.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#nose" id="id4"&gt;Nose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#unittest" id="id5"&gt;Unittest&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#django" id="id6"&gt;Django&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Прогон тестов на большом проекте отнимает продолжительное время. Простейший
способ ускорить выполнение на многоядерной системе использовать конкуренцию.&lt;/p&gt;
&lt;div class="section" id="nose"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id4"&gt;Nose&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Если вы используете &lt;a class="reference external" href="https://nose.readthedocs.org/en/latest/"&gt;nose&lt;/a&gt; для тестирования, то у вас уже есть возможность
конкурентного запуска.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;You can parallelize a test run across a configurable number of worker
processes. While this can speed up CPU-bound test runs, it is mainly useful
for IO-bound tests that spend most of their time waiting for data to arrive
from someplace else and can benefit from parallelization.&amp;quot;&lt;/p&gt;
&lt;p class="attribution"&gt;&amp;mdash;&lt;a class="reference external" href="https://nose.readthedocs.org/en/latest/"&gt;nose&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Обычный запуск тестов выглядит так:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ nosetests
&lt;/pre&gt;
&lt;p&gt;Для использования конкуренции:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ nosetests --processes=4
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="unittest"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id5"&gt;Unittest&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Все хорошо, но как ускорить тесты если вы не используете &lt;a class="reference external" href="https://nose.readthedocs.org/en/latest/"&gt;nose&lt;/a&gt;? Задавшись этим
вопросом я обнаружил модуль &lt;a class="reference external" href="https://github.com/cgoldberg/concurrencytest.git"&gt;concurrencytest&lt;/a&gt; от &lt;a class="reference external" href="http://goldb.org/"&gt;Corey Goldberg&lt;/a&gt;. Он позволяет
переопределить &lt;tt class="docutils literal"&gt;unittest.TestSuite&lt;/tt&gt; для использования конкурентного запуска
тестов.&lt;/p&gt;
&lt;p&gt;Базовый пример использования:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from concurrencytest import ConcurrentTestSuite, fork_for_tests

# Здесь мы определяем unittest.TestSuite и unittest.TestRunner
suite = ...

# И обеспечиваем конкурентный запуск
concurrent_suite = ConcurrentTestSuite(suite, fork_for_tests())
runner.run(concurrent_suite)
&lt;/pre&gt;
&lt;p&gt;Вот и все. Теперь тесты будут выполняться асинхронно.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;По умолчанию &lt;a class="reference external" href="https://github.com/cgoldberg/concurrencytest.git"&gt;concurrencytest&lt;/a&gt; запускает форки по количеству ядер
процессора вашей системы. Вы можете явно указать количество процессов
при создании ConcurrentTestSuite задав аргумент для функции для
fork_for_tests (fork_for_tests(8)).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="django"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id6"&gt;Django&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Приведу пример интеграции &lt;a class="reference external" href="https://github.com/cgoldberg/concurrencytest.git"&gt;concurrencytest&lt;/a&gt; в &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; проект. Нужно создать
собственный &lt;cite&gt;TestRunner&lt;/cite&gt; который переопределит создаваемую &lt;cite&gt;TestSuite&lt;/cite&gt;.
Пусть код проекта лежит в модуле &lt;cite&gt;main&lt;/cite&gt;, создадим файл &lt;cite&gt;main/test_runner.py&lt;/cite&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from concurrencytest import ConcurrentTestSuite, fork_for_tests
from django.test.simple import DjangoTestSuiteRunner


class DjangoConcurencyTestSuiteRunner(DjangoTestSuiteRunner):

    def build_suite(self, test_labels, extra_tests=None, **kwargs):
        suite = super(DjangoConcurencyTestSuiteRunner, self).build_suite(
            test_labels, extra_tests=extra_tests, **kwargs
        )

        concurrency_suite = ConcurrentTestSuite(suite, fork_for_tests())
        return concurrency_suite
&lt;/pre&gt;
&lt;p&gt;Теперь осталось переопределить опцию &lt;tt class="docutils literal"&gt;TEST_RUNNER&lt;/tt&gt; в настройках  &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt;,
указав путь импорта к нашему раннеру:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
TEST_RUNNER = 'main.test_runner.DjangoConcurencyTestSuiteRunner'
&lt;/pre&gt;
&lt;p&gt;Не забудьте добавить в ваше окружение зависимость от &lt;a class="reference external" href="https://github.com/cgoldberg/concurrencytest.git"&gt;concurrencytest&lt;/a&gt; и
обновить его.&lt;/p&gt;
&lt;p&gt;Все, теперь запуск Django тестов будет автоматически использовать
все присуствующие в системе ядра процессора. В моих проектах прирост скорости
выполнения тестов составил от 35% до 60% процентов, на 4-х ядерном macbook pro.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Кстати если вы используете &lt;a class="reference external" href="https://pypi.python.org/pypi/tox/"&gt;tox&lt;/a&gt; для прогона тестов используя разные
окружения, то для использования конкурентного запуска вам достаточно
просто установить модуль &lt;a class="reference external" href="https://pypi.python.org/pypi/detox/"&gt;detox&lt;/a&gt; и запускать tox-тесты с его помощью.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="test"></category><category term="django"></category></entry><entry><title>OSX - Программы которые я использую</title><link href="http://klen.github.io/mac.html" rel="alternate"></link><published>2013-04-04T00:00:00+04:00</published><updated>2013-04-04T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2013-04-04:mac.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id11"&gt;Джентльменский набор&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#xtrafinder" id="id12"&gt;XtraFinder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#xee" id="id13"&gt;Xee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#vlc" id="id14"&gt;VLC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#transmission" id="id15"&gt;Transmission&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#apptrap" id="id16"&gt;AppTrap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#deegres" id="id17"&gt;Deegres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#iterm2" id="id18"&gt;iTerm2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#face2face" id="id19"&gt;Face2Face&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id20"&gt;Заключение&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;img alt="" src="/static/img/osx.jpg" /&gt;
&lt;p&gt;Вот и настала моя очередь перейти на &lt;a class="reference external" href="http://www.apple.com/us/en/mac/"&gt;Mac&lt;/a&gt;. Уже некоторое время я использую
&lt;strong&gt;Macbook Pro Retina&lt;/strong&gt; и могу сказать, что доволен выбором на все 100%. Это
именно его крышку вы можете наблюдать на картинке сверху.&lt;/p&gt;
&lt;p&gt;Долгое время меня удерживали от перехода на технику Apple трудности установки
и настройки серверного окружения для разработки. Я использовал &lt;strong&gt;debian&lt;/strong&gt; системы
и поднимал на своем рабочем ноутбуке такую же среду, что и на сервере. Kонечно,
если постараться, многое можно настроить используя &lt;strong&gt;OSX&lt;/strong&gt;. Но далеко не все так же
просто. Потом я стал плотно работать с виртуализацией и гостевая система
стала не такой критичной. Возможно я напишу об этом в следующих
статьях, но в этой мне захотелось описать используемое мной программное
обеспечение.&lt;/p&gt;
&lt;p&gt;И так мой «&lt;em&gt;must have&lt;/em&gt;» лист. Все программы и плагины представленные ниже,
бесплатны на момент написания этого текста.&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Джентльменский набор&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Программное обеспечение которое в описании не нуждается: &lt;a class="reference external" href="http://skype.com"&gt;Skype&lt;/a&gt;, &lt;a class="reference external" href="https://www.google.com/intl/en/chrome/browser/"&gt;Chrome&lt;/a&gt;,
&lt;a class="reference external" href="http://dropbox.com"&gt;Dropbox&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ну и для &lt;strong&gt;OSX&lt;/strong&gt; включу сюда же &lt;a class="reference external" href="http://adium.im/"&gt;Adium&lt;/a&gt; и &lt;a class="reference external" href="http://mxcl.github.com/homebrew/"&gt;Homebrew&lt;/a&gt; (для разработчиков).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="xtrafinder"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id12"&gt;XtraFinder&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/xtra.jpg" /&gt;
&lt;p&gt;После использования &lt;cite&gt;nautilus&lt;/cite&gt; мне не хватало нескольких вещей в стандартном файловом
менеджере &lt;strong&gt;OSX&lt;/strong&gt; — &lt;cite&gt;Finder&lt;/cite&gt;. В основном это касалось панельного режима.&lt;/p&gt;
&lt;p&gt;Проблему решил бесплатный плагин &lt;a class="reference external" href="http://www.trankynam.com/xtrafinder/"&gt;Xtrafinder&lt;/a&gt;. Вкладки, панели, удобные кнопки,
быстрое переключение отображения скрытых файлов и многое другое. Платные
аналоги стоят до $50 и уступают в функциональности.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="xee"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id13"&gt;Xee&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/xee.jpg" /&gt;
&lt;p&gt;Не смог я привыкнуть к стандартному &lt;cite&gt;Preview&lt;/cite&gt; для просмотра картинок. Нет опции
запоминания увеличения, достаточно спорное решение с листанием. OpenSource
просмотрщик &lt;a class="reference external" href="http://wakaba.c3.cx/s/apps/xee.html"&gt;Xee&lt;/a&gt; пришелся по душе гораздо больше.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="vlc"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id14"&gt;VLC&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/vlc.jpg" /&gt;
&lt;p&gt;Удобный кроссплатформенный медиа-плеер. Практически комбайн, хотя я использую
его исключительно для просмотра видео. Стандартный &lt;cite&gt;Quicktime&lt;/cite&gt; не соперник для &lt;a class="reference external" href="http://wakaba.c3.cx/s/apps/xee.html"&gt;VLC&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="transmission"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id15"&gt;Transmission&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/transmission.jpg" /&gt;
&lt;p&gt;Торрент-качалка. Возможно и не лучший выбор, но опять же присутствует на всех
платформах, что избавляет от модальности при переключении на другие &lt;strong&gt;OS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Интерфейс &lt;a class="reference internal" href="#transmission"&gt;Transmission&lt;/a&gt; не перегружен и оставляет приятное впечатление.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="apptrap"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id16"&gt;AppTrap&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/apptrap1small.png" /&gt;
&lt;p&gt;В 2013 году при удалении программ на &lt;strong&gt;OSX&lt;/strong&gt; стандартными инструментами, в системе
остаются неиспользуемые служебные файлы, обнаружить и очистить которые —
задача нетривиальная. &lt;a class="reference external" href="http://onnati.net/apptrap/"&gt;Apptrap&lt;/a&gt; — бесплатное решение для надежного удаления программ.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="deegres"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id17"&gt;Deegres&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/deegres.jpg" /&gt;
&lt;p&gt;Бесплатный виджет для меню-бара отображающий текущую погоду. В том или ином виде
использую подобные решения на всех платформах. &lt;a class="reference external" href="https://itunes.apple.com/us/app/degrees/id430173763?mt=12"&gt;Deegres&lt;/a&gt; можно найти в &lt;strong&gt;AppStore&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="iterm2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id18"&gt;iTerm2&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/iterm.jpg" /&gt;
&lt;p&gt;Использую &lt;a class="reference external" href="http://www.iterm2.com/"&gt;iTerm2&lt;/a&gt; вместо стандартного терминала &lt;strong&gt;OSX&lt;/strong&gt;. Вкладки, темы,
полезные хоткеи, удобная работа с клипбордом, панельный режим.&lt;/p&gt;
&lt;p&gt;Считаю его удобнее чем &lt;cite&gt;TotalTerminal&lt;/cite&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="face2face"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id19"&gt;Face2Face&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/mail.jpg" /&gt;
&lt;p&gt;&lt;strong&gt;Mail&lt;/strong&gt; — стандартное приложение электронной почты в &lt;strong&gt;OSX&lt;/strong&gt;. Для него
существует немалое количество плагинов.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.chungwasoft.com/face2face/"&gt;Face2Face&lt;/a&gt; ищет изображения отправителей писем используя такие сервисы
как &lt;a class="reference external" href="http://gravatar.com"&gt;Gravatar&lt;/a&gt;, &lt;a class="reference external" href="http://facebook.com"&gt;Facebook&lt;/a&gt; и прочие.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id20"&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Надеюсь вы нашли для себя что-нибудь полезное в этом списке.&lt;/p&gt;
&lt;p&gt;PS: Разумеется здесь упомянуты не все используемые мной программы. Например
я предпочитаю &lt;a class="reference external" href="http://picasa.google.com/"&gt;Picasa&lt;/a&gt; стандартному &lt;a class="reference external" href="http://www.apple.com/ilife/iphoto/"&gt;iPhoto&lt;/a&gt;. Из платных программ выделил бы:
&lt;a class="reference external" href="https://agilebits.com/onepassword"&gt;1Password&lt;/a&gt;, &lt;a class="reference external" href="http://macpaw.com/cleanmymac"&gt;CleanMyMac&lt;/a&gt;, &lt;a class="reference external" href="http://macbookreader.com/"&gt;Bookreader&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;В заключении хочу спросить пользователей &lt;strong&gt;OSX&lt;/strong&gt;: часто ли и в каких целях вы
используете &lt;a class="reference external" href="http://support.apple.com/kb/ht2492"&gt;Dashboard&lt;/a&gt;? Все мои знакомые в лучшем случае держат там погоду и
калькулятор, и практически не открывают его. После пары недель использования я
осознал его бесполезность и полностью удалил из системы:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
defaults write com.apple.dashboard mcx-disabled -boolean true
killall Dock
defaults write com.apple.dashboard mcx-disabled -boolean false
&lt;/pre&gt;
&lt;/div&gt;
</summary><category term="mac"></category><category term="osx"></category><category term="software"></category></entry><entry><title>Pylama — аудит python-кода</title><link href="http://klen.github.io/pylama.html" rel="alternate"></link><published>2012-09-02T00:00:00+04:00</published><updated>2012-09-02T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-09-02:pylama.html</id><summary type="html">&lt;p&gt;Процесс &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%BF%D1%80%D0%B5%D1%80%D1%8B%D0%B2%D0%BD%D0%B0%D1%8F_%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D1%8F"&gt;непрерывной интеграции&lt;/a&gt; программных продуктов всегда был очень
важен для разработке. В моем случае я не ограничеваюсь только тестированием
(юнит или интеграционным), но также провожу и интроспекцию кода на соответствие
стандартам или например ограничение логической вложенности.&lt;/p&gt;
&lt;p&gt;Во-втором случае код моих проектов автоматически прогоняется через ряд
утилит: &lt;strong&gt;pyflakes&lt;/strong&gt;, &lt;strong&gt;pep8&lt;/strong&gt;, &lt;strong&gt;mccabe&lt;/strong&gt;, &lt;strong&gt;pylint&lt;/strong&gt;. Какое то время
я использовал &lt;a class="reference external" href="http://pypi.python.org/pypi/flake8/"&gt;Flake8&lt;/a&gt; сочетающую в себе все эти линтеры. Но потом его
возможностей мне стало не хватать. Схожий функционал уже был мной разработан
для &lt;a class="reference external" href="https://github.com/klen/python-mode"&gt;python-mode&lt;/a&gt;, поэтому я вынес его в отдельный модуль.
Так появился &lt;a class="reference external" href="http://pypi.python.org/pypi/pylama/"&gt;Pylama&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://pypi.python.org/pypi/pylama/"&gt;Pylama&lt;/a&gt; это утилита для проверки python-кода схожая с &lt;a class="reference external" href="http://pypi.python.org/pypi/flake8/"&gt;Flake8&lt;/a&gt;, но обладающая
рядом улучшений.&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;Установка&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ pip install pylama
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;Использование&lt;/h2&gt;
&lt;p&gt;Рекурсивная проверка текущей диектории:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ pylama
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Игнорировать определенные типы (группы типов) ошибок:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ pylama -i W,E501
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Изменение набора линтеров:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ pylama -l &lt;span class="s2"&gt;&amp;quot;pep8,mccabe&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;Опции&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ pylama --help

usage: pylama &lt;span class="o"&gt;[&lt;/span&gt;-h&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--ignore IGNORE&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--verbose&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--select SELECT&lt;span class="o"&gt;]&lt;/span&gt;
            &lt;span class="o"&gt;[&lt;/span&gt;--linters LINTERS&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--complexity COMPLEXITY&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--skip SKIP&lt;span class="o"&gt;]&lt;/span&gt;
            &lt;span class="o"&gt;[&lt;/span&gt;path&lt;span class="o"&gt;]&lt;/span&gt;

Code audit tool &lt;span class="k"&gt;for&lt;/span&gt; python.

positional arguments:
path                  Path on file or directory.

optional arguments:
-h, --help            show this &lt;span class="nb"&gt;help&lt;/span&gt; message and &lt;span class="nb"&gt;exit&lt;/span&gt;
--ignore IGNORE, -i IGNORE
                        Ignore errors and warnings.
--verbose, -v         Verbose mode.
--select SELECT, -s SELECT
                        Select errors and warnings.
--linters LINTERS, -l LINTERS
                        Select errors and warnings.
--complexity COMPLEXITY, -c COMPLEXITY
                        Set mccabe complexity.
--skip SKIP           Skip files &lt;span class="o"&gt;(&lt;/span&gt;Ex. messages.py&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="pylama"&gt;
&lt;h2&gt;Переключение настроек pylama из файла&lt;/h2&gt;
&lt;p&gt;В любом python-файле вы можете установить опции проверки для pylama.
Они будут установлены только для данного файла.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="err"&gt;Где&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;то&lt;/span&gt; &lt;span class="err"&gt;в&lt;/span&gt; &lt;span class="err"&gt;коде&lt;/span&gt;
&lt;span class="c1"&gt;# Игнорировать все Warnings кроме W301&lt;/span&gt;
&lt;span class="c1"&gt;# lint_ignore=W:lint_select=W301&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Для выключения проверки текущего файла:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# lint=0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Разработка &lt;a class="reference external" href="http://pypi.python.org/pypi/pylama/"&gt;pylama&lt;/a&gt; ведется на Github: &lt;a class="reference external" href="http://github.com/klen/pylama"&gt;http://github.com/klen/pylama&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="pep8"></category><category term="pyflakes"></category><category term="mccabe"></category></entry><entry><title>Pyradio — простой консольный радиоплеер</title><link href="http://klen.github.io/pyradio.html" rel="alternate"></link><published>2012-09-01T00:00:00+04:00</published><updated>2012-09-01T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-09-01:pyradio.html</id><summary type="html">&lt;img alt="" src="/static/img/pyradio.png" /&gt;
&lt;p&gt;Очень простой клиент для прослушивания онлайн-радиостанций. Ничего лишнего.&lt;/p&gt;
&lt;p&gt;Автор оригинального проекта, прекратил его поддержку длительное время назад
и пока не выходит на связь.&lt;/p&gt;
&lt;p&gt;Я исправил ошибки, доработал консольный клиент и собрал пакет для PYPI.
Теперь можно просто ввести:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo pip install pyradio
pyradio -p 15
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;На самом деле вам еще понадобиться установленный mplayer.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;И слушать музыку.&lt;/p&gt;
</summary><category term="python"></category><category term="radio"></category></entry><entry><title>Неделя с Flask</title><link href="http://klen.github.io/some-flask-things.html" rel="alternate"></link><published>2012-08-31T00:00:00+04:00</published><updated>2012-08-31T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-08-31:some-flask-things.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#flask-foundation" id="id5"&gt;Flask-Foundation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#flask-mixer" id="id6"&gt;Flask-Mixer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#flask-collect" id="id7"&gt;Flask-Collect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id8"&gt;Заключение&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Стал использовать &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; для небольших проектов. &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; неимоверно разрослась,
да и просто замылила глаз. В процессе работы родилось несколько полезных приложений.&lt;/p&gt;
&lt;div class="section" id="flask-foundation"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id5"&gt;Flask-Foundation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Я &lt;a class="reference external" href="../notes-flask-opensource-ru.html"&gt;уже писал&lt;/a&gt; про open-source проекты на &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; с хорошим исходным кодом.
Но как показывает практика, для быстрого старта их недостаточно.&lt;/p&gt;
&lt;p&gt;Я собрал свою сборку: &lt;a class="reference external" href="http://github.com/klen/Flask-Foundation"&gt;http://github.com/klen/Flask-Foundation&lt;/a&gt; базового
проекта на &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;, содержащую следующие вещи:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/mrjoes/flask-admin/"&gt;Flask-Admin&lt;/a&gt; — администрирование;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/rduplain/flask-script"&gt;Flask-Script&lt;/a&gt; — автоматизация;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://packages.python.org/Flask-Cache/"&gt;Flask-Cache&lt;/a&gt; — кеширование;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://flask.pocoo.org/docs/extensiondev/"&gt;Flask-Debugtoolbar&lt;/a&gt; — отладка;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/mitsuhiko/flask-babel"&gt;Flask-Babel&lt;/a&gt; — локализация;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/rduplan/flask-wtf"&gt;Flask-WTF&lt;/a&gt; — поддержка WTForms;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/mbr/flask-bootstrap"&gt;Flask-Bootstrap&lt;/a&gt; — стилизация;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://packages.python.org/Flask-Mail/"&gt;Flask-Mail&lt;/a&gt; — почта;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/mitsuhiko/flask-sqlalchemy"&gt;Flask-SQLAlchemy&lt;/a&gt; — ORM, базы данных;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://packages.python.org/Flask-Testing/"&gt;Flask-Testing&lt;/a&gt; — тестирование;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://pypi.python.org/pypi/alembic/0.3.5"&gt;Alembic&lt;/a&gt; — миграции бд;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Проект содержит богатый функционал и может послужить хорошим примером для быстрого старта.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="flask-mixer"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id6"&gt;Flask-Mixer&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Фабрика генерации объектов из &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; моделей. Создавалась мной в целях тестирования,
но может использоваться и в других случаях (пакетная генерация данных).&lt;/p&gt;
&lt;p&gt;Адрес на &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt;: &lt;a class="reference external" href="http://github.com/klen/Flask-Mixer"&gt;http://github.com/klen/Flask-Mixer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Примеры работы:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Простая генерация модели&lt;/span&gt;
&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;user1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;user1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;

&lt;span class="c1"&gt;# Модель может быть задана строкой&lt;/span&gt;
&lt;span class="n"&gt;role1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;app.models.Role&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;role1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;role1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;role1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="c1"&gt;# Генерация объекта с предопределенными значениями&lt;/span&gt;
&lt;span class="n"&gt;user2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Это могут быть значения для референс-объектов&lt;/span&gt;
&lt;span class="n"&gt;role1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user__username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;role2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;test2&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Значение может быть функцией&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;callable_value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;callable_value&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Принудительная установка случайного значения для поля с default value&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;

&lt;span class="c1"&gt;# Установка случайного значения из базы данных&lt;/span&gt;
&lt;span class="n"&gt;profiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;profiles&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="flask-collect"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Flask-Collect&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Нехватку такого приложения я почувствовал при первом же деплое &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;-проекта.
Как известно раздавать статичные файлы (css, js и тп) питоном в нашем
бизнесе не комильфо. Обычно проксирующий WSGI веб-сервер отдает их
из определенной папки напрямую. Для сборки таких файлов из приложений проекта
в &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; существует встроенное приложение.&lt;/p&gt;
&lt;p&gt;Во &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; ничего подобного не нашлось. Есть инструкции в документации где
рекомендовано отдавать статичные файлы веб-сервером, но нет ни слова о том
как их собирать. Не найдя ничего на Pypi я написал свое.&lt;/p&gt;
&lt;p&gt;Использование:&lt;/p&gt;
&lt;p&gt;В коде:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;collect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;или из консоли при использовании &lt;a class="reference external" href="http://github.com/rduplain/flask-script"&gt;Flask-Script&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ./manage.py collect
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Адрес на &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt;: &lt;a class="reference external" href="http://github.com/klen/Flask-Collect"&gt;http://github.com/klen/Flask-Collect&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;В целом &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; мне нравится. Код чище, его меньше, работает быстрее.
&lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; на голову уделывает на текущий момент &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; ORM.&lt;/p&gt;
&lt;p&gt;Но порог вхождения гораздо выше, как ни странно, множество задач уже
решенных сообществом &lt;a class="reference external" href="http://djangoproject.org/"&gt;Django&lt;/a&gt; тут приходится решать заново.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="flask"></category><category term="sqlalchemy"></category></entry><entry><title>Полезности для GIT</title><link href="http://klen.github.io/some-git-things.html" rel="alternate"></link><published>2012-07-19T00:00:00+04:00</published><updated>2012-07-19T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-07-19:some-git-things.html</id><summary type="html">&lt;p&gt;Алиас для более компактного &lt;cite&gt;git status&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global alias.st &lt;span class="s1"&gt;&amp;#39;status -sb&amp;#39;&lt;/span&gt;
$ git st
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Алиас для редактирования последнего коммита:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global alias.amend &lt;span class="s1"&gt;&amp;#39;commit --amend -C HEAD&amp;#39;&lt;/span&gt;
$ git amend
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Алиас для отмены последнего коммита:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global alias.undo &lt;span class="s1"&gt;&amp;#39;reset --soft HEAD^&amp;#39;&lt;/span&gt;
$ git undo
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Предпочитаемый мной вывод &lt;cite&gt;diff&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global alias.diff &lt;span class="s1"&gt;&amp;#39;diff --word-diff&amp;#39;&lt;/span&gt;
$ git undo
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;«Визуальная» история веток в консоли:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global alias.hist &lt;span class="s2"&gt;&amp;quot;log --pretty=format:&amp;#39;%Cred%h%Creset %ad | %Cgreen%s%d%Creset [%an]&amp;#39; --graph --date=short&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Удобная работа с &lt;a class="reference external" href="https://github.com/nvie/gitflow/"&gt;Git-Flow&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global alias.hotfix &lt;span class="s1"&gt;&amp;#39;flow hotfix&amp;#39;&lt;/span&gt;
$ git config --global alias.feature &lt;span class="s1"&gt;&amp;#39;flow feature&amp;#39;&lt;/span&gt;
$ git config --global alias.release &lt;span class="s1"&gt;&amp;#39;flow release&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Автоматическая коррекция опечаток во вводимых командах:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global help.autocorrect 1
$ git comit
$ git bronch
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Автоматическая запись разрешенных мердж конфликтов, для будущего использования:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git config --global rerere.enabled 1
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Получение веток с не влитыми данными:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git branch --no-merged
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Список коммитов в ветке &lt;em&gt;branchA&lt;/em&gt; отсуствующих в ветке &lt;em&gt;branchB&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git log branchA ^branchB
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Многие используют баш функцию &lt;cite&gt;__git_ps1&lt;/cite&gt; для вывода названия текущей ветки,
но не все знают, что ее можно сделать более информативной.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# In your .bashrc or .profile:&lt;/span&gt;

    &lt;span class="nv"&gt;GIT_PS1_SHOWDIRTYSTATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
    &lt;span class="nv"&gt;GIT_PS1_SHOWSTASHSTATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

    $ &lt;span class="o"&gt;(&lt;/span&gt;develop *$&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/pre&gt;&lt;/div&gt;
</summary><category term="git"></category></entry><entry><title>PyServe. Веб-сервер одной командой</title><link href="http://klen.github.io/pyserve.html" rel="alternate"></link><published>2012-06-14T00:00:00+04:00</published><updated>2012-06-14T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-06-14:pyserve.html</id><summary type="html">&lt;p&gt;Иногда возникает необходимость быстро запустить веб-сервер
нацеленный на содержимое локальной директории.&lt;/p&gt;
&lt;p&gt;При установленном python это делается буквально в одну строку.&lt;/p&gt;
&lt;p&gt;Для python версий 2.*:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python -m SimpleHTTPServer
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Для python версий 3.*:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python -m http.server
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Я часто пользовался этой функциональностью, например при отладке standalone HTML
приложений работающих с удаленными API. В какой то момент мне захотелось большего:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Одинаковый синтаксис для python 2 и 3 версий;&lt;/li&gt;
&lt;li&gt;Возможность отключения &amp;quot;автоиндекса&amp;quot; для директорий (игнорирование index.html);&lt;/li&gt;
&lt;li&gt;Возможность указания IP-адреса сервера (шаринг в сети);&lt;/li&gt;
&lt;li&gt;Возможность скрытия системных файлов;&lt;/li&gt;
&lt;li&gt;Более приятный внешний вид при выводе каталога директории;&lt;/li&gt;
&lt;li&gt;Возможность указания пути к директории (не только текущей);&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Так появился пакет &lt;a class="reference external" href="https://github.com/klen/pyserve"&gt;PyServe&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="" src="/static/img/pyserve.png" /&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ pip install pyserve
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Пакет не содержит зависимостей, поддерживает всю вышеперечисленную функциональность и прост
в установке. Теперь независимо от версии python вы можете запустить сервер одной командой:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ serve
&lt;/pre&gt;&lt;/div&gt;
</summary><category term="python"></category><category term="http"></category></entry><entry><title>Создание python-пакетов (aka setup scripts)</title><link href="http://klen.github.io/create-python-packages.html" rel="alternate"></link><published>2012-06-13T00:00:00+04:00</published><updated>2012-06-13T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-06-13:create-python-packages.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id8"&gt;Создаем структуру проекта&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#setup-py" id="id9"&gt;Редактируем мета-информацию (setup.py)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id10"&gt;Виртуальное окружение&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id11"&gt;Создание команд&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id12"&gt;Работа с версиями&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id6" id="id13"&gt;Управление зависимостями&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#manifest-in" id="id14"&gt;Управление файлами проекта (MANIFEST.in)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id7" id="id15"&gt;Создание и запуск тестов&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#pypi-python-org" id="id16"&gt;Публикация пакета на pypi.python.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Одна из действительно полезных вещей в python — это система скриптов установки. Любой, серьезно
увлекающийся python-программированием разработчик рано или поздно сталкивается с ней.
Но из-за гибкости инструментария скриптов установки, их документация весьма раздута.
На текущий момент имеется набор утилит (&lt;a class="reference external" href="http://pypi.python.org/pypi/setuptools"&gt;setuptools&lt;/a&gt;, &lt;a class="reference external" href="http://docs.python.org/distutils/"&gt;distutils&lt;/a&gt;, &lt;a class="reference external" href="http://pypi.python.org/pypi/distribute"&gt;distribute&lt;/a&gt;) выполняющих
одинаковые задачи.&lt;/p&gt;
&lt;img alt="" src="/static/img/state_of_packaging.jpg" /&gt;
&lt;p&gt;В данной статье я на конкретных примерах покажу как создать и настроить простой python-пакет.&lt;/p&gt;
&lt;p&gt;Наш проект будет иметь следующую функциональность:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Метод возвращающий строку: &amp;quot;Hello World!&amp;quot;;&lt;/li&gt;
&lt;li&gt;Команда &lt;cite&gt;helloworld&lt;/cite&gt; печатающая эту строку в стандартный вывод.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Исходные коды для данной статьи можно увидеть по адресу: &lt;a class="reference external" href="https://github.com/klen/klen.github.com/tree/master/_code/helloworld-project"&gt;https://github.com/klen/klen.github.com/tree/master/_code/helloworld-project&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Создаем структуру проекта&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для начала создадим директорию для пакета. Ее минимальный
набор файлов состоит из: файла дистрибьюции (&lt;cite&gt;setup.py&lt;/cite&gt;) описывающего
метаданные и python кода проекта (в нашем случае модуля &lt;tt class="docutils literal"&gt;helloworld&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;Также, xорошим тоном считается создание в корне директории файла с описанием проекта: &lt;cite&gt;README.txt&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;Получаем следующую структуру:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
helloworld-project
├── helloworld
│   ├── __init__.py
│   └── core.py
├── setup.py
└── README.txt
&lt;/pre&gt;
&lt;p&gt;Наша корневая директория &lt;cite&gt;helloworld-project&lt;/cite&gt; будет содержать мета-данные пакета и вспомогательные файлы
(тесты, лицензию, документацию и т.д.), а поддиректория &lt;cite&gt;helloworld&lt;/cite&gt; непосредственно сам модуль &lt;cite&gt;helloworld&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;Теперь отредактируем файл: &lt;cite&gt;helloworld/core.py&lt;/cite&gt; и добавим логику нашего приложения (получение и вывод строки &amp;quot;Hello World!&amp;quot;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_message&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello World!&amp;quot;&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_message&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;get_message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="setup-py"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Редактируем мета-информацию (setup.py)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Заполним файл описания &lt;cite&gt;README.rst&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gh"&gt;Description&lt;/span&gt;
&lt;span class="gh"&gt;===========&lt;/span&gt;

An example Hello World project.
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь отредактируем файл &lt;cite&gt;setup.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;setuptools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;find_packages&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;os.path&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirname&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;helloworld&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;find_packages&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;long_description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;README.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Убедитесь, что в вашей системе доступны &lt;a class="reference external" href="http://pypi.python.org/pypi/setuptools"&gt;setuptools&lt;/a&gt;, в противном
случае установите python-пакет &lt;a class="reference external" href="http://pypi.python.org/pypi/distribute"&gt;distribute&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Этих операций достаточно, чтобы собрать пакет дистрибьюции. Выполните команду
сборки:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python setup.py sdist
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В случае успеха вы получите файл: &lt;cite&gt;dist/helloworld-1.0.tar.gz&lt;/cite&gt;. Это полноценный,
архивированный python-пакет и вы можете распространять его среди прочих разработчиков.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Виртуальное окружение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv/"&gt;Virtualenv&lt;/a&gt; — пакет применяемый для создания изолированного python-окружения. Используем
его для тестирования нашего проекта.&lt;/p&gt;
&lt;p&gt;Создадим окружение &lt;tt class="docutils literal"&gt;env&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ virtualenv env
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Команда создаст директорию &lt;tt class="docutils literal"&gt;env&lt;/tt&gt; внутри нашего проекта и установит туда &lt;tt class="docutils literal"&gt;python&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;pip&lt;/tt&gt; и &lt;tt class="docutils literal"&gt;distribute&lt;/tt&gt;.
Произведем в него установку нашего проекта.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ./env/bin/python setup.py install
running install
running bdist_egg
running egg_info
&lt;span class="o"&gt;[&lt;/span&gt;...&lt;span class="o"&gt;]&lt;/span&gt;
Processing dependencies &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;helloworld&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0
Finished processing dependencies &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;helloworld&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И протестируем его работоспособность:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ./env/bin/python
&amp;gt;&amp;gt;&amp;gt; import helloworld.core as hw
&amp;gt;&amp;gt;&amp;gt; hw.get_message&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;Hello World!&amp;#39;&lt;/span&gt;
&amp;gt;&amp;gt;&amp;gt; hw.print_message&lt;span class="o"&gt;()&lt;/span&gt;
Hello World!
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Все работает. Осталось добавить поддержку команды &lt;tt class="docutils literal"&gt;helloworld&lt;/tt&gt; в консоли.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Создание команд&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для создания команды &lt;tt class="docutils literal"&gt;helloworld&lt;/tt&gt; изменим файл &lt;cite&gt;setup.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;entry_points&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;console_scripts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;helloworld = helloworld.core:print_message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В параметре &lt;tt class="docutils literal"&gt;entry_points&lt;/tt&gt; мы задаем словарь с &amp;quot;точками вызова&amp;quot; нашего приложения. Ключ &lt;tt class="docutils literal"&gt;console_scripts&lt;/tt&gt;
задает список создаваемых исполняемых скриптов (в Windows это будут exe-файлы). В данном случае
мы указали создание исполняемого скрипта &lt;tt class="docutils literal"&gt;helloworld&lt;/tt&gt; при вызове которого будет запускаться метод &lt;tt class="docutils literal"&gt;print_message&lt;/tt&gt;
из модуля &lt;tt class="docutils literal"&gt;helloworld.core&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Переустановим модуль в наше окружение и проверим работу созданного скрипта (для этого прийдется активировать наше окружение):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ./env/bin/python setup.py install
$ &lt;span class="nb"&gt;source&lt;/span&gt; ./env/bin/activate
&lt;span class="o"&gt;(&lt;/span&gt;env&lt;span class="o"&gt;)&lt;/span&gt;
$ helloworld
Hello World!
&lt;span class="o"&gt;(&lt;/span&gt;env&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Похоже все работает.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id12"&gt;Работа с версиями&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Номер версии важная часть любого проекта. От него зависит обновление пакетов
и разрешение зависимостей. В примере выше мы указали номер версии &lt;tt class="docutils literal"&gt;1.0&lt;/tt&gt; в файле &lt;cite&gt;setup.py&lt;/cite&gt;.
Более правильное решение перенести его в файл &lt;cite&gt;helloworld/__init__.py&lt;/cite&gt; чтобы сделать доступным
в python-коде. По существующим соглашения для хранения номера версии в модуле, используется
переменная &lt;tt class="docutils literal"&gt;__version__&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;helloworld/__init__.py`&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Изменим файл &lt;cite&gt;setup.py&lt;/cite&gt;, чтобы нам не приходилось редактировать номер версии в двух местах:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;helloworld&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;helloworld&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;helloworld&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Существует множество систем наименования версий в python обычно рекомендуется использовать &lt;a class="reference external" href="http://www.python.org/dev/peps/pep-0386/"&gt;PEP386&lt;/a&gt;.
Можно представить, что обозначение версии состоит из номера мажорного, минорного релизов
(номера багфикса при необходимости), разделенных точками. В последней части версии
разрешается использовать буквы латинского алфавита. Примеры из официальной документации:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
0.4       0.4.0  (these two are equivalent)
0.4.1
0.5a1
0.5b3
0.5
0.9.6
1.0
1.0.4a3
1.0.4b1
1.0.4
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id13"&gt;Управление зависимостями&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Добавим функциональности нашему проекту. Создадим команду &lt;tt class="docutils literal"&gt;serve&lt;/tt&gt; которая будет запускать
вебсервер отдающий страницу со строкой &amp;quot;Hello world!&amp;quot; генерируемой нашим модулем. Для этого
воспользуемся пакетом &lt;a class="reference external" href="http://pypi.python.org/pypi/Flask/0.8"&gt;Flask&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Добавляем файл &lt;cite&gt;helloworld/web.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;helloworld.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_message&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;index.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;get_message&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_server&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И файл &lt;cite&gt;helloworld/templates/index.html&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE HTML&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{message}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И опишем команду &lt;tt class="docutils literal"&gt;serve&lt;/tt&gt; в файле &lt;cite&gt;setup.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;entry_points&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s1"&gt;&amp;#39;console_scripts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;helloworld = helloworld.core:print_message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;&amp;#39;serve = helloworld.web:run_server&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь в нашем проекте появилась зависимость от пакета &lt;a class="reference external" href="http://pypi.python.org/pypi/Flask/0.8"&gt;Flask&lt;/a&gt;. Без его установки наше приложение
не будет правильно работать. За описание зависимостей в файле &lt;cite&gt;setup.py&lt;/cite&gt; отвечает параметр &lt;tt class="docutils literal"&gt;install_requires&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;install_requires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Flask==0.8&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Проверим установку зависимостей обновив наш пакет и работу команды &lt;tt class="docutils literal"&gt;serve&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ./env/bin/python setup.py develop
...
Processing dependencies &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;helloworld&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.1
Searching &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;Flask&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.8
...
$ serve
   * Running on http://127.0.0.1:5000/
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Открыв браузер по адресу &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;http://127.0.0.1:5000&lt;/span&gt;&lt;/tt&gt; вы должны увидеть нашу страницу.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="manifest-in"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id14"&gt;Управление файлами проекта (MANIFEST.in)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;На текущий момент при сборке нашего пакета &lt;tt class="docutils literal"&gt;distutils&lt;/tt&gt; включает в него только python-файлы.
Необходимо включить в него файл &lt;cite&gt;helloworld/templates/index.html&lt;/cite&gt; без которого проект работать не будет.&lt;/p&gt;
&lt;p&gt;Чтобы сделать это мы должны сообщить &lt;tt class="docutils literal"&gt;distutils&lt;/tt&gt; какие еще файлы надо включать в наш проект. Один из способов —
это создание файла &lt;cite&gt;MANIFEST.in&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;recursive-include helloworld/templates *.html
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Данная команда указывает &lt;tt class="docutils literal"&gt;distutils&lt;/tt&gt; на включение в проект &lt;em&gt;всех&lt;/em&gt; html файлов в директории &lt;cite&gt;helloworld/templates&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;Также придется обновить &lt;cite&gt;setup.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;include_package_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь шаблоны будут включены в наш проект.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id15"&gt;Создание и запуск тестов&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Хорошей практикой считается создание тестов для вашего проекта.
Добавим простейшую реализацию, файл &lt;cite&gt;tests.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;helloworld.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_message&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloworldTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_helloworld&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_message&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Hello World!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И обновим &lt;cite&gt;setup.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;test_suite&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tests&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь мы можем произвести предварительное тестирование нашего проекта:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python setup.py &lt;span class="nb"&gt;test&lt;/span&gt;
running &lt;span class="nb"&gt;test&lt;/span&gt;
running egg_info
writing requirements to helloworld.egg-info/requires.txt
writing helloworld.egg-info/PKG-INFO
writing top-level names to helloworld.egg-info/top_level.txt
writing dependency_links to helloworld.egg-info/dependency_links.txt
writing entry points to helloworld.egg-info/entry_points.txt
reading manifest file &lt;span class="s1"&gt;&amp;#39;helloworld.egg-info/SOURCES.txt&amp;#39;&lt;/span&gt;
reading manifest template &lt;span class="s1"&gt;&amp;#39;MANIFEST.in&amp;#39;&lt;/span&gt;
writing manifest file &lt;span class="s1"&gt;&amp;#39;helloworld.egg-info/SOURCES.txt&amp;#39;&lt;/span&gt;
running build_ext
test_helloworld &lt;span class="o"&gt;(&lt;/span&gt;tests.HelloworldTestCase&lt;span class="o"&gt;)&lt;/span&gt; ... ok

----------------------------------------------------------------------
Ran &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; in 0.000s

OK
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Обратите внимание, что для запуска тестов даже не нужно создание виртуального окружения. Необходимые
зависимости будут скачаны в директорию проекта в виде &lt;tt class="docutils literal"&gt;egg&lt;/tt&gt; пакетов.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pypi-python-org"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id16"&gt;Публикация пакета на pypi.python.org&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Прежде чем вы сможете опубликовать свой проект вам необходимо зарегистрироваться на &lt;a class="reference external" href="http://pypi.python.org"&gt;PyPi&lt;/a&gt;.
Запишите ваши реквизиты в файле &lt;cite&gt;~/.pypirc&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;distutils&lt;span class="o"&gt;]&lt;/span&gt;
index-servers &lt;span class="o"&gt;=&lt;/span&gt;
    pypi

&lt;span class="o"&gt;[&lt;/span&gt;pypi&lt;span class="o"&gt;]&lt;/span&gt;
username:&amp;lt;username&amp;gt;
password:&amp;lt;password&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Все ваш проект готов к публикации. Достаточно ввести соответствующую команду:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python setup.py register sdist upload
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Вы не сможете опубликовать пакет &lt;cite&gt;helloworld&lt;/cite&gt;, тк данное имя проекта уже занято.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="setuptools"></category><category term="distutils"></category><category term="distribute"></category></entry><entry><title>Серверная реализация Android C2DM Push уведомлений (python)</title><link href="http://klen.github.io/android-push-python.html" rel="alternate"></link><published>2012-04-03T00:00:00+04:00</published><updated>2012-04-03T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-04-03:android-push-python.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id7"&gt;Введение&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id8"&gt;Подготовка к работе&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id9"&gt;Получение токена для сервера&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#push" id="id10"&gt;Отправка Push уведомлений&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#django" id="id11"&gt;Интеграция с Django&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;&lt;strong&gt;Статьи по теме&lt;/strong&gt;&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="../urbanairship-ru.html"&gt;Urbanairship, push-уведомления для мобильных приложений&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="../ru-ios-push-python.html"&gt;IOS Push Уведомления&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Настало время рассказать, как реализовать серверную поддержку &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; уведомлений для &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Android"&gt;Android&lt;/a&gt;. Эта возможность появилась
в мобильной системе начиная с версии &lt;strong&gt;2.2&lt;/strong&gt; (API8). Ниже я рассмотрю реализацию на &lt;a class="reference external" href="http://python.org"&gt;Python&lt;/a&gt;, но для программиста владеющего
другими языками, не должно составить труда справиться самостоятельно.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Исходные коды для данной статьи можно увидеть по адресу: &lt;a class="reference external" href="https://github.com/klen/klen.github.com/tree/master/_code/android-push"&gt;https://github.com/klen/klen.github.com/tree/master/_code/android-push&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/android-push1.png" /&gt;
&lt;p&gt;Эта простая схема описывает работу с &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt; сервисом. Ваше &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Android"&gt;Android&lt;/a&gt; приложение (&lt;strong&gt;APP&lt;/strong&gt;) регистрируется в &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt; сервисе
(например при запуске) и отправляет полученный регистрационный код серверу (&lt;strong&gt;APP SERVER&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;Теперь сервер (&lt;strong&gt;APP SERVER&lt;/strong&gt;) используя полученный регистрационный код и специальный токен может сформировать специальный
HTTP-запрос сервису &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt;, который в свою очередь отправит &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; сообщение устройству на котором запущена ваша программа.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;По-умолчанию в &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt; установлена квота на отсылку не более 200000 уведомлений в день.
&lt;a class="reference external" href="http://code.google.com/android/c2dm/quotas.html"&gt;http://code.google.com/android/c2dm/quotas.html&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Подготовка к работе&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для начала необходимо зарегистрировать ваше приложение в &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt; по адресу: &lt;a class="reference external" href="http://code.google.com/android/c2dm/signup.html"&gt;http://code.google.com/android/c2dm/signup.html&lt;/a&gt;&lt;/p&gt;
&lt;img alt="" src="/static/img/android-push2.png" /&gt;
&lt;p&gt;Вам потребуется указать ваш действующий Google-аккаунт и доменное имя вашего приложения. Вы должны получить на почту письмо
с подтверждением. После регистрации вашему приложению будет добавлена возможность получать Push-уведомления с
учетом действующих в &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt; квот.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Настройка поддержки &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; уведомлений на стороне приложения в данной статье не рассматривается.
Вы можете изучить например эту статью: &lt;a class="reference external" href="http://prodroid.com.ua/?p=66"&gt;C2DM Написание Android клиента&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Получение токена для сервера&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Получение авторизационного токена для сервера подробно рассматривается в документации: &lt;a class="reference external" href="https://developers.google.com/accounts/docs/AuthForInstalledApps"&gt;ClientLogin for Installed Applications&lt;/a&gt;. Резюмируя данный документ: для получения токена, нам надо сформировать &lt;strong&gt;POST&lt;/strong&gt; запрос со следующими параметрами:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="20%" /&gt;
&lt;col width="80%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;accountType&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HOSTED_OR_GOOGLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Email&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="mailto:our&amp;#64;email.here"&gt;our&amp;#64;email.here&lt;/a&gt; (email аккаунта регистрировавшего C2DM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Passwd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;our_password_here (пароль)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ac2dm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;our.app идентификатор для логирования (например доменное имя)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Пример запроса:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
POST /accounts/ClientLogin HTTP/1.0
Content-type: application/x-www-form-urlencoded

accountType=HOSTED_OR_GOOGLE&amp;amp;Email;=jondoe&amp;#64;gmail.com&amp;amp;Passwd;=north23AZ&amp;amp;service;=cl&amp;amp;source=Gulp-CalGulp-1.05
&lt;/pre&gt;
&lt;p&gt;Сделать это можно например используя &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/CURL"&gt;Curl&lt;/a&gt; (Замените &lt;em&gt;ACCOUNT&lt;/em&gt;, &lt;em&gt;PASSWORD&lt;/em&gt;, &lt;em&gt;SOURCE&lt;/em&gt; на ваши реквизиты):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
curl -X POST https://www.google.com/accounts/ClientLogin -d Email=ACCOUNT -d Passwd=PASSWORD -d source=SOURCE -d accountType=HOSTED_OR_GOOGLE -d service=ac2dm
&lt;/pre&gt;
&lt;p&gt;В третьей строке &lt;em&gt;Auth=...&lt;/em&gt; и будет ваш искомый токен. Сохраните его в надежном месте.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="push"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Отправка Push уведомлений&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Отправка уведомлений описана в разделе: &lt;a class="reference external" href="http://code.google.com/android/c2dm/index.html#push"&gt;How the Application Server Sends Messages&lt;/a&gt;.
Поддерживаются следующие параметры:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="31%" /&gt;
&lt;col width="69%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;registration_id&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ключ регистрации, полученный приложением от &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt; и переданный серверу приложений&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;collapse_key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Название группы сообщений.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Authorization: GoogleLogin auth=&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Токен авторизации (передается в заголовке)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;data.&amp;lt;key&amp;gt;&lt;/td&gt;
&lt;td&gt;Набор дополнительных данных в формате key-value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;delay_while_idle&lt;/td&gt;
&lt;td&gt;Флаг отложенной доставки. Сообщение может быть доставлено позже.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Этих данных достаточно, чтобы написать простой код (для работы с HTTP я использую замечательную библиотеку &lt;a class="reference external" href="http://docs.python-requests.org/en/latest/index.html"&gt;Requests&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.settings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;C2DM_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C2DM_TOKEN&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AndroidNotifyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registration_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collapse_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay_while_idle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Authorization&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GoogleLogin auth=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;C2DM_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;registration_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;registration_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;collapse_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;collapse_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;delay_while_idle&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;data.&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C2DM_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Error&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;AndroidNotifyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;AndroidNotifyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ClientLogin invalid.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;AndroidNotifyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Service temporarily unavailable.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;  &lt;span class="c1"&gt;#**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь отправлять нотификации можно так:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;send_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;device-registration-id-here&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="django"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Интеграция с Django&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;При интеграции с &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt; проектом стоит подумать об асинхронности запросов в &lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;C2DM&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Асинхронность достигается использованием &lt;a class="reference external" href="http://celeryproject.org/"&gt;Celery&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;celery.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;android&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_notify&lt;/span&gt;

&lt;span class="nd"&gt;@task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ignore_result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_retry_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_async_android_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;send_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_async_android_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_async_android_push&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Fix rst**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Мысли на будущее: отключение недействительных устройств, массовые рассылки и проверка доставки.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="android"></category><category term="push"></category><category term="c2dm"></category><category term="python"></category><category term="django"></category></entry><entry><title>Filler. Пример простой игры на Canvas.</title><link href="http://klen.github.io/filler.html" rel="alternate"></link><published>2012-04-01T00:00:00+04:00</published><updated>2012-04-01T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-04-01:filler.html</id><summary type="html">&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Canvas_(HTML)"&gt;Canvas&lt;/a&gt; HTML API для отрисовки графики. Поддерживается всеми
&lt;strong&gt;современными&lt;/strong&gt; браузерами (в IE начиная с 9й версии)&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Для изучения &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Canvas_(HTML)"&gt;Canvas&lt;/a&gt; решил написать что-нибудь на &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/JavaScript"&gt;JavaScript&lt;/a&gt;, с которым я
практически никогда не работал. В качестве задачи, выбрал реализацию простой игры
по захвату поля, которая в наших интернетах называется &lt;strong&gt;Filler&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;В процессе разработки использовал отличные библиотеки &lt;a class="reference external" href="https://github.com/theshock/atomjs"&gt;AtomJS&lt;/a&gt; и &lt;a class="reference external" href="https://github.com/theshock/libcanvas"&gt;LibCanvas&lt;/a&gt; от
&lt;a class="reference external" href="https://github.com/theshock"&gt;TheShock&lt;/a&gt;. Инструменты понравились и всячески рекомендуются к применению.
Выражаю огромную благодарность автору за работу.&lt;/p&gt;
&lt;p&gt;Результат вы можете опробовать ниже. Цель игры захватить большинство клеток,
меняя свой цвет. Игра начнется по клику на поле. Управлять можно также контролами снизу.
Для начала новой игры, перезагрузите страницу.&lt;/p&gt;
&lt;p&gt;Боты довольно слабенькие, код довольно грязный. Нет поддержки тача (добавлю).
Но в первом приближении другого и не требовалось.&lt;/p&gt;
&lt;p&gt;Исходники доступны на гитхабе: &lt;a class="reference external" href="https://github.com/klen/filler"&gt;https://github.com/klen/filler&lt;/a&gt;&lt;/p&gt;
&lt;div class="filler"&gt;&lt;/div&gt;
&lt;style&gt;
    .filler { background: #000; width: 760px; margin: 0 auto; padding: 10px 0;}
    .filler div { margin: 0 auto; }
&lt;/style&gt;
&lt;script src="../theme/other/filler.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script&gt;atom.dom(function(){
        window.game = new Filler.Game({
            appendTo: '.filler',
            engineSize: new Point(25,20) });
    });
&lt;/script&gt;&lt;p&gt;Ссылки:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://developer.mozilla.org/ru/%D0%9E%D0%B1%D1%83%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_canvas"&gt;Обучение canvas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://habrahabr.ru/post/119772/"&gt;Canvas FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://habrahabr.ru/post/121046/"&gt;Основы LibCanvas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary><category term="canvas"></category><category term="javascript"></category><category term="filler"></category><category term="libcanvas"></category><category term="atomjs"></category></entry><entry><title>Серверная реализация IOS Push уведомлений (python)</title><link href="http://klen.github.io/ios-push-python.html" rel="alternate"></link><published>2012-03-20T00:00:00+04:00</published><updated>2012-03-20T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-03-20:ios-push-python.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id7"&gt;Введение&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id8"&gt;Получение и конвертация сертификата&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id9"&gt;Реализация IOS Push в python-приложениях&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#django" id="id10"&gt;Интеграция с Django&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;img alt="" src="/static/img/push.jpg" /&gt;
&lt;p&gt;Я уже писал &lt;a class="reference external" href="../urbanairship-ru.html"&gt;статью&lt;/a&gt; про реализацию &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; уведомлений с использованием сервиса &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt;.
У этого способа есть ряд преимуществ и недостатков. Преимущества я описал в вышеприведенной статье, недостаки сервиса в
его лимитах на количество бесплатных сообщений и периодических отключениях на техническое обслуживание.&lt;/p&gt;
&lt;p&gt;Сегодня мы рассмотрим реализацию &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Apple_iOS"&gt;IOS&lt;/a&gt; &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; уведомлений своими руками для &lt;a class="reference external" href="http://python.org"&gt;Python&lt;/a&gt; программ.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Исходные коды для данной статьи можно увидеть по адресу: &lt;a class="reference external" href="https://github.com/klen/klen.github.com/tree/master/_code/ios-push"&gt;https://github.com/klen/klen.github.com/tree/master/_code/ios-push&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Apple_iOS"&gt;IOS&lt;/a&gt; &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; уведомления бывают двух видов: локальные и удаленные. Локальные инициируются приложением на стороне
клиента и в данной статье не рассматриваются. Удаленные уведомления отсылаются со стороны сервера на клиентское приложение
при помощи службы &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Apple_Push_Notification_Service"&gt;APNS&lt;/a&gt; (Apple Push Notification Service). Процесс выглядит следующим образом:&lt;/p&gt;
&lt;img alt="" src="/static/img/apns-scheme.jpg" /&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Приложение регистрируется в &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Apple_Push_Notification_Service"&gt;APNS&lt;/a&gt; и получает Device Token;&lt;/li&gt;
&lt;li&gt;Приложение сообщает Device Token нашему серверу;&lt;/li&gt;
&lt;li&gt;Теперь сервер может установить &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/SSL"&gt;SSL&lt;/a&gt; соединение с &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Apple_Push_Notification_Service"&gt;APNS&lt;/a&gt; и отправить &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; уведомление;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Получение и конвертация сертификата&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; сертификаты для приложений можно получить на &lt;a class="reference external" href="https://developer.apple.com/ios/manage/overview/index.action"&gt;iOS Provisioning Portal&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="" src="/static/img/ios-push.png" /&gt;
&lt;p&gt;Выберите свое приложение и зайдите в раздел настроек: &lt;tt class="docutils literal"&gt;Configure&lt;/tt&gt;.&lt;/p&gt;
&lt;img alt="" src="/static/img/ios-push2.png" /&gt;
&lt;p&gt;Скачайте production и development сертификаты (&lt;tt class="docutils literal"&gt;*.cer&lt;/tt&gt;). Возможно вам потребуется предварительно включить
&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_Push"&gt;Push&lt;/a&gt; нотификации для своего приложения и создать их.&lt;/p&gt;
&lt;img alt="" src="/static/img/ios-push3.png" /&gt;
&lt;p&gt;Дальнейшие действия лучше провести на компьютере с OSX. Импортируйте полученные сертификаты и экспортируйте их в формате &lt;strong&gt;p12&lt;/strong&gt;.
Вероятно вам потребуется ключ разработчика с которым были созданы CSR запросы.&lt;/p&gt;
&lt;img alt="" src="/static/img/ios-push4.png" /&gt;
&lt;p&gt;Полученный файл в формате &lt;strong&gt;p12&lt;/strong&gt; необходимо переконвертировать в pem. Для этой задачи я написал простенький shell скрипт.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/klen/klen.github.com/blob/master/_code/ios-push/convert12.sh"&gt;https://github.com/klen/klen.github.com/blob/master/_code/ios-push/convert12.sh&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Сконвертируем с его помощью полученный от Apple Dev сертификат в нужный нам формат:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Получим и подготовим скрипт конвертации&lt;/span&gt;
wget https://raw.github.com/klen/klen.github.com/master/_code/ios-push/convert12.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; chmod +x convert12.sh

&lt;span class="c1"&gt;# Сконвертируем полученные от Apple сертификаты&lt;/span&gt;
./convert12 convert pushprod.p12
./convert12 convert pushdev.p12
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В процессе у вас будет запрошен пароль на сертификаты, если они создавались без пароля, просто нажмите &lt;cite&gt;Enter&lt;/cite&gt;.
Результатом этой операции будут &lt;tt class="docutils literal"&gt;pem&lt;/tt&gt; файлы с теми же названиями.&lt;/p&gt;
&lt;p&gt;Этим же скриптом их можно протестировать, что в дальнейшем избавит от множества проблем при отладке:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# В результате должно быть установлено соединение с Apple&lt;/span&gt;
./convert12 &lt;span class="nb"&gt;test&lt;/span&gt; pushprod.pem prod
./convert12 &lt;span class="nb"&gt;test&lt;/span&gt; pushdev.pem dev
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если соединение сразу сбрасывается или выводятся SSL ошибки, вы что-то сделали неправильно.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Реализация IOS Push в python-приложениях&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;В своих проектах я использую &lt;a class="reference external" href="https://github.com/simonwhitaker/PyAPNs"&gt;PyAPNs&lt;/a&gt; библиотеку. Простой файл обвязка вокруг нее реализует все основные задачи:
&lt;a class="reference external" href="https://github.com/klen/klen.github.com/blob/master/_code/ios-push/push.py"&gt;push.py&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os.path&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;op&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;apns&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APNs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Payload&lt;/span&gt;


&lt;span class="n"&gt;KEYS_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fakekey.pem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KEYS_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_sandbox&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keys_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;KEYS_FILE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot; Create and return production or develop server. &amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;APNs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use_sandbox&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;use_sandbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cert_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;keys_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;keys_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot; Send simple device notify. &amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;get_server&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gateway_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_feedbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot; Get inactive tokens. &amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feedback_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# Fix rst**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Пример использования:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;push&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_notify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_server&lt;/span&gt;

&lt;span class="n"&gt;token_hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b87&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;send_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Hello world!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Вполне возможно, что токен от приложения будет приходить к вам в формате &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Base64"&gt;Base64&lt;/a&gt;, тогда перед сохранением
необходимо проделать следующие преобразования.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;binascii&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;base64&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlsafe_b64decode&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fix_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;unicode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urlsafe_b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binascii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="django"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Интеграция с Django&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;При интеграции с &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt; проектом необходимо подумать о переключении между &lt;strong&gt;developer&lt;/strong&gt; и &lt;strong&gt;production&lt;/strong&gt;
и об асинхронности запросов в &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Apple_Push_Notification_Service"&gt;APNS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Асинхронность достигается использованием &lt;a class="reference external" href="http://celeryproject.org/"&gt;Celery&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;celery.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ios&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_notify&lt;/span&gt;

&lt;span class="nd"&gt;@task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ignore_result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_retry_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_async_ios_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;send_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_async_ios_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot; Send IOS push notification. &amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alert&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sound&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_async_ios_push&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sound&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alert&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;custom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)))&lt;/span&gt;  &lt;span class="c1"&gt;# Fix rst**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Стоит подумать и об отключении неактивных устройств: &lt;tt class="docutils literal"&gt;tasks.py&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;abstract_app.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;celery.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;periodic_task&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.ios&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_feedbacks&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.settings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IOS_CLEAN_INTERVAL&lt;/span&gt;


&lt;span class="nd"&gt;@periodic_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ignore_result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_every&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;IOS_CLEAN_INTERVAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_async_feedbacks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;feedbacks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_feedbacks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;feedbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;feedbacks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;  &lt;span class="c1"&gt;# Fix rst*&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Полностью пример модуля для &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt; вы можете увидеть по ссылке: &lt;a class="reference external" href="https://github.com/klen/klen.github.com/tree/master/_code/ios-push/django/push"&gt;https://github.com/klen/klen.github.com/tree/master/_code/ios-push/django/push&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Написать функцию для асинхронной &lt;strong&gt;broadcast&lt;/strong&gt; рассылки сообщений множеству клиентов, предлагается для самостоятельного
написания в качестве домашнего задания.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="ios"></category><category term="push"></category><category term="iphone"></category><category term="ipad"></category><category term="python"></category><category term="apns"></category><category term="django"></category></entry><entry><title>Настройка HTTPS для чайников</title><link href="http://klen.github.io/https-setup.html" rel="alternate"></link><published>2012-03-12T00:00:00+04:00</published><updated>2012-03-12T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-03-12:https-setup.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id6"&gt;Введение&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#csr" id="id7"&gt;Запрос на получение сертификата (CSR)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#ssl-90" id="id8"&gt;Получение подписанных SSL ключей на 90-дневной период&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#nginx-ssl" id="id9"&gt;Настройка Nginx для работы с SSL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;&lt;strong&gt;HTTPS&lt;/strong&gt; (Hypertext Transfer Protocol Secure) — расширение протокола &lt;strong&gt;HTTP&lt;/strong&gt; поддерживающее шифрование. Данные, передаваемые по протоколу &lt;strong&gt;HTTPS&lt;/strong&gt; «упаковываются» в криптографический протокол &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/SSL"&gt;SSL&lt;/a&gt; или &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/TCL"&gt;TLS&lt;/a&gt;, тем самым обеспечивается защита этих данных. В отличие от &lt;strong&gt;HTTP&lt;/strong&gt; для &lt;strong&gt;HTTPS&lt;/strong&gt; по умолчанию используется &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/TCP"&gt;TCP&lt;/a&gt;-порт 443.&lt;/p&gt;
&lt;p&gt;В данной статье я расскажу о том, как бесплатно (90 дней) получить сертифицированные &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/SSL"&gt;SSL&lt;/a&gt; ключи для своего сервера и настроить &lt;strong&gt;HTTPS&lt;/strong&gt; соединения.
На данный момент в русскоязычных источниках эта информация представлена разрозненно, поэтому данную статью можно использовать как руководство
по получению подписанных ключей и быстрой настройке &lt;strong&gt;HTTPS&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id6"&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Чтобы подготовить сервер для обработки https-соединений, администратор должен получить и установить в систему сертификат для этого веб-сервера.
Сертификат можно создать самостоятельно с помощью &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Unix"&gt;Unix&lt;/a&gt; утилиты &lt;cite&gt;ssl-ca&lt;/cite&gt; из пакета &lt;strong&gt;OpenSSL&lt;/strong&gt;, но подписанный данным методом сертификат не
является безопасным и большинство браузеров выдают предупреждения об этом. Часть агентов также блокирует сайты с такими сертификатами,
например IPhone приложения.&lt;/p&gt;
&lt;p&gt;Сертификаты подписанные компаниями-сертификаторами избавлены от данной проблемы, но требуют оплаты для использования. Основные компании-сертификаторы:
&lt;a class="reference external" href="http://www.verisign.com/"&gt;VerySign&lt;/a&gt;, &lt;a class="reference external" href="http://www.ssl.com/"&gt;SSLCOM&lt;/a&gt;, &lt;a class="reference external" href="http://www.digicert.com/"&gt;DigiCert&lt;/a&gt; и другие. Так сложилось, что я предпочитаю использовать &lt;a class="reference external" href="http://www.ssl.com/"&gt;SSLCOM&lt;/a&gt; в основном из-за их 90-дневного бесплатного периода
использования для новых ключей, получение которого и будет описано в данной статье. За три месяца можно основательно протестировать проект, понять приоритеты,
сделать полноценный запуск и в дальнейшем уже решить будете ли вы продлевать действие сертификата за деньги.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="csr"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Запрос на получение сертификата (CSR)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CSR&lt;/strong&gt; — запрос на получение сертификата, требуется для подписи ключей компанией-сертификатором. Я создаю его в консоли &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Unix"&gt;Unix&lt;/a&gt;-систем,
например в терминале на стороне сервера. Если у вас нет такой возможности, вы можете создать &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Certificate_signing_request"&gt;CSR&lt;/a&gt; онлайн: &lt;a class="reference external" href="https://www.reg.ru/sslcertificate/generate_key_and_csr"&gt;https://www.reg.ru/sslcertificate/generate_key_and_csr&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Из консоли:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Создадим директорию для работы&lt;/span&gt;
mkdir -p ~/csr &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/csr

&lt;span class="c1"&gt;# Создадим приватный ключ и запрос на сертификат&lt;/span&gt;
openssl req -new -newkey rsa:2048 -nodes -keyout &amp;lt;your_domain_name&amp;gt;.key -out &amp;lt;your_domain_name&amp;gt;.csr
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Все поля &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Certificate_signing_request"&gt;CSR&lt;/a&gt; должны заполняться на английском. Код страны — двух буквенный &lt;a class="reference external" href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2"&gt;Alpha2&lt;/a&gt; (RU для России).&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;your_domain_name&lt;/tt&gt; — имя домена для которого создается сертификат&lt;/p&gt;
&lt;p class="last"&gt;&lt;strong&gt;Common name&lt;/strong&gt; — адрес домена (без www). Wildcart записи вида *.domen.com не поддерживаются на бесплатном тарифе.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Пример заполнения:&lt;/p&gt;
&lt;img alt="example" src="/images/https_0.png" /&gt;
&lt;p&gt;Скопируйте полученный ключ &lt;cite&gt;&amp;lt;your_domain_name&amp;gt;.key&lt;/cite&gt; и файл запроса &lt;cite&gt;&amp;lt;your_domain_name&amp;gt;.csr&lt;/cite&gt; в надежное место.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ssl-90"&gt;
&lt;h2&gt;Получение подписанных &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/SSL"&gt;SSL&lt;/a&gt; ключей на 90-дневной период&lt;/h2&gt;
&lt;p&gt;Подписывать ключи будем у &lt;a class="reference external" href="http://ssl.com"&gt;http://ssl.com&lt;/a&gt;. Для начала работы войдите на сервис или &lt;a class="reference external" href="http://ssl.com/users/new"&gt;зарегистрируйтесь&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;После входа попадаем на &lt;a class="reference external" href="http://ssl.com/account"&gt;страницу задач&lt;/a&gt; и выбираем &lt;tt class="docutils literal"&gt;buy certificate&lt;/tt&gt; для продолжения.&lt;/p&gt;
&lt;img alt="page" src="/images/https_1.png" /&gt;
&lt;p&gt;На открывшейся странице выбираем &lt;tt class="docutils literal"&gt;Free SSL&lt;/tt&gt;.&lt;/p&gt;
&lt;img alt="page" src="/images/https_2.png" /&gt;
&lt;p&gt;Нажимаем кнопку &lt;tt class="docutils literal"&gt;GET&lt;/tt&gt; и попадаем на первый шаг процесса подписания нашего сертификата. В открывшейся странице выберите чекбокс
&lt;tt class="docutils literal"&gt;I want to submit my CSR now&lt;/tt&gt; для добавления полученного нами на предыдущем шаге &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Certificate_signing_request"&gt;CSR&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;В данной статье мы будем настраивать &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Nginx"&gt;Nginx&lt;/a&gt; поэтому из выпадающего списка я выбрал соответствующую опцию.&lt;/p&gt;
&lt;img alt="page" src="/images/https_3.png" /&gt;
&lt;p&gt;Если &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Certificate_signing_request"&gt;CSR&lt;/a&gt; запрос был сформирован правильно, то на следующем шаге вас попросят принять условия использования. После принятия
вы попадаете в форму ввода дополнительной информации, где от вас потребуется ввод адреса и почтового индекса. Далее информация
для контактных лиц.&lt;/p&gt;
&lt;p&gt;Пятый шаг — подтверждение домена. Возможно два варианта, размещение файла на сервере или подтверждение по электронной почте.
Адреса требуемые для подтверждения через email не всегда доступны, поэтому я обычно выкладываю на сервер полученный файл.&lt;/p&gt;
&lt;img alt="page" src="/images/https_4.png" /&gt;
&lt;p&gt;Пример конфигурации для &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Nginx"&gt;Nginx&lt;/a&gt; (файл &lt;cite&gt;D3AD7299FFC0954B0FD047063ABB7EFD.txt&lt;/cite&gt; лежит в папке &lt;cite&gt;/home/user&lt;/cite&gt;):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
location = /D3AD7299FFC0954B0FD047063ABB7EFD.txt {
    alias /home/user/D3AD7299FFC0954B0FD047063ABB7EFD.txt;
}
&lt;/pre&gt;
&lt;p&gt;После получения подписанного сертификата можно будет удалить файл и изменения в настройках.&lt;/p&gt;
&lt;p&gt;Если проверку вы прошли успешно, остается только ждать подтверждения от компании-сертификатора, что может занять от нескольких
часов до суток.&lt;/p&gt;
&lt;img alt="page" src="/images/https_5.png" /&gt;
&lt;p&gt;После добавления ключей компанией сертификатором вам будет выслано уведомительное письмо. Откройте страницу заказа сертификата и
загрузите его к себе на компьютер. Для &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Nginx"&gt;Nginx&lt;/a&gt; вы получите файл &lt;tt class="docutils literal"&gt;ca_bundle.txt&lt;/tt&gt;&lt;/p&gt;
&lt;img alt="" src="/images/https_6.png" /&gt;
&lt;p&gt;Полученный файл переименуйте в формат &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;your_domain_name&amp;gt;.bundle.crt&lt;/span&gt;&lt;/tt&gt; и вместе с ключом который мы создали на первом шаге
(&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;your_domain_name&amp;gt;.key&lt;/span&gt;&lt;/tt&gt;) положите на сервер.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="nginx-ssl"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Настройка Nginx для работы с SSL&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для включения &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/SSL"&gt;SSL&lt;/a&gt; в &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Nginx"&gt;Nginx&lt;/a&gt; добавьте в конфигурацию сервера следующие настройки:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
server {
    ...
    listen 443;
    ssl                     on;
    ssl_protocols   SSLv3 TLSv1;
    ssl_certificate /&amp;lt;path_to_file&amp;gt;/&amp;lt;your_domain_name&amp;gt;.bundle.crt;
    ssl_certificate_key     /&amp;lt;path_to_file&amp;gt;/&amp;lt;your_domain_name&amp;gt;.key;
    ...
}
&lt;/pre&gt;
&lt;p&gt;Для автоматического направления всех HTTP запросов на HTTPS, обычно достаточно следующей конфигурации:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# HTTPS settings
server {
    listen 443;
    ssl                     on;
    ssl_protocols   SSLv3 TLSv1;
    ssl_certificate /&amp;lt;path_to_file&amp;gt;/&amp;lt;your_domain_name&amp;gt;.bundle.crt;
    ssl_certificate_key     /&amp;lt;path_to_file&amp;gt;/&amp;lt;your_domain_name&amp;gt;.key;
    ...
}

# HTTP settings
server {
    listen 80
    rewrite ^(.*) https://$server_name$1 permanent;
}
&lt;/pre&gt;
&lt;p&gt;И не забудьте обновить сертификаты через три месяца! :)&lt;/p&gt;
&lt;/div&gt;
</summary><category term="https"></category><category term="ssl"></category><category term="nginx"></category><category term="csr"></category><category term="verysign"></category><category term="digicert"></category><category term="deploy"></category></entry><entry><title>Flask. Примеры opensource проектов</title><link href="http://klen.github.io/notes-flask-opensource.html" rel="alternate"></link><published>2012-02-18T00:00:00+04:00</published><updated>2012-02-18T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-02-18:notes-flask-opensource.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; — легковесный веб-фреймворк для python. Создатель стремится сделать его
простым, легким в освоении для новичков и легко расширяемым.&lt;/p&gt;
&lt;p&gt;Ядро микрофреймворка содержит следующие возможности:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Сервер для разработки и дебаггер;&lt;/li&gt;
&lt;li&gt;Поддержка unit тестов;&lt;/li&gt;
&lt;li&gt;RESTfull request routing;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Jinja_(template_engine)"&gt;Jinja2&lt;/a&gt; шаблоны;&lt;/li&gt;
&lt;li&gt;Сессии;&lt;/li&gt;
&lt;li&gt;100% совместимость с &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/WSGI"&gt;WSGI&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Поддержка Unicode;&lt;/li&gt;
&lt;li&gt;Хорошая документация;&lt;/li&gt;
&lt;li&gt;Совместимость с &lt;a class="reference external" href="GAE"&gt;Google App Engine&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Лёгкая расширяемость.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Лучшие способ освоить новую библиотеку — прочитать документацию и
изучить хорошие примеры. Несколько открытых проектов на &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; я и собрал ниже.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://hg.rafaelmartins.eng.br/blohg/"&gt;http://hg.rafaelmartins.eng.br/blohg/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/j4mie/brightonpy.org"&gt;https://github.com/j4mie/brightonpy.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/dcolish/Cockerel"&gt;https://github.com/dcolish/Cockerel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://bitbucket.org/xoraxax/domination"&gt;https://bitbucket.org/xoraxax/domination&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/lincolnloop/emailed-me"&gt;https://github.com/lincolnloop/emailed-me&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/mitsuhiko/flask-feedback"&gt;https://github.com/mitsuhiko/flask-feedback&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/mitsuhiko/flask-pastebin"&gt;https://github.com/mitsuhiko/flask-pastebin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/sharms/HomePage"&gt;https://github.com/sharms/HomePage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/kennethreitz/httpbin"&gt;https://github.com/kennethreitz/httpbin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/riquellopes/micro-blog"&gt;https://github.com/riquellopes/micro-blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/sfermigier/Planet-GTLL.git"&gt;https://github.com/sfermigier/Planet-GTLL.git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://bitbucket.org/leafstorm/ryshcate"&gt;https://bitbucket.org/leafstorm/ryshcate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/eugenkiss/Simblin.git"&gt;https://github.com/eugenkiss/Simblin.git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Если вам известны другие хорошие примеры, я с удовольствием добавлю их в этот
список.&lt;/p&gt;
</summary><category term="flask"></category></entry><entry><title>Ссылки и заметки</title><link href="http://klen.github.io/notes-welcome.html" rel="alternate"></link><published>2012-02-18T00:00:00+04:00</published><updated>2012-02-18T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2012-02-18:notes-welcome.html</id><summary type="html">&lt;p&gt;Новый дизайн сайта и новая рубрика «Заметки». Формат полноценной статьи не
всегда удобен когда хочется поделиться мыслями или найденной в интернете
информацией. Для этих целей и предназначен данный раздел.&lt;/p&gt;
</summary><category term="notes"></category></entry><entry><title>Несколько полезных сниппетов для джанго</title><link href="http://klen.github.io/django-snippets.html" rel="alternate"></link><published>2011-12-02T00:00:00+04:00</published><updated>2011-12-02T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-12-02:django-snippets.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id2"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#filefield-imagefield" id="id9"&gt;Автоматическая генерация имени файла в полях типа FileField, ImageField&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id10"&gt;Пример работы&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id11"&gt;Параметры сниппета&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id12"&gt;Работа с кешем&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id6" id="id13"&gt;Примеры работы&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id7" id="id14"&gt;Атомарные обновления&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Ниже находится несколько полезных &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; сниппетов, которые я использую в своей работе.&lt;/p&gt;
&lt;div class="section" id="filefield-imagefield"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Автоматическая генерация имени файла в полях типа FileField, ImageField&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;В &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; поля типа &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield"&gt;FileField&lt;/a&gt; и &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/models/fields/#imagefield"&gt;ImageField&lt;/a&gt; позволяют определять функцию для генерации имени
сохраняемого файла. Она указывается в аргументе &lt;tt class="docutils literal"&gt;upload_to&lt;/tt&gt; при создании поля.&lt;/p&gt;
&lt;p&gt;Когда мне надоело придумывать уникальные имена файлов, при сохранении их
в &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt;-проектах я написал нижеприведенный сниппет.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Генерируемые пути файлов содержат только латинские и цифровые символы;&lt;/li&gt;
&lt;li&gt;Файлы с одинаковыми названиями, загружаемые в разные инстансы не перезаписываются;&lt;/li&gt;
&lt;li&gt;Файлы удобно хранятся отсортированные по приложениям и моделям;&lt;/li&gt;
&lt;li&gt;Путь файла однозначно говорит нам о том, что за инстанс его использует;&lt;/li&gt;
&lt;li&gt;Система не позволяет тысячам файлов скапливаться в одной папке;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;md5&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Auto generate name for File and Image fields.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# We think that we use utf8 based OS file system&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt;
    &lt;span class="n"&gt;basedir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;basedir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basedir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basedir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id10"&gt;Пример работы&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Например у нас есть модель &lt;tt class="docutils literal"&gt;Archive&lt;/tt&gt; в приложении &lt;tt class="docutils literal"&gt;storage&lt;/tt&gt;, &lt;cite&gt;storage/models.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Archive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В результате сохраненные файлы будут иметь пути вида:
&lt;cite&gt;{MEDIA_URL}/storage/archive/21/40/2140e8fe8678bc08ff6e9b10c9639068.zip&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;Здесь присутствует имя приложения, имя модели, созданное новое имя файла и его расширение.
При этом путь дробится на папки согласно четырем первым символам в имени файла &lt;cite&gt;21/40/2140e8...&lt;/cite&gt;.
Это позволяет значительно отсрочить ситуацию с сотнями тысяч объектов в папке и тормоза файловой системы.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id11"&gt;Параметры сниппета&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Функция &lt;cite&gt;upload_to&lt;/cite&gt; может принимать до 4-х параметров. Аргументы &lt;strong&gt;instance&lt;/strong&gt; и &lt;strong&gt;filename&lt;/strong&gt; передаются &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Аргумент &lt;strong&gt;prefix&lt;/strong&gt; принимает строковые значения и позволяет дополнительно настраивать пути файлов.
Например при определении префикса &lt;cite&gt;prefix=&amp;quot;cart&amp;quot;&lt;/cite&gt; вышеприведенный код будет сохранять файлы с путями вида:
&lt;cite&gt;{MEDIA_URL}/storage/archive/cart/21/40/2140e8fe8678bc08ff6e9b10c9639068.zip&lt;/cite&gt;. Префикс был добавлен после имени модели.&lt;/p&gt;
&lt;p&gt;Аргумент &lt;strong&gt;unique&lt;/strong&gt; принимает булевы значения и позволяет гарантированно получить уникальное имя файла. По-умолчанию для &lt;cite&gt;instance&lt;/cite&gt;
с одинаковым ключом, для одинаковых имен файла, будет создан идентичный путь. Это сделано намеренно, чтобы при обновлении
файлов пути в конкретной модели не изменялись. Параметр &lt;cite&gt;unique&lt;/cite&gt; меняет это поведение и при каждом изменении файла, путь будет другим.&lt;/p&gt;
&lt;p&gt;Но как использовать все эти параметры если &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; передает в указанную функцию только первые два? Здесь нам поможет &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%80%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"&gt;Каррирование&lt;/a&gt;.
Мы используем функцию &lt;strong&gt;curry&lt;/strong&gt; из &lt;cite&gt;django.utils.functional&lt;/cite&gt;, которая имитирует это поведение в python.&lt;/p&gt;
&lt;p&gt;Например в нижеприведенном коде:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.functional&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;curry&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Archive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cover&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Файлы из поля &lt;cite&gt;cover&lt;/cite&gt; будут сохраняться с путями вида: &lt;cite&gt;{MEDIA_URL}/storage/archive/cover/21/40/2140e8fe8678bc08ff6e9b10c9639068.jpg&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;Файлы из поля &lt;cite&gt;file&lt;/cite&gt; будут сохраняться с путями вида: &lt;cite&gt;{MEDIA_URL}/storage/archive/file/21/40/2140e8fe8678bc08ff6e9b10c9639068.zip&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;Файлы из поля &lt;cite&gt;other&lt;/cite&gt; всегда будут иметь уникальные пути, вида: &lt;cite&gt;{MEDIA_URL}/storage/archive/11/11/1111e8fe8678bc08ff6e9b10c9639068.mp3&lt;/cite&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id12"&gt;Работа с кешем&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;В работе с кешированием проекта нужна четкая стратегия и обозначенные соглашения.
Например наименования ключей кэша. &lt;cite&gt;SomeModel.object.filter(active=True)&lt;/cite&gt; с каким ключом хранить
результат выполнения этого запроса? Как параллельный разработчик узнает этот ключ? Когда производить
инвалидацию? Для &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; существуют приложения помогающие решать эту проблему, но зачастую они слишком
перегружены и сложны в использовании.&lt;/p&gt;
&lt;p&gt;Для простых проектов я написал несколько полезных функций:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.cache&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_model&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ModelBase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models.query&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.encoding&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;smart_str&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cached_instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Auto cached model instance.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;#39;model_class&amp;#39; must be either a model&amp;quot;&lt;/span&gt;
                                &lt;span class="s2"&gt;&amp;quot; or a model name in the format&amp;quot;&lt;/span&gt;
                                &lt;span class="s2"&gt;&amp;quot; app_label.model_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cache_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_related&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cached_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Auto cached queryset and generate results.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;cache_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Generate cache key and clean cached value.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;cache_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Auto generate cache key for model or queryset&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelBase&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Objects must be queryset or model.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Cache key cannot be empty.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clean_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Replace spaces with &amp;#39;-&amp;#39; and hash if length is greater than 250.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;cache_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;r&amp;#39;\s+&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cache_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smart_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cache_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cache_key&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_timeout&lt;/span&gt;

        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

    &lt;span class="c1"&gt;#**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id13"&gt;Примеры работы&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Генерация ключей для Queryset (разные ключи для разных запросов)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaxiStation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaxiStation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaxiStation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaxiStation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;intaxi&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaxiStation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Output: &amp;#39;SELECT-&amp;quot;main_taxistation&amp;quot;.&amp;quot;id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;active&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;title&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;city_id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;agent_id&amp;quot;,--873aa3b2fbd81cdaa9fce75e60706579&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# Output: &amp;#39;SELECT-&amp;quot;main_taxistation&amp;quot;.&amp;quot;id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;active&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;title&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;city_id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;agent_id&amp;quot;,--873aa3b2fbd81cdaa9fce75e60706579&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# Output: &amp;#39;SELECT-&amp;quot;main_taxistation&amp;quot;.&amp;quot;id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;active&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;title&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;city_id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;agent_id&amp;quot;,--043271300c8db6cc9152ef3119e6195c&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# Output: &amp;#39;SELECT-&amp;quot;main_taxistation&amp;quot;.&amp;quot;id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;active&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;title&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;city_id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;agent_id&amp;quot;,--98960ebe77c30e08fe6b4a4fd2b1ab57&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# Output: &amp;#39;SELECT-&amp;quot;main_taxistation&amp;quot;.&amp;quot;id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;active&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;title&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;city_id&amp;quot;,-&amp;quot;main_taxistation&amp;quot;.&amp;quot;agent_id&amp;quot;,--043271300c8db6cc9152ef3119e6195c&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Генерация ключей для Model&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaxiStation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;generate_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaxiStation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Output: main.taxistation:pk=50&lt;/span&gt;

&lt;span class="c1"&gt;# Кеширование Queryset (получение данных из кеша или из БД с сохранением в кеш)&lt;/span&gt;
&lt;span class="nb"&gt;all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cached_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SomeModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;some_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;cached_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SomeModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Принудительная очистка кеша Queryset&lt;/span&gt;
&lt;span class="n"&gt;clean_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SomeModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;clean_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SomeModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Кеширование instance&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cached_instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Some title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# можно и по имени&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cached_instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;app.order&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Some title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Очистка кеша&lt;/span&gt;
&lt;span class="n"&gt;clean_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Some title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id14"&gt;Атомарные обновления&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Часто возникают ситуации когда нам необходимо обновить одно или несколько полей объекта.
В &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; по-умолчанию это можно сделать при помощи следующего кода:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Допустим у нас есть объект order (instance of model)&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;custom_value&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_clean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;При этом ORM &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; создает и выполняет запрос содержащий обновление &lt;strong&gt;всех полей&lt;/strong&gt; объекта, что может быть довольно медленной операцией.&lt;/p&gt;
&lt;p&gt;Значительно быстрее сработает следующая конструкция:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;custom_value&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_clean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;custom_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;custom_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Но она несколько неудобна. Следующая функция решает эту проблему.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;operator&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models.expressions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;


&lt;span class="n"&gt;EXPRESSION_NODE_CALLBACKS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MUL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DIV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MOD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;or_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CannotResolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_expression_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXPRESSION_NODE_CALLBACKS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;CannotResolve&lt;/span&gt;
    &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]:&lt;/span&gt;
        &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full_clean&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;Atomically update instance, setting field/value pairs from kwargs&amp;quot;&lt;/span&gt;

    &lt;span class="c1"&gt;# apply the updated args to the instance to mimic the change&lt;/span&gt;
    &lt;span class="c1"&gt;# note that these might slightly differ from the true database values&lt;/span&gt;
    &lt;span class="c1"&gt;# as the DB could have been updated by another thread. callers should&lt;/span&gt;
    &lt;span class="c1"&gt;# retrieve a new copy of the object if up-to-date values are required&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ExpressionNode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolve_expression_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# clean instance before update&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;full_clean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_clean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# fields that use auto_now=True should be updated corrected, too!&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;auto_now&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auto_now&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pre_save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;rows_affected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_default_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rows_affected&lt;/span&gt;

&lt;span class="c1"&gt;#**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь мы можем делать так:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Обновили объект и сохранили его в базу, через&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;custom_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;custom_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_field&lt;/span&gt;
&lt;span class="c1"&gt;# Output: custom_value&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Или даже так:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;custom_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;custom_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;other_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;more_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;more_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Обходясь при этом без тяжелых запросов к базе.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
SELECT (1) AS &amp;quot;a&amp;quot; FROM &amp;quot;main_order&amp;quot; WHERE &amp;quot;main_order&amp;quot;.&amp;quot;id&amp;quot; = 22387  LIMIT 1; args=(22387,)
UPDATE &amp;quot;main_order&amp;quot; SET &amp;quot;created_at&amp;quot; = E'2011-12-02 19:39:00.969044', &amp;quot;city_id&amp;quot; = 1, &amp;quot;when&amp;quot; = E'2011-12-02 19:50:00', &amp;quot;price&amp;quot; = E'100.00', &amp;quot;expected_price&amp;quot; = E'350.00', &amp;quot;car_class&amp;quot; = 10, &amp;quot;conditioner&amp;quot; = false, &amp;quot;smoke&amp;quot; = false, &amp;quot;no_smoke&amp;quot; = false, &amp;quot;child_seat&amp;quot; = false, &amp;quot;long_length&amp;quot; = false, &amp;quot;meet_sign&amp;quot; = false, &amp;quot;from_address_id&amp;quot; = 17130, &amp;quot;to_address_id&amp;quot; = 17131, &amp;quot;route_id&amp;quot; = NULL, &amp;quot;from_node_id&amp;quot; = 501, &amp;quot;to_node_id&amp;quot; = 501, &amp;quot;from_address_entrance_no&amp;quot; = E'', &amp;quot;from_comment&amp;quot; = E'', &amp;quot;to_comment&amp;quot; = E'', &amp;quot;flight_number&amp;quot; = E'', &amp;quot;meet_sign_text&amp;quot; = E'', &amp;quot;auto_id&amp;quot; = NULL, &amp;quot;fare_id&amp;quot; = 42, &amp;quot;taxistation_id&amp;quot; = 1, &amp;quot;passenger_id&amp;quot; = 71111111111, &amp;quot;device_id&amp;quot; = E'200774696189910', &amp;quot;device_agent&amp;quot; = E'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.2 ', &amp;quot;device_name&amp;quot; = E'Web', &amp;quot;device_token&amp;quot; = E'', &amp;quot;device_sms&amp;quot; = true, &amp;quot;current_status&amp;quot; = E'WaitForCarAssigment', &amp;quot;updated&amp;quot; = E'2011-12-02 19:40:09.405462', &amp;quot;credit&amp;quot; = false WHERE &amp;quot;main_order&amp;quot;.&amp;quot;id&amp;quot; = 22387 ; args=(u'2011-12-02 19:39:00.969044', 1, u'2011-12-02 19:50:00', u'100.00', u'350.00', 10, False, False, False, False, False, False, 17130, 17131, 501, 501, u'', u'', u'', u'', u'', 42, 1, 71111111111L, u'200774696189910', u'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.2 ', u'Web', u'', True, 'WaitForCarAssigment', u'2011-12-02 19:40:09.405462', False, 22387)
&lt;/pre&gt;
&lt;p&gt;VS&lt;/p&gt;
&lt;pre class="literal-block"&gt;
UPDATE &amp;quot;main_order&amp;quot; SET &amp;quot;price&amp;quot; = E'100.00' WHERE &amp;quot;main_order&amp;quot;.&amp;quot;id&amp;quot; = 22387 ; args=(u'100.00', 22387)
&lt;/pre&gt;
&lt;p&gt;Надеюсь эти несколько простых функций, будут также полезны вам, как и мне.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="django"></category><category term="cache"></category><category term="upload"></category><category term="orm"></category></entry><entry><title>Настройка сервера. Создаем и разворачиваем django-проект</title><link href="http://klen.github.io/deploy-setup.html" rel="alternate"></link><published>2011-11-26T00:00:00+04:00</published><updated>2011-11-26T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-11-26:deploy-setup.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#makesite" id="id12"&gt;Makesite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id13"&gt;Установка и настройка Makesite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id14"&gt;Создание проекта&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id15"&gt;Подготовка Django-проекта к деплою&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id6" id="id16"&gt;Создание ключей для клонирования репозитория&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id7" id="id17"&gt;Установка зависимостей&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id8" id="id18"&gt;Установка и удаление проекта&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id9" id="id19"&gt;Обновление проекта&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;В &lt;a class="reference external" href="../gitolite-setup-ru.html"&gt;предыдущей&lt;/a&gt; статье показывалось как поднять и настроить
хостинг &lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt; репозиториев на своем сервере. Ниже будет описано создание &lt;a class="reference external" href="http://djangoproject.org"&gt;Django&lt;/a&gt; проекта,
развертывание его на сервере и настройка авто-обновления кода.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Все инструкции в этой статье приводятся для системы
&lt;strong&gt;Ubuntu Oneiric&lt;/strong&gt;, пользователь &lt;strong&gt;ubuntu&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="makesite"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id12"&gt;Makesite&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Настройка, развертывание и обновление сайтов — операции, требующие автоматизации. Есть множество
утилит для этого, например &lt;a class="reference external" href="https://github.com/capistrano/capistrano"&gt;Capistrano&lt;/a&gt; (ruby) или &lt;a class="reference external" href="http://docs.fabfile.org/en/1.3.3/index.html"&gt;Fabric&lt;/a&gt; (python). В свое время эта
задача встала и передо мной.
Были определены следующие требования:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;В основе системы должны находится &lt;strong&gt;shell&lt;/strong&gt; скрипты;&lt;/li&gt;
&lt;li&gt;Развертывание проекта должно производится одной командой;&lt;/li&gt;
&lt;li&gt;Элементарное обновление или удаление проектов;&lt;/li&gt;
&lt;li&gt;Каждый экземпляр проекта привязан к ветке репозитория;&lt;/li&gt;
&lt;li&gt;Настройки всех проектов могут быть заданы как глобально, так и в момент развертывания;&lt;/li&gt;
&lt;li&gt;Легкость настройки и создания шаблонов;&lt;/li&gt;
&lt;li&gt;Автономность уже развернутого проекта;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Беглый осмотр существующих решений привел к необходимости в очередной раз писать свой велосипед.
В итоге на свет появился &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt;. Описание всех принципов его работы выходит за рамки данной статьи.&lt;/p&gt;
&lt;p&gt;Основное его предназначение — автоматическое создание будущей структуры сайта. Файлов
конфигурации сервера и скриптов обновления и перезапуска. Вы можете изменять и редактировать
параметры этих файлов после развертывания.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id13"&gt;Установка и настройка Makesite&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Данные действия выполняются на сервере&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Для начала работы на нашем сервере потребуется установить и настроить &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt;.
Рекомендуется проводить установку при помощи &lt;strong&gt;setuptools&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Устанавливаем setup-tools&lt;/span&gt;
sudo apt-get install python-setuptools python-pip

&lt;span class="c1"&gt;# Устанавливаем makesite&lt;/span&gt;
sudo easy_install makesite

&lt;span class="c1"&gt;# Или при помощи Pip&lt;/span&gt;
sudo pip install makesite
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь нам необходимо решить в какой директории будут развернуты наши проекты и указать ее для &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt;.
Обычно я использую &lt;cite&gt;/var/www&lt;/cite&gt;. Чтобы не вводить этот путь при каждой операции, определим системную переменную &lt;cite&gt;$MAKESITE_HOME&lt;/cite&gt;. Удобно сразу это сделать при помощи следующей команды:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Создаем директорию для хранения наших будущих сайтов&lt;/span&gt;
$ sudo mkdir /var/www

&lt;span class="c1"&gt;# Выводим и проверяем настройки bash&lt;/span&gt;
$ makesite shell -p /var/www

&lt;span class="c1"&gt;# Записываем их в ваш ~/.bashrc&lt;/span&gt;
$ makesite shell -p /var/www &amp;gt;&amp;gt; ~/.bashrc

&lt;span class="c1"&gt;# Загружаем настройки для текущего сеанса&lt;/span&gt;
$ &lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Создаем корневой файл конфигурации в указанной нами директории &lt;cite&gt;/var/www/makesite.ini&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;В примерах используется адрес созданного в предыдущих статьях &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt; сервера: &lt;strong&gt;awsdemo.us.to&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Main]&lt;/span&gt;

&lt;span class="c1"&gt;# Указываем источник получения кода проектов по умолчанию&lt;/span&gt;
&lt;span class="c1"&gt;# В данном случае будет использоваться созданный нами репозиторий&lt;/span&gt;
&lt;span class="c1"&gt;# Префикс &amp;#39;git+&amp;#39; указывает Makesite на способ получения&lt;/span&gt;
&lt;span class="c1"&gt;# В настройках можно применять переменные, в данном случае будет&lt;/span&gt;
&lt;span class="c1"&gt;# производиться поиск репозитория по имени разворачиваемого проекта&lt;/span&gt;
&lt;span class="na"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;git+git@awsdemo.us.to:%(project)s.git&lt;/span&gt;

&lt;span class="c1"&gt;# Доменное имя для создаваемых конфигураций сайтов&lt;/span&gt;
&lt;span class="c1"&gt;# Можно было сделать например так:&lt;/span&gt;
&lt;span class="c1"&gt;# domain = %(branch)s.%(project)s.us.to&lt;/span&gt;
&lt;span class="c1"&gt;# Но так как полученный нами бесплатный DNS не поддерживает Wildcard записи,&lt;/span&gt;
&lt;span class="c1"&gt;# поступим проще:&lt;/span&gt;
&lt;span class="na"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;%(project)s.us.to&lt;/span&gt;

&lt;span class="c1"&gt;# Режим деплоя (это всего лишь переменная которую можно использовать в шаблонах)&lt;/span&gt;
&lt;span class="na"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Создание этого файла необязательно, но он здорово упрощает развертывание. В любом случае
у вас всегда есть возможность изменять конфигурации после развертывания.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id14"&gt;Создание проекта&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Данные действия выполняются локально&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Перейдем к репозиторию &lt;cite&gt;awsdemo&lt;/cite&gt; на нашей локальной машине, который мы создали в
прошлой статье и превратим его в Django-проект.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Переходим в директорию нашего проекта&lt;/span&gt;
$ &lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/awsdemo

&lt;span class="c1"&gt;# Создаем новый django-проект&lt;/span&gt;
$ django-admin.py startproject aws

&lt;span class="c1"&gt;# Переносим файлы из созданного проекта в наш репозиторий&lt;/span&gt;
$ mv aws/* . &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -r aws

&lt;span class="c1"&gt;# Сохраняем изменения в git&lt;/span&gt;
$ git add .
$ git commit -m &lt;span class="s1"&gt;&amp;#39;Start new django-project&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь его надо подготовить к деплою.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Файлы с исходным кодом Django-проекта можно найти по адресу:
&lt;a class="reference external" href="https://github.com/klen/klen.github.com/tree/master/_code/awsdemo"&gt;https://github.com/klen/klen.github.com/tree/master/_code/awsdemo&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id15"&gt;Подготовка Django-проекта к деплою&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Данные действия выполняются локально&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Рекомендуется, но совсем необязательно, создавать в корневой
директории проекта файл конфигурации &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; с основными параметрами деплоя.&lt;/p&gt;
&lt;p&gt;Создадим файл &lt;cite&gt;makesite.ini&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Main]&lt;/span&gt;
&lt;span class="c1"&gt;# Шаблоны Makesite которые будут применятся для нашего сайта&lt;/span&gt;
&lt;span class="c1"&gt;# всегда есть возможность создать собственные шаблоны&lt;/span&gt;
&lt;span class="c1"&gt;# в том числе и в хранимые с исходниками проекта&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;virtualenv,django,uwsgi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Для шаблона &lt;cite&gt;virtualenv&lt;/cite&gt; (он создает и обновляет при необходимости
виртуальное окружение проекта) необходимо создать файл с зависимостями.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;requirements.txt&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Django&lt;/span&gt;
&lt;span class="n"&gt;Django&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mf"&gt;1.3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Для шаблона &lt;cite&gt;uwsgi&lt;/cite&gt; (он запускает наш проект с помощью uwsgi и nginx)
необходимо создать файл определяющий наше wsgi-приложение.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;wsgi.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.handlers.wsgi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WSGIHandler&lt;/span&gt;

&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;settings&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WSGIHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Следующее, что необходимо сделать это поправить &lt;cite&gt;settings.py&lt;/cite&gt;
изменив его следующим образом (включаем базу данных, интерфейс администрирования,
определяем путь к STATIC_ROOT и правим urlconf):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;DATABASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;ENGINE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;django.db.backends.sqlite3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;NAME&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;aws.db&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;
&lt;span class="n"&gt;DEPLOY_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;STATIC_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEPLOY_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;static&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;ROOT_URLCONF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;urls&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Uncomment the next line to enable the admin:&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;django.contrib.admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И последнее создадим фикстуру для создания первого пользователя &lt;cite&gt;initial_data.json&lt;/cite&gt;
(login: admin, pasword: admin):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;model&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;auth.user&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;pk&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;fields&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;username&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;admin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;first_name&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Darth&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;last_name&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Vader&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;is_active&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;is_superuser&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;is_staff&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;last_login&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2000-01-01 00:00:00&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;groups&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;user_permissions&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;sha1$bb19a$51b2bac8dd83c30e6cf6694bf3049241a14124ea&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;email&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;admin@admin.admin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;date_joined&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2000-01-01 00:00:00&amp;quot;&lt;/span&gt;
        &lt;span class="p"&gt;}}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Наш проект готов к деплою.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id16"&gt;Создание ключей для клонирования репозитория&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Данные действия выполняются на сервере&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Деплоить будем из нашего репозитория. Чтобы это возможность появилась,
надо или перенести свой приватный ключ на сервер или создать новый и дать
на него доступ в &lt;strong&gt;gitolite-admin&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Я предпочитаю второй вариант:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Переходим в нашу домашнюю .ssh директорию&lt;/span&gt;
$ &lt;span class="nb"&gt;cd&lt;/span&gt; ~/.ssh

&lt;span class="c1"&gt;# Создаем отдельный ключ для makesite (укажите makesite в имени файла)&lt;/span&gt;
&lt;span class="c1"&gt;# без паролей&lt;/span&gt;
$ ssh-keygen
  Generating public/private rsa key pair.
  Enter file in which to save the key &lt;span class="o"&gt;(&lt;/span&gt;/home/ubuntu/.ssh/id_rsa&lt;span class="o"&gt;)&lt;/span&gt;: makesite
  Enter passphrase &lt;span class="o"&gt;(&lt;/span&gt;empty &lt;span class="k"&gt;for&lt;/span&gt; no passphrase&lt;span class="o"&gt;)&lt;/span&gt;:
  Enter same passphrase again:
  Your identification has been saved in makesite.
  Your public key has been saved in makesite.pub.

&lt;span class="c1"&gt;# Копируем ключ в id_rsa&lt;/span&gt;
$ cp makesite id_rsa
$ cp makesite.pub id_rsa.pub
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Полученный ключ надо подключить к нашему репозиторию, путем редактирования &lt;cite&gt;gitolite_admin&lt;/cite&gt; (подробности в предыдущей статье). Вкратце ваш порядок действий:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Скопировать ключ &lt;cite&gt;~/.ssh/makesite.pub&lt;/cite&gt; в &lt;cite&gt;gitolite-admin/keydir/makesite.pub&lt;/cite&gt; (например при помощи &lt;strong&gt;scp&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Обновить параметры доступа к &lt;cite&gt;awsdemo&lt;/cite&gt; в файле &lt;cite&gt;gitolite-admin/conf/gitolite.conf&lt;/cite&gt; (добавить строчку &lt;cite&gt;RW+  makesite&lt;/cite&gt;)&lt;/li&gt;
&lt;li&gt;Сделать коммит с изменениями и пуш на сервер.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Теперь можно проверить работу с репозиторием на стороне сервера:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git clone git@awsdemo.us.to:awsdemo.git /tmp/aws
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В случае успеха репозиторий должен быть скопирован с удаленного сервера иначе вы, что-то неправильно сделали
с ключами.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id17"&gt;Установка зависимостей&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Данные действия выполняются на сервере&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Дадим команду &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; на деплой нашего проекта:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;makesite install awsdemo
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Помните мы указали строку &amp;quot;src = &lt;a class="reference external" href="mailto:git+git&amp;#64;awsdemo.us.to"&gt;git+git&amp;#64;awsdemo.us.to&lt;/a&gt;:%(project)s.git&amp;quot; в &lt;cite&gt;/var/www/makesite.ini&lt;/cite&gt;, она говорит &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; где получать исходники проекта. То есть вышеприведенная команда по факту транслируется следующим образом:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
makesite install awsdemo --src git+git&amp;#64;awsdemo.us.to:awsdemo.git
&lt;/pre&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; поддерживает установку из пути файловой системы,
или git, mercurial (hg+), subversion (svn+) систем контроля версий&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Вывод команды будет приблизительно таким:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Clone src: git+git&amp;#64;awsdemo.us.to:awsdemo.git
--------------------------------------------
Cmd: /usr/bin/git clone git&amp;#64;awsdemo.us.to:awsdemo.git /tmp/tmpkWznFx/source
remote: Counting objects: 12, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 12 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (12/12), done.
Resolving deltas: 100% (1/1), done.

Deploy templates: base,src-git,virtualenv,supervisor,nginx,uwsgi
----------------------------------------------------------------
Prepare template: base
Prepare template: src-git
Prepare template: virtualenv
Prepare template: supervisor
Prepare template: nginx
Prepare template: uwsgi

Check requirements
------------------
Cmd: sudo chmod +x /tmp/tmpkWznFx/service/*.sh
Cmd: /bin/bash /tmp/tmpkWznFx/service/base_check.sh
Cmd: /bin/bash /tmp/tmpkWznFx/service/virtualenv_check.sh
Error: Command 'virtualenv' not found!
Install python-virtualenv package

Command '/bin/bash /tmp/tmpkWznFx/service/virtualenv_check.sh' returned non-zero exit status 127
See log: /tmp/tmp8IAJYB
&lt;/pre&gt;
&lt;p&gt;Он говорит нам о том, что на стадии проверки зависимостей &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; прервал установку.
Команда &lt;cite&gt;virtualenv&lt;/cite&gt; не была найдена в системе.&lt;/p&gt;
&lt;p&gt;Мы можем поставить &lt;cite&gt;python-virtualenv&lt;/cite&gt; и повторить попытку, но споткнемся на &lt;cite&gt;nginx&lt;/cite&gt;, &lt;cite&gt;uwsgi&lt;/cite&gt; или других зависимостях. Поэтому поставим все сразу одной командой.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo apt-get install python-virtualenv nginx uwsgi uwsgi-plugin-python nginx supervisor
sudo /etc/init.d/nginx start
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id18"&gt;Установка и удаление проекта&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Данные действия выполняются на сервере&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Теперь повторим деплой нашего проекта:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ makesite install awsdemo
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Эта команда создаст следующую структуру директорий:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/var/www/master/
            awsdemo/
                |
                |-deploy/      # Файлы конфигурации
                |-logs/        # Логи nginx, supervisor, uwsgi
                |-media/       # Папка для загружаемых файлов
                |-service/     # Скрипты для обслуживания проекта
                |-source/      # Исходный код проекта
                |-static/      # Статика проекта
                |-.virtualenv/ # Виртуальное окружение проекта
                |-makesite.ini # Параметры проекта
                |-.makesite    # Список шаблонов проекта
&lt;/pre&gt;
&lt;p&gt;Сайт уже запущен и работает. &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; скачал исходники, развернул виртуальное окружение,
создал файлы конфигурации nginx, supervisor, uwsgi и подключил их, выполнил синхронизацию
базы данных, скопировал файлы статики, а также создал shell скрипты для обновления,
удаления и перезапуска проекта.&lt;/p&gt;
&lt;img alt="" src="/static/img/awsdemo2.png" /&gt;
&lt;p&gt;Для пробы удалим развернутый проект:&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;В bash, для большинства команд &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt;, работает автодополнение.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Указываем короткое имя проекта (ветка.проект)&lt;/span&gt;
$ makesite uninstall master.awsdemo

&lt;span class="c1"&gt;# Или можно указать полный путь&lt;/span&gt;
$ makesite uninstall /var/www/awsdemo/master
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Проект удален из системы, nginx и supervisor перезагружены. Теперь можно развернуть его
снова вышеприведенной командой.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id9"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id19"&gt;Обновление проекта&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Данные действия выполняются локально&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Изменим что-нибудь в нашем проекте, например добавим &lt;cite&gt;views.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Hello from makesite!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Подключим его в &lt;cite&gt;urls.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Examples:&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;r&amp;#39;^$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;views.home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Сохраним и отправим изменения на сервер. Теперь на сервере достаточно ввести
команду &lt;cite&gt;makesite update master.awsdemo&lt;/cite&gt;, чтобы наш сайт обновился. При этом новые
зависимости, миграции, статические файлы &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; обработает самостоятельно.&lt;/p&gt;
&lt;p&gt;В рамках данной статьи я планировал показать как создать свой хук на обновление репозитория
в gitolite, чтобы git push в удаленный репозиторий вызывал автоматическое обновление
соответствующих сайтов (по факту makesite update), но статья и так получилась достаточно длинной,
поэтому эта задача предлагается к выполнению в качестве домашнего задания.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Подробная статья про работу с &lt;a class="reference external" href="https://github.com/klen/makesite"&gt;Makesite&lt;/a&gt; находится в моих ближайших планах&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</summary><category term="hosting"></category><category term="deploy"></category><category term="makesite"></category><category term="django"></category><category term="uwsgi"></category><category term="nginx"></category></entry><entry><title>Настройка сервера. Gitolite — хостинг git-репозиториев</title><link href="http://klen.github.io/gitolite-setup.html" rel="alternate"></link><published>2011-11-18T00:00:00+04:00</published><updated>2011-11-18T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-11-18:gitolite-setup.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id7"&gt;Начало работы&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#gitolite" id="id8"&gt;Установка Gitolite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id9"&gt;Управление Gitolite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;В &lt;a class="reference external" href="../aws-ru.html"&gt;предыдущей&lt;/a&gt; статье я рассказал как создать свой бесплатный
(в течении года) сервер на облаке от &lt;a class="reference external" href="http://aws.amazon.com/"&gt;Amazon Web Services&lt;/a&gt;.
Пришла пора настроить на нем среду для удобной разработки.&lt;/p&gt;
&lt;p&gt;Ниже будет описано создание собственного &lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt; репозитория с помощью &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt;.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Все инструкции в этой статье приводятся для системы
&lt;strong&gt;Ubuntu Oneiric&lt;/strong&gt;, пользователь &lt;strong&gt;ubuntu&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Начало работы&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Первые мои команды на новом сервере:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Обновление системы (да мне нравятся свежие пакеты)&lt;/span&gt;
sudo apt-get update
sudo apt-get upgrade

&lt;span class="c1"&gt;# Утилиты для сборки&lt;/span&gt;
$ sudo apt-get install build-essential

&lt;span class="c1"&gt;# Конфигурирование локалей&lt;/span&gt;
$ sudo locale-gen ru_RU.UTF-8
$ sudo locale-gen en_US.UTF-8
$ sudo dpkg-reconfigure locales
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="gitolite"&gt;
&lt;h2&gt;Установка &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt; распределенная система контроля версий и не нуждается в хостинг-сервере.
Но в таком случае для доступа к репозиторию необходим доступ к файловой системе,
в которой он расположен. В противном случае для синхронизации (команды
разработчиков или кода на сервере) необходим &lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt; хостинг с сетевым доступом.&lt;/p&gt;
&lt;p&gt;Самые распространенные решения: &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt; и &lt;a class="reference external" href="https://github.com/res0nat0r/gitosis.git"&gt;Gitosis&lt;/a&gt;. Я предпочитаю &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt;
из-за удобства настройки и установки.&lt;/p&gt;
&lt;p&gt;Для начала установим &lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt;  и &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt; из пакетов:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Установка git и gitolite&lt;/span&gt;
$ sudo apt-get install git gitolite
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь нужно создать владельца репозиториев. Обычно используется пользователь
с логином: &lt;strong&gt;git&lt;/strong&gt;. Создадим его и выполним под ним настройку &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Создание пользователя git&lt;/span&gt;
$ sudo useradd -m git

&lt;span class="c1"&gt;# Копируем свой публичный ssh rsa ключ из домашней директории&lt;/span&gt;
&lt;span class="c1"&gt;# для настройки управления репозиторием&lt;/span&gt;
$ sudo cp ~/.ssh/id_rsa.pub /home/git/admin.pub

&lt;span class="c1"&gt;# Заходим под пользователем git и заканчиваем настройку&lt;/span&gt;
$ sudo su git
$ &lt;span class="nb"&gt;cd&lt;/span&gt;
$ gl-setup admin.pub
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В этот момент откроется &lt;a class="reference external" href="www.vim.org"&gt;Vim&lt;/a&gt; с настройками репозитория по-умолчанию. Сохраняя
спокойствие, нажмите &lt;cite&gt;:x&lt;/cite&gt; для сохранения и выхода из редактора. Нажмите &lt;strong&gt;Ctrl+D&lt;/strong&gt;
для окончания сеанса пользователя &lt;strong&gt;git&lt;/strong&gt;. Поздравляю первый этап настройки
закончен, можете закрыть соединение с сервером, дальнейшая настройка будет
произведена на вашей локальной машине.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;Управление &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Управление &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt; осуществляется с помощью специального репозитория
&lt;strong&gt;gitolite-admin&lt;/strong&gt; который уже создан у вас на сервере. Добавление репозиториев
и пользователей, изменения конфигурации производятся через стандартные &lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt;
операции (commit, push).&lt;/p&gt;
&lt;p&gt;Скачаем этот репозиторий на ваш локальный компьютер:&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Здесь, и далее, я работаю с адресом моей созданной на Amazon VPS:
&lt;cite&gt;awsdemo.us.to&lt;/cite&gt;. Для вас он будет отличаться. Как создать и прикрепить
свой домен было рассказано в прошлой статье.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# -- Ваша локальная машина --&lt;/span&gt;
&lt;span class="c1"&gt;# git clone git@&amp;lt;your_vps_domain&amp;gt;:gitolite-admin.git&lt;/span&gt;
git clone git@awsdemo.us.to:gitolite-admin.git
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если вы ничего не напутали с ключами и настройками, то &lt;strong&gt;gitolite-admin&lt;/strong&gt; будет
скопирован на ваш компьютер.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;strong&gt;gitolite-admin&lt;/strong&gt; включает в себя две папки. Папка &lt;cite&gt;keys&lt;/cite&gt; включает в
себя файлы с публичными частями rsa ключей пользователей репозитория.
Папка &lt;cite&gt;conf&lt;/cite&gt; содержит файлы конфигурации gitolite.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Посмотрим на файл &lt;cite&gt;conf/gitolite.conf&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;repo    gitolite-admin
        RW+     &lt;span class="o"&gt;=&lt;/span&gt;   admin

repo    testing
        RW+     &lt;span class="o"&gt;=&lt;/span&gt;   @all
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Он говорит нам о том, что на нашем хостинге доступно два репозитория:
&lt;strong&gt;gitolite-admin&lt;/strong&gt; и &lt;strong&gt;testing&lt;/strong&gt;, при чем к первому из них доступ на чтение
и запись имеет только пользователь с ключом сохраненным в файле
&lt;cite&gt;keys/admin.pub&lt;/cite&gt;, а ко второму все пользователи.&lt;/p&gt;
&lt;p&gt;Добавим еще одну запись для нашего будущего &lt;a class="reference external" href="www.djangoproject.org"&gt;Django&lt;/a&gt;-проекта, я назову его &lt;cite&gt;awsdemo&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;repo    gitolite-admin
        RW+     &lt;span class="o"&gt;=&lt;/span&gt;   admin

repo    testing
        RW+     &lt;span class="o"&gt;=&lt;/span&gt;   @all

repo    awsdemo
        RW+     &lt;span class="o"&gt;=&lt;/span&gt;   admin
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Сохраним изменения и отправим их на сервер:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git commit -am &lt;span class="s1"&gt;&amp;#39;Add django repo&amp;#39;&lt;/span&gt;
$ git push origin master
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь можно создавать сам проект и привязывать его к удаленному хостингу:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Создание директории для будущего проекта&lt;/span&gt;
$ mkdir -p ~/Projects/awsdemo
$ &lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/awsdemo

&lt;span class="c1"&gt;# Создание репозитория&lt;/span&gt;
$ git init

&lt;span class="c1"&gt;# Привязываем его к нашему хостингу&lt;/span&gt;
$ git remote add origin git@awsdemo.us.to:awsdemo.git

&lt;span class="c1"&gt;# Первый коммит&lt;/span&gt;
$ touch README
$ git add .
$ git commit -am &lt;span class="s2"&gt;&amp;quot;Initial commit&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Отправляем изменения на сервер&lt;/span&gt;
$ git push origin master

  Counting objects: 3, &lt;span class="k"&gt;done&lt;/span&gt;.
  Writing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;3/3&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="m"&gt;210&lt;/span&gt; bytes, &lt;span class="k"&gt;done&lt;/span&gt;.
  Total &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;delta 0&lt;span class="o"&gt;)&lt;/span&gt;, reused &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;delta 0&lt;span class="o"&gt;)&lt;/span&gt;
  To git@ec2-174-129-57-41.compute-1.amazonaws.com:django.git
    * &lt;span class="o"&gt;[&lt;/span&gt;new branch&lt;span class="o"&gt;]&lt;/span&gt;      master -&amp;gt; master
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Все, новый репозиторий создан и доступен для клонирования из &lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;Gitolite&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Продолжение: &lt;a class="reference external" href="../deploy-setup-ru.html"&gt;Настройка сервера. Создаем и разворачиваем django-проект&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
</summary><category term="hosting"></category><category term="git"></category><category term="gitolite"></category></entry><entry><title>Год облачного хостинга от Amazon — бесплатно</title><link href="http://klen.github.io/aws.html" rel="alternate"></link><published>2011-11-11T00:00:00+04:00</published><updated>2011-11-11T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-11-11:aws.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#aws" id="id7"&gt;Бесплатный период использования сервиса от AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id8"&gt;Регистрация на Amazon Web Services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#vps" id="id9"&gt;Создание и запуск VPS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#ip-dns" id="id10"&gt;Привязка IP и настройка DNS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id11"&gt;Подключение к серверу&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;img alt="" src="/static/img/aws.png" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;a class="reference external" href="AWS"&gt;Amazon Web Services&lt;/a&gt; инфраструктура Web Services платформы в облаке, представленная компанией &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Amazon"&gt;Amazon&lt;/a&gt; в начале 2006 года. В данной инфраструктуре представлено много сервисов для предоставления различных услуг, таких как: &lt;a class="reference external" href="EC2"&gt;Amazon EC2&lt;/a&gt; аренда виртуальных сервисов, &lt;a class="reference external" href="SIMPLEDB"&gt;Amazon SimpleDB&lt;/a&gt; облачная база данных, &lt;a class="reference external" href="S3"&gt;Amazon S3&lt;/a&gt; файловый хостинг и множество других.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Немногие знают, но компания &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Amazon"&gt;Amazon&lt;/a&gt; каждому зарегистрировавшемуся пользователю, фактически предоставляет полноценную бесплатную VPS на год. Многие из тех кто знает об этом
не используют это выгодное предложение так как боятся работать с &amp;quot;облаками&amp;quot;, считая их настройку чем-то сложным и недоступным.&lt;/p&gt;
&lt;p&gt;Облачный хостинг имеет ряд преимуществ перед прочими решениями:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Гибкая ценовая политика, вы платите только за то, что используете;&lt;/li&gt;
&lt;li&gt;Легкое масштабирование проектов;&lt;/li&gt;
&lt;li&gt;Отказоустойчивость.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В случае &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt;  к преимуществам добавляется:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Огромное количество доступных образов систем;&lt;/li&gt;
&lt;li&gt;Удобные интерфейсы управления (включая консольные и GUI утилиты);&lt;/li&gt;
&lt;li&gt;Автоматический load-ballancer;&lt;/li&gt;
&lt;li&gt;И еще множество полезнейших опций, перечислять которые я сейчас просто не возьмусь.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В данной статье мы создадим и запустим бесплатную VPS на &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Amazon"&gt;Amazon&lt;/a&gt; с выделенным IP-адресом и прикрепленным доменом. В &lt;a class="reference external" href="gitolite-setup-ru.html"&gt;следующих&lt;/a&gt; статьях я покажу как настроить на ней &lt;a class="reference external" href="http://git-scm.com/"&gt;Git&lt;/a&gt; хостинг для ваших репозиториев и поднять &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; проект с автообновлением кода.  Все вместе не должно занять у вас более нескольких часов.&lt;/p&gt;
&lt;p&gt;Итак поехали.&lt;/p&gt;
&lt;div class="section" id="aws"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Бесплатный период использования сервиса от AWS&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для новых зарегистрированных пользователей &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Amazon"&gt;Amazon&lt;/a&gt; предоставляет программу бесплатного использования
&lt;a class="reference external" href="http://aws.amazon.com/free/"&gt;AWS Free Usage Tier&lt;/a&gt; сроком на год. Вкратце она включает в себя:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;750 часов работы &lt;strong&gt;VPS t1.micro&lt;/strong&gt; в месяц (читаем 1 VPS в месяц бесплатно);&lt;/li&gt;
&lt;li&gt;10 ГБ гарантированного места бесплатно;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;На самом деле в программу входит несколько больше услуг и вы можете ознакомится с ними самостоятельно.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Регистрация на Amazon Web Services&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для использования &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt; вам нужен зарегистрированный, активный аккаунт.
Чтобы зарегистрироваться вам потребуются:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Кредитная карточка Visa или MasterCard (нужна только для проверки);&lt;/li&gt;
&lt;li&gt;Работающие email и мобильный номер для подтверждения.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ссылка для регистрации: &lt;a class="reference external" href="https://aws-portal.amazon.com/gp/aws/developer/registration/index.html"&gt;https://aws-portal.amazon.com/gp/aws/developer/registration/index.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Я лишь коротко опишу этот процесс:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;На первых шагах вы создадите аккаунт на &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt;, введя свои электронную почту, пароль и адрес;&lt;/li&gt;
&lt;li&gt;После создания и подтверждения аккаунта пройдите по ссылке: &lt;a class="reference external" href="https://aws-portal.amazon.com/gp/aws/developer/subscription/index.html?productCode=AmazonEC2"&gt;https://aws-portal.amazon.com/gp/aws/developer/subscription/index.html?productCode=AmazonEC2&lt;/a&gt;
и подпишитесь на услуги &lt;a class="reference external" href="http://aws.amazon.com/ec2/"&gt;EC2&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;В процессе подписки вас попросят ввести данные кредитной карты (с нее будет произведено пробное списание, с возвратом)
и пройти верификацию вашего мобильного телефона (вам позвонит робот и попросит ввести код подтверждения в режиме тонального набора)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Вы можете спокойно подписываться на все услуги &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt;. Это облачный хостинг и деньги с вас снимут только по факту реального использования услуг.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Если все прошло успешно вы можете пройти в консоль управления вашими сервисами от &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Amazon"&gt;Amazon&lt;/a&gt;: &lt;a class="reference external" href="https://console.aws.amazon.com/ec2/"&gt;https://console.aws.amazon.com/ec2/&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="vps"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Создание и запуск VPS&lt;/a&gt;&lt;/h2&gt;
&lt;img alt="" src="/static/img/aws_ec2.png" /&gt;
&lt;p&gt;В левом верхнем углу вы видите меню выбора региона для вашей будущей системы.
В левом нижнем углу &lt;tt class="docutils literal"&gt;Key Pairs&lt;/tt&gt;: меню управления вашими ключами доступа.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;У вас ведь есть своя пара rsa ssh ключей? Если да, то рекомендую выбрать &lt;tt class="docutils literal"&gt;Key Pairs&lt;/tt&gt;
и нажав кнопку &lt;tt class="docutils literal"&gt;Import Key Pair&lt;/tt&gt; загрузить публичную часть вашего ключа на &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;В центре находится кнопка создания и запуска сервера &lt;tt class="docutils literal"&gt;Launch Instance&lt;/tt&gt;.  После ее нажатия
необходимо будет выбрать образ вашего будущего сервера из доступных,
но сейчас я предлагаю поступить иначе.&lt;/p&gt;
&lt;p&gt;Я буду устанавливать &lt;a class="reference external" href="http://ubuntu.com"&gt;Ubuntu&lt;/a&gt; и воспользуюсь для этого замечательным сайтом: &lt;a class="reference external" href="http://alestic.com/"&gt;http://alestic.com/&lt;/a&gt;.
Образы доступные на нем уже оптимизированы под &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="" src="/static/img/images.png" /&gt;
&lt;p&gt;Выбираем из табов регион для создания вашей системы. Выбираем образ &lt;strong&gt;с загрузкой по EBS&lt;/strong&gt;
(это гарантирует вам сохранность данных если сервер по каким либо причинам будет остановлен)
и нажимаем на кнопку рядом с 32 или 64-битной версией для перехода в консоль &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Откроется окно с выбранной системой, нажимайте кнопку &lt;tt class="docutils literal"&gt;&amp;lt;Continue&amp;gt;&lt;/tt&gt; (обратите внимание, что для
бесплатного периода &lt;tt class="docutils literal"&gt;Instance type&lt;/tt&gt; вашей системы должен быть &lt;tt class="docutils literal"&gt;t1.micro&lt;/tt&gt;, он выбран по умолчанию).&lt;/p&gt;
&lt;p&gt;Когда вы дойдете до этапа создания ключей (&lt;tt class="docutils literal"&gt;Create Key Pair&lt;/tt&gt;) вы сможете выбрать ключ из уже
загруженных ранее или создать новый. Во втором случае сохраните полученный ключ на свой компьютер.&lt;/p&gt;
&lt;p&gt;Следующий этап &lt;tt class="docutils literal"&gt;Configure Firewall&lt;/tt&gt; управление политиками безопасности.  Политика безопасности
(Security Group) регламентирует какие порты и типы соединений открыты наружу на вашем сервере.&lt;/p&gt;
&lt;p&gt;Создайте новую группу и откройте порты TCP 22, 80 для доступа по SSH и WWW.&lt;/p&gt;
&lt;img alt="" src="/static/img/aws_group.png" /&gt;
&lt;p&gt;Заканчивайте настройку и запускайте ваш сервер: &lt;tt class="docutils literal"&gt;Launch&lt;/tt&gt;.&lt;/p&gt;
&lt;img alt="" src="/static/img/aws_instance.png" /&gt;
&lt;p&gt;Запуск займет некоторое время в течении которого статус сервера изменится на &lt;tt class="docutils literal"&gt;running&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ip-dns"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Привязка IP и настройка DNS&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;При выделении сервера в нижней части страницы показывается область информации,
в том числе и &lt;strong&gt;DNS&lt;/strong&gt; адрес.&lt;/p&gt;
&lt;p&gt;Адрес по-умолчанию не слишком удобен для работы, например &lt;cite&gt;ec2-184-72-89-43.compute-1.amazonaws.com&lt;/cite&gt;, поэтому далее мы привяжем более удобную DNS запись.&lt;/p&gt;
&lt;p&gt;Для начала нужно привязать к нашей VPS постоянный IP адрес. Откройте в вашей консоли пункт меню &lt;tt class="docutils literal"&gt;Elastic IPs&lt;/tt&gt; и нажмите кнопку &lt;tt class="docutils literal"&gt;Allocate New Address&lt;/tt&gt; для выделения вашим серверам нового постоянного IP-адреса.&lt;/p&gt;
&lt;img alt="" src="/static/img/aws-ip.png" /&gt;
&lt;p&gt;После создания адреса, его необходимо привязать к нашему серверу. Нажимаем &lt;tt class="docutils literal"&gt;Associate Address&lt;/tt&gt; в появившемся окне, в выпадающем списке, выбираем нашу VPS и подтверждаем привязку. Все, теперь наш сервер доступен по данному IP и мы можем привязать к нему DNS.&lt;/p&gt;
&lt;p&gt;Если у вас уже есть свой домен, то вы можете сразу прописать A-запись для связывания его с полученным выше IP-адресом. Если нет, то я предлагаю получить бесплатный, с помощью сервиса &lt;a class="reference external" href="http://freedns.afraid.org/"&gt;FreeDNS&lt;/a&gt;. Вообще, это обычный DNS-провайдер продающий регистрацию доменов и услуги по их сопровождению, но у его клиентов есть уникальная возможность расшаривать поддомены третьего уровня для бесплатного использования всеми желающими. Нет никаких гарантий, что клиент не остановит обслуживание корневого домена второго уровня, но например &lt;cite&gt;mooo.com&lt;/cite&gt; существует с 2001 года. Вкратце, данный сервис позволит вам получить бесплатный домен третьего уровня с бесплатным хостингом DNS. Например: для данной статьи я создам &lt;cite&gt;awsdemo.us.to&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;Итак, регистрируйтесь на &lt;a class="reference external" href="http://freedns.afraid.org/"&gt;FreeDNS&lt;/a&gt;, заходите в консоль управления на вкладку &lt;tt class="docutils literal"&gt;Subdomains&lt;/tt&gt; и нажимайте добавить.&lt;/p&gt;
&lt;img alt="" src="/static/img/aws-dns.png" /&gt;
&lt;p&gt;В появившемся окне указываем имя нашего субдомена, выбираем корневой домен, вписываем IP-адрес, полученный от &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Amazon"&gt;Amazon&lt;/a&gt;, и нажимаем сохранить. Поздравляю, у вашего сервера появился бесплатный домен.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Подключение к серверу&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;После запуска мы можем подключиться с помощью SSH к нашему серверу &lt;cite&gt;awsdemo.us.to&lt;/cite&gt;.
Учтите, что пользователь по умолчанию на образах с &lt;a class="reference external" href="http://alestic.com/"&gt;http://alestic.com/&lt;/a&gt; — &lt;strong&gt;ubuntu&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Если вы предварительно загрузили свои ключи:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh ubuntu@awsdemo.us.to
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Или с использованием полученного ключа от &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Amazon"&gt;Amazon&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# (Для ключа с именем test.pem)&lt;/span&gt;
&lt;span class="c1"&gt;# Устанавливаем необхожимые права&lt;/span&gt;
chmod &lt;span class="m"&gt;400&lt;/span&gt; test.pem

&lt;span class="c1"&gt;# Подключаемся с полученным ключом&lt;/span&gt;
ssh -i test.pem ubuntu@awsdemo.us.to
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В случае успеха вы должны попасть в терминал вашего запущенного сервера, поздравляю!&lt;/p&gt;
&lt;img alt="" src="/static/img/aws_done.png" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Обязательно загляните в замечательную справку &lt;a class="reference external" href="http://aws.amazon.com/"&gt;AWS&lt;/a&gt;: &lt;a class="reference external" href="http://aws.amazon.com/documentation/ec2/"&gt;http://aws.amazon.com/documentation/ec2/&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Продолжение: &lt;a class="reference external" href="../gitolite-setup-ru.html"&gt;Настройка сервера. Gitolite — хостинг git-репозиториев&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
</summary><category term="aws"></category><category term="hosting"></category><category term="cloud"></category><category term="ec2"></category><category term="freedns"></category></entry><entry><title>VIM плагин для комфортной работы с python</title><link href="http://klen.github.io/python-mode.html" rel="alternate"></link><published>2011-10-10T00:00:00+04:00</published><updated>2011-10-10T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-10-10:python-mode.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#python-mode-screencast" id="id2"&gt;Python-mode screencast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;В своей работе я использую редактор &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt;. Я довольно долго шел к этому выбору,
перепробовав множество решений. Мне сразу не понравились большие &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%B0%D1%8F_%D1%81%D1%80%D0%B5%D0%B4%D0%B0_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8"&gt;IDE&lt;/a&gt;, вроде
&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Eclipse_(%D1%81%D1%80%D0%B5%D0%B4%D0%B0_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8)"&gt;Eclipse&lt;/a&gt; (и основанных на нем систем) или &lt;a class="reference external" href="http://www.jetbrains.com/idea/"&gt;IDEA&lt;/a&gt;. Во-первых это &lt;a class="reference external" href="http://www.java.org"&gt;JAVA&lt;/a&gt; и медлительность,
хотя в последнее время ситуация улучшается. Во-вторых сложность самостоятельного
расширения функциональности. Меня почти устраивали &lt;a class="reference external" href="http://macromates.com/"&gt;Textmate&lt;/a&gt; (osx) и его аналог
&lt;a class="reference external" href="http://e-texteditor.com/"&gt;E-texteditor&lt;/a&gt; (windows) с которыми я проработал продолжительное время, но так
случилось, что судьба свела меня с &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt; :)&lt;/p&gt;
&lt;p&gt;Не буду разводить холивар и расписывать все его достоинства, опишу лишь основные
преимущества сыгравшие для меня важнейшую роль.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Легкая расширяемость. Расширения кроме несложного встроенного vimlang, можно
писать практически на чем угодно: python, ruby, tcl, shell.&lt;/li&gt;
&lt;li&gt;Независимость от настроенной рабочей системы. Дайте мне терминал и я в пару команд
получу на нем работающий &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt; с моими настройками и плагинами. Именно поэтому
VIM-еры работают на удаленных терминалах также комфортно как и на локальной
машине.&lt;/li&gt;
&lt;li&gt;Мощность. При казалось бы простоте и аскетизме &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt; очень гибко настраивается.
Я еще не встречал функциональности в &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%B0%D1%8F_%D1%81%D1%80%D0%B5%D0%B4%D0%B0_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8"&gt;IDE&lt;/a&gt; которой бы не мог при желании
получить в &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VIM-modes&lt;/strong&gt;. Да именно инопланетный режим работы &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt; стал для меня наибольшим
плюсом. У меня нет мышки, ни дома, ни на работе. Пальцы всегда на буквенном
блоке клавиатуры. Это сложно объяснить, но когда к этому привыкаешь
становится трудно работать по другому.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ну, а самый большой минус &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt; конечно же — кривая вхождения. С этим редактором
надо съесть пуд соли, но если вы подружитесь он оправдает все ожидания.&lt;/p&gt;
&lt;div class="section" id="python-mode-screencast"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id2"&gt;Python-mode screencast&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для работы с python я написал отдельный плагин. Его главное достоинство в том,
что он не требует установки в систему используемых python библиотек (&lt;a class="reference external" href="http://pypi.python.org/pypi/pylint"&gt;Pylint&lt;/a&gt;,
&lt;a class="reference external" href="http://pypi.python.org/pypi/pyflakes"&gt;Pyflakes&lt;/a&gt; и &lt;a class="reference external" href="http://rope.sourceforge.net/"&gt;Rope&lt;/a&gt;). Вот его основные возможности:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Вывод python-документации в отдельном окне;&lt;/li&gt;
&lt;li&gt;Запуск текущего кода и вывод результата в отдельное окно;&lt;/li&gt;
&lt;li&gt;Проверка кода библиотекой &lt;a class="reference external" href="http://pypi.python.org/pypi/pylint"&gt;Pylint&lt;/a&gt; или &lt;a class="reference external" href="http://pypi.python.org/pypi/pyflakes"&gt;Pyflakes&lt;/a&gt; (автоматически или вручную);&lt;/li&gt;
&lt;li&gt;Умное автодополнение, переход к определениям объектов, рефакторинг и множество
других фич библиотеки &lt;a class="reference external" href="http://rope.sourceforge.net/"&gt;Rope&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Автоматическая поддержка &lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv"&gt;Virtualenv&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;И еще несколько полезных мелочей для python.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Чтобы продемонстрировать работу плагина я снял небольшой скринкаст (извиняюсь за
качество, это мой первый опыт). Итак &lt;strong&gt;Python-mode&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="youtube" align="center"&gt;&lt;iframe width="420" height="315" src="https://www.youtube.com/embed/67OZNp9Z0CQ" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;p&gt;Скачать плагин можно с Github: &lt;a class="reference external" href="https://github.com/klen/python-mode"&gt;https://github.com/klen/python-mode&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Также там можно посмотреть мою конфигурацию этого замечательного редактора:
&lt;a class="reference external" href="https://github.com/klen/.vim"&gt;https://github.com/klen/.vim&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Если вы заинтересовались &lt;a class="reference external" href="http://www.vim.org"&gt;VIM&lt;/a&gt; и хотите узнать о нем побольше, советую посмотреть
к примеру эти скринкасты:&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.derekwyatt.org/vim/vim-tutorial-videos/vim-novice-tutorial-videos/"&gt;http://www.derekwyatt.org/vim/vim-tutorial-videos/vim-novice-tutorial-videos/&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
</summary><category term="vim"></category><category term="python"></category></entry><entry><title>Urbanairship, push-уведомления для мобильных приложений</title><link href="http://klen.github.io/urbanairship.html" rel="alternate"></link><published>2011-07-16T00:00:00+04:00</published><updated>2011-07-16T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-16:urbanairship.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id7"&gt;Регистрация и создание приложения&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#push" id="id8"&gt;Отправка push-уведомлений&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id9"&gt;Заключение&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Статью про самостоятельную реализацию IOS Push смотрите &lt;a class="reference external" href="../ios-push-python-ru.html"&gt;здесь&lt;/a&gt;.
Самостоятельная серверная реализация Android Push рассматривается &lt;a class="reference external" href="../android-push-python-ru.html"&gt;тут&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Разрабатывая мобильные приложения рано или поздно сталкиваешься с
необходимостью оперативного оповещения клиентов о происходящих на сервере
изменениях. Клиентское приложение иногда связывается с сервером и получает
данные которые его ожидают. Но что делать если приложение не соединено с
сервером или выключено, а оповестить о событиях необходимо?&lt;/p&gt;
&lt;p&gt;Для этого практически для всех популярных мобильных платформ (ios, android,
blackberry) компаниями-разработчиками реализована функциональность
Push-уведомлений.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Push-уведомления&lt;/strong&gt; — сообщения которые сервер отправляет
клиенту для информирования его о новых данных. Например это может быть
новое сообщение от знакомого, изменение статуса заказа, появление определенного
товара и так далее.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Для веб-приложений вариант реализации push-уведомлений описывается в статье:
&lt;a class="reference external" href="http://klen.github.com/tornadio_socket-io-ru.html"&gt;Создание сервера оповещений с использованием Tornado и Socket.IO&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Практически для каждого типа мобильных устройств данный функционал необходимо
реализовывать отдельно, поэтому для своего последнего проекта я решил
воспользовался специальным сервисом &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Данный сервис позволяет пользоваться едиными API для отправки уведомлений на
apple, android, blackberry устройства. Позволяет вести статистику и пользоваться
отладочной консолью. Без сложностей переключаться между боевым и разработческим
режимами отправки. И самое главное демократичной ценовой политикой
&lt;a class="reference external" href="https://urbanairship.com/pricing/"&gt;https://urbanairship.com/pricing/&lt;/a&gt;. Бесплатное ограничение в &lt;strong&gt;миллион
уведомлений в месяц&lt;/strong&gt; покроет большинство небольших проектов, а если ваше
приложение перерастет этот порог то $0.001 за уведомление (2.8 копейки) при
превышении не должно составить трудностей и это значительно дешевле SMS-сообщений.&lt;/p&gt;
&lt;p&gt;В данной статье рассматривается работа c &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt; на примере iPhone
push-уведомлений.&lt;/p&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Регистрация и создание приложения&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Регистрируемся на &lt;a class="reference external" href="http://urbanairship.com"&gt;http://urbanairship.com&lt;/a&gt;, при регистрации вас попросят
указать адрес для платежей и действующие данные кредитной карты.&lt;/li&gt;
&lt;li&gt;Перед созданием приложения на &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt; нам необходимо получить
push SSL сертификат. Идем на
&lt;a class="reference external" href="https://developer.apple.com/ios/manage/overview/index.action"&gt;iOS Provisioning Portal&lt;/a&gt;
и получаем &lt;strong&gt;Development&lt;/strong&gt; и &lt;strong&gt;Production Push SSL Certificate&lt;/strong&gt; в формате
&lt;cite&gt;*.cer&lt;/cite&gt;. Получив их на любое Apple Mac устройство конвертируйте в
Personal Information Exchange (.p12) формат.&lt;/li&gt;
&lt;li&gt;Теперь мы готовы зарегистрировать наше приложение на &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt;. Идем по
адресу &lt;a class="reference external" href="https://go.urbanairship.com/apps/"&gt;https://go.urbanairship.com/apps/&lt;/a&gt; и создаем два новых приложения
( example-production, example-development ) выбирая соответственно Production
или Development режимы и загружая соответствующие сертификаты.
Для каждого из режимов записываем &lt;cite&gt;Application Key&lt;/cite&gt; и
&lt;cite&gt;Application Master Secret&lt;/cite&gt; они нам понадобятся для работы с API.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Рекомендуется создавать по отдельному приложению для production и
development режимов.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="push"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Отправка push-уведомлений&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Реализуем с помощью python механизм push-уведомлений для нашего приложения.
Прежде всего рассмотрим каким образом это все реализовано на примере IOS
устройств.&lt;/p&gt;
&lt;p&gt;Для работы службы уведомлений требуется IOS 3.0 или выше, подключение к
Интернету и поддержка в вашей мобильной программе. Чтобы получать уведомления
от программы, ее необходимо открыть как минимум один раз. Когда IOS устройство
находится в активном режиме (экран включен) прием уведомлений осуществляется в
любое время, в противном случае через каждые 15 минут.&lt;/p&gt;
&lt;p&gt;Для отправки уведомления на устройство сервер должен знать &lt;strong&gt;Device token&lt;/strong&gt;
который приложение получает при регистрации в &lt;strong&gt;Apple Push Notification
Service&lt;/strong&gt;. В уведомлениях можно использовать звуки, значки, текстовые сообщения.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;a class="reference external" href="http://urbanairship.com/docs/index.html"&gt;Urbanairship push documentation&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Резюмируя:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Приложение должно зарегистрироваться в Apple и получить &lt;strong&gt;Device Token&lt;/strong&gt;;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Приложение должно сообщить &lt;strong&gt;Device Token&lt;/strong&gt; серверу (например&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;http-заголовком);&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Для отправки уведомления сервер делает http запрос к API &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt;,
с JSON данными.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Реализация на python (django) может выглядеть следующим образом:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlencode&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URLError&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simplejson&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;celery.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;

&lt;span class="nd"&gt;@task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ignore_result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_retry_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_ios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Send IOS push notification&lt;/span&gt;
&lt;span class="sd"&gt;        with urbanairship service.&lt;/span&gt;
&lt;span class="sd"&gt;        Celery async task.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;device_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URBANSHIP_PUSH_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;simplejson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;device_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;device_token&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="n"&gt;aps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alert&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s1"&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Basic &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URBANSHIP_APP_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URBANSHIP_MASTER_SECRET&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                    &lt;span class="s1"&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;URLError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;_send_ios&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;urbanairship server is unresponsible, cannot send ios notification&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_send_ios&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;В данном примере я использовал &lt;a class="reference external" href="http://celeryproject.org/"&gt;Celery&lt;/a&gt; для выполнения асинхронного оповещения.
Если на вашем проекте очереди задач не используются уберите декоратор &lt;cite&gt;task&lt;/cite&gt; и
обвязку кода отправки в &lt;cite&gt;try ... except&lt;/cite&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;В настройках проекта мы должны указать ключи: &lt;strong&gt;'URBANSHIP_PUSH_URL,
URBANSHIP_APP_KEY, URBANSHIP_MASTER_SECRET'&lt;/strong&gt; полученные нами ранее. В своей
разработке я стартую продакшен и дев сервера с разными настройками,
соответственно с разными ключами для &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Обратите внимание, что для использования этой функции нам необходимо знать
&lt;strong&gt;device_token&lt;/strong&gt;, который нам должно сообщить приложение.&lt;/p&gt;
&lt;p&gt;Использование:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# simple message&lt;/span&gt;
&lt;span class="n"&gt;send_ios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;FAKEDEVICETOKEN&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# message with sound&lt;/span&gt;
&lt;span class="n"&gt;send_ios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Beep&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;FAKEDEVICETOKEN&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# message with data&lt;/span&gt;
&lt;span class="n"&gt;send_ios&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Some data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;updated&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;sound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;FAKEDEVICETOKEN&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если отправка не происходит откройте приложение в &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt; в режиме
просмотра и посмотрите сообщения в консоли (для отладки должен быть
активирован чекбокс debug в настройках (production, development) приложения).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Мы рассмотрели push-уведомления для IOS устройств, но фактически уже имеем
поддержку android или blackberry. Необходимо только изменять JSON данные в
запросе к API &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Мой опыт взаимодействия с &lt;a class="reference external" href="http://urbanairship.com/"&gt;Urbanairship&lt;/a&gt; на протяжении последних трех месяцев
исключительно позитивный.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://urbanairship.com/docs/index.html"&gt;Urbanairship push documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Introduction/Introduction.html"&gt;Apple Push Notification Service Programming Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://code.google.com/android/c2dm/"&gt;Android Cloud to Device Messaging Framework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
</summary><category term="json"></category><category term="push notification"></category><category term="python"></category><category term="apple"></category><category term="android"></category></entry><entry><title>Запуск django и других python проектов при помощи uwsgi+nginx</title><link href="http://klen.github.io/uwsgi-nginx.html" rel="alternate"></link><published>2011-07-15T00:00:00+04:00</published><updated>2011-07-15T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-15:uwsgi-nginx.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id7"&gt;Установка зависимостей&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id8"&gt;Конфигурирование и запуск&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id9"&gt;Запуск&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#django" id="id10"&gt;Настройка django-проекта&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id11"&gt;Управление&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Редакция от 11/11/2011&lt;/p&gt;
&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Статья описывает развертывание python-проектов на linux сервере с использованием
&lt;a class="reference external" href="http://nginx.net/"&gt;nginx&lt;/a&gt;, &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisor&lt;/a&gt;, &lt;a class="reference external" href="http://projects.unbit.it/uwsgi/"&gt;uwsgi&lt;/a&gt;. В интернете уже есть множество материалов на эту тему,
ниже находится небольшая систематизация моего опыта.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Практически в каждом веб-проекте используется легковесный веб-сервер (&lt;strong&gt;nginx&lt;/strong&gt;,
&lt;strong&gt;lighthttpd&lt;/strong&gt; и тд), как минимум для генерации статики. При этом довольно часто для
динамической части проекта применяют apache + модули. В один момент мне надоело
ставить nginx и apache одновременно и теперь я использую связку &lt;a class="reference external" href="http://nginx.net/"&gt;nginx&lt;/a&gt; + &lt;a class="reference external" href="http://projects.unbit.it/uwsgi/"&gt;uwsgi&lt;/a&gt;
из-за простоты развертывания, удобства обслуживания и быстродействия.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UWSGI&lt;/strong&gt; — быстрый, легко настраиваемый сервер для &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface"&gt;WSGI&lt;/a&gt; приложений, написанный на &lt;strong&gt;C&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Далее я покажу как настроить и запустить python &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface"&gt;WSGI&lt;/a&gt; приложение (в том числе
и с использованием &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt;).&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Сравнение производительности WSGI серверов:
&lt;a class="reference external" href="http://nichol.as/benchmark-of-python-web-servers"&gt;http://nichol.as/benchmark-of-python-web-servers&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Установка зависимостей&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для начала необходимо установить &lt;a class="reference external" href="http://nginx.net/"&gt;nginx&lt;/a&gt;, &lt;a class="reference external" href="http://projects.unbit.it/uwsgi/"&gt;uwsgi&lt;/a&gt;. Последние версии &lt;a class="reference external" href="http://nginx.net/"&gt;nginx&lt;/a&gt; уже
содержат поддержку &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface"&gt;WSGI&lt;/a&gt;, поэтому вручную собирать ничего не надо.&lt;/p&gt;
&lt;s&gt;Я предпочитаю устанавливать &lt;b&gt;uwsgi&lt;/b&gt; с помощью &lt;b&gt;pip&lt;/b&gt;.&lt;/s&gt;&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Рассмотрено для debian-based дистрибутивов. Права суперпользователя.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# nginx и uwsgi устанавливаем прямо из репозиториев&lt;/span&gt;
&lt;span class="c1"&gt;# для последних версий uwsgi неоюходимо дополнительно&lt;/span&gt;
&lt;span class="c1"&gt;# установить пакет: uwsgi-plugin-python&lt;/span&gt;
apt-get install nginx uwsgi uwsgi-plugin-python
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Для управления &lt;a class="reference external" href="http://projects.unbit.it/uwsgi/"&gt;uwsgi&lt;/a&gt; и множеством других процессов, я использую &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisor&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;apt-get install supervisor
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Если вы получите ошибку &lt;strong&gt;ImportError&lt;/strong&gt; поставьте python-модуль
&lt;strong&gt;elementtree&lt;/strong&gt; и повторите установку &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisor&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Конфигурирование и запуск&lt;/a&gt;&lt;/h2&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Файлы с исходным кодом для данной статьи можно найти по адресу:
&lt;a class="reference external" href="https://github.com/klen/klen.github.com/tree/master/_code/uwsgi"&gt;https://github.com/klen/klen.github.com/tree/master/_code/uwsgi&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Предположим у нас есть python-проект расположенный состоящий
из очень простого &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface"&gt;WSGI&lt;/a&gt; приложения (&lt;cite&gt;wsgi.py&lt;/cite&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;200 OK&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Pong!&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;response_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-Length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)))]&lt;/span&gt;
    &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Здесь и далее предполагается, что корневой путь для проекта
&lt;cite&gt;/home/klen/Projects/klen.github.com/_code/uwsgi&lt;/cite&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Создадим конфигурационный файл с настройками &lt;a class="reference external" href="http://projects.unbit.it/uwsgi/"&gt;uwsgi&lt;/a&gt; для запуска нашего приложения: &lt;cite&gt;uwsgi.xml&lt;/cite&gt;&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;В данном примере рассмотрен XML формат, но можно использовать
INI или параметры строки запуска&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- UWSGI XML Configuration File --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;uwsgi&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Сокет для передачи данных --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;socket&amp;gt;&lt;/span&gt;/home/klen/Projects/klen.github.com/_code/uwsgi/uwsgi.sock&lt;span class="nt"&gt;&amp;lt;/socket&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Путь к виртуальному окружению --&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- &amp;lt;home&amp;gt;/home/klen/Projects/klen.github.com/_code/uwsgi/.virtualenv&amp;lt;/home&amp;gt; --&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Нам понадобится включенный python плагин --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;python&lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Дополнительный python-путь --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pythonpath&amp;gt;&lt;/span&gt;/home/klen/Projects/klen.github.com/_code/uwsgi&lt;span class="nt"&gt;&amp;lt;/pythonpath&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Модуль python с определением wsgi приложения --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;wsgi&lt;span class="nt"&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Количество процессов --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;processes&amp;gt;&lt;/span&gt;10&lt;span class="nt"&gt;&amp;lt;/processes&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Максимальное количество соединений для каждого процесса --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;max-requests&amp;gt;&lt;/span&gt;5000&lt;span class="nt"&gt;&amp;lt;/max-requests&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Максимальный размер запроса --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;buffer-size&amp;gt;&lt;/span&gt;32768&lt;span class="nt"&gt;&amp;lt;/buffer-size&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Убивать каждое соединение требующее больше 30 сек для обработки --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;harakiri&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/harakiri&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;reload-mercy&amp;gt;&lt;/span&gt;8&lt;span class="nt"&gt;&amp;lt;/reload-mercy&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;master&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;no-orphans&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/uwsgi&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Создадим конфигурационный файл с настройками для &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisor&lt;/a&gt;: &lt;cite&gt;supervisor.conf&lt;/cite&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[program:example]&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/uwsgi -x /home/klen/Projects/klen.github.com/_code/uwsgi/uwsgi.xml&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;www-data&lt;/span&gt;
&lt;span class="na"&gt;stdout_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/klen/Projects/klen.github.com/_code/uwsgi/logs/uwsgi.log&lt;/span&gt;
&lt;span class="na"&gt;stderr_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/klen/Projects/klen.github.com/_code/uwsgi/logs/uwsgi_err.log&lt;/span&gt;
&lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/klen/Projects/klen.github.com/_code/uwsgi&lt;/span&gt;
&lt;span class="na"&gt;autostart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;autorestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;redirect_stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;stopsignal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;QUIT&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Обычно я держу конфигурацию &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisor&lt;/a&gt; в проекте и создаю
на нее симлинки в &lt;cite&gt;/etc/supervisor/conf.d&lt;/cite&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Настроим &lt;strong&gt;server&lt;/strong&gt; в &lt;a class="reference external" href="http://nginx.net/"&gt;nginx&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;example.proxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;ip_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;unix:///home/klen/Projects/klen.github.com/_code/uwsgi/uwsgi.sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kn"&gt;listen&lt;/span&gt;      &lt;span class="mi"&gt;4444&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# В продакшен добавьте сюда свой домен&lt;/span&gt;
    &lt;span class="c1"&gt;# server_name example.com;&lt;/span&gt;

    &lt;span class="kn"&gt;access_log&lt;/span&gt;  &lt;span class="s"&gt;/home/klen/Projects/klen.github.com/_code/uwsgi/logs/nginx_access.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;error_log&lt;/span&gt;   &lt;span class="s"&gt;/home/klen/Projects/klen.github.com/_code/uwsgi/logs/nginx_error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Статика у нас будет хранится в папк static в корне проекта&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;^~&lt;/span&gt; &lt;span class="s"&gt;/static/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/var/www/example&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/favicon.ico&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;(.*)&lt;/span&gt; &lt;span class="s"&gt;/static/favicon.ico&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/robots.txt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;(.*)&lt;/span&gt; &lt;span class="s"&gt;/static/robots.txt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;uwsgi_pass&lt;/span&gt;  &lt;span class="s"&gt;example.proxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;include&lt;/span&gt;     &lt;span class="s"&gt;uwsgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Запуск&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Создадим ссылку на файлы конфигурации проекта для &lt;a class="reference external" href="http://nginx.net/"&gt;nginx&lt;/a&gt; и &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisor&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# создадим папку для логов&lt;/span&gt;
mkdir logs

&lt;span class="c1"&gt;# nginx&lt;/span&gt;
ln -s /home/klen/Projects/klen.github.com/_code/uwsgi/nginx.conf /etc/nginx/sites-enabled/uwsgi-example.conf

&lt;span class="c1"&gt;# supervisor&lt;/span&gt;
ln -s /home/klen/Projects/klen.github.com/_code/uwsgi/supervisor.conf /etc/supervisor/conf.d/uwsgi-example.conf
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Дадим права на запись всем пользователям:&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;По хорошему надо просто изменить владельца проекта на www-data.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;chmod a+w -R /home/klen/Projects/klen.github.com/_code/uwsgi
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Перезапустим &lt;a class="reference external" href="http://nginx.net/"&gt;nginx&lt;/a&gt; и &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisor&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/etc/init.d/nginx restart
/etc/init.d/supervisor stop
/etc/init.d/supervisor start
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если все прошло успешно, то перейдя по адресу: &lt;a class="reference external" href="http://localhost:4444/"&gt;http://localhost:4444/&lt;/a&gt;
вы должны увидеть наше работающее приложение.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="django"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Настройка django-проекта&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt; проекта вам необходимо просто изменить &lt;cite&gt;wsgi.py&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.handlers.wsgi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WSGIHandler&lt;/span&gt;


&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;DJANGO_SETTINGS_MODULE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;settings&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WSGIHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Предпологается, что проект находится в той же директории,
в противном случае добавьте необходимые пути в sys.path&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Управление&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Старт проекта&lt;/span&gt;
supervisorctl start example

&lt;span class="c1"&gt;# Перезапуск проекта&lt;/span&gt;
supervisorctl restart example

&lt;span class="c1"&gt;# Останов проекта&lt;/span&gt;
supervisorctl stop example

&lt;span class="c1"&gt;# Статус проекта&lt;/span&gt;
supervisorctl status example
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Также вы можете использовать параметр &lt;a class="reference external" href="http://projects.unbit.it/uwsgi/"&gt;uwsgi&lt;/a&gt; &lt;strong&gt;touch-reload&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Данный способ успешно применяется мной в боевой и девелоперской среде.
Например так работают сайты &lt;a class="reference external" href="http://intaxi.ru"&gt;http://intaxi.ru&lt;/a&gt; и &lt;a class="reference external" href="http://venturebrothers.ru"&gt;http://venturebrothers.ru&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="uwsgi"></category><category term="nginx"></category><category term="supervisor"></category><category term="django"></category></entry><entry><title>Django-markdown — поддержка Markdown в Django</title><link href="http://klen.github.io/git-markdown.html" rel="alternate"></link><published>2011-07-09T00:00:00+04:00</published><updated>2011-07-09T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-09:git-markdown.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id5"&gt;Установка&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id6"&gt;Подключение к проекту&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id7"&gt;Использование и настройка&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; (маркдаун) — облегчённый язык разметки. Первоначально создан
Джоном Грубером (англ. John Gruber) и Аароном Шварцем, целью которых
являлось создание максимально удобочитаемого и удобного в публикации
облегчённого языка разметки. Многие идеи языка были позаимствованы из
существующих соглашений по разметке текста в электронных письмах.
Реализации языка Markdown преобразуют текст в формате Markdown в валидный,
правильно построенный XHTML и заменяет левые угловые скобки («&amp;lt;») и
амперсанды («&amp;amp;») на соответствующие коды сущностей.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Иногда возникает необходимость использовать язык разметки для редактирования
текстов. HTML редактировать руками при набивании текстов никто не хочет да это
и не нужно, а вот какой нибудь wiki-синтаксис или &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; интуитивно понятны
программистам. Конечно секретаршу трудно обучить не GUI-евой разметке (хотя
возможно), но если проект предполагает нечастые обновления силами
квалифицированного персонала, &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; становится очень удобен.&lt;/p&gt;
&lt;p&gt;Для своих задач я написал &lt;a class="reference external" href="https://github.com/klen/django_markdown"&gt;Django-markdown&lt;/a&gt; — &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt; приложение позволяющее
быстро и несложно подключить &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; синтаксис на проект.&lt;/p&gt;
&lt;p&gt;Список возможностей:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Поддержка &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; синтаксиса в шаблонах;&lt;/li&gt;
&lt;li&gt;Поддержка в стандартных &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/contrib/flatpages/"&gt;Django-flatpages&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Поддержка в стандартном &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/"&gt;Django-admin&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Поддержка в формах и виджетах (wysiwyg).&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt="" src="/static/img/django-markdown.jpg" /&gt;
&lt;p&gt;Чаще всего я использую это приложение, когда на проекте есть &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/contrib/flatpages/"&gt;Django-flatpages&lt;/a&gt;
для удобства редактирования.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;a class="reference external" href="https://github.com/klen/django_markdown"&gt;Django-markdown&lt;/a&gt; предназначен для версии Django 1.3 и выше.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id5"&gt;Установка&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Приложение можно установить из &lt;a class="reference external" href="http://pypi.python.org"&gt;PyPi&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;easy_install django-markdown

&lt;span class="c1"&gt;# Или с помощью pip&lt;/span&gt;

pip install django-markdown
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id6"&gt;Подключение к проекту&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Необходимо добавить &lt;cite&gt;django_markdown&lt;/cite&gt; в &lt;strong&gt;INSTALLED_APPS&lt;/strong&gt;, settings.py:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;django_markdown&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И создать url в базовом url_config, urls.py:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^markdown/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;django_markdown.urls&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Подключать URL необходимо если вы хотите использовать функционал
предпросмотра в виджетах.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Использование и настройка&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Использование в формах:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django_markdown.widgets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MarkdownWidget&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyMarkdownForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MarkdownWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Просто подключаем виджет к нужному полю.&lt;/p&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;В стандартном &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/"&gt;Django-admin&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django_markdown.admin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MarkdownModelAdmin&lt;/span&gt;

&lt;span class="n"&gt;adimin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MarkdownModelAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Все поля типа TextField будут выведены с использованием &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; виджета&lt;/p&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="3"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/contrib/flatpages/"&gt;Django-flatpages&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# В вашем базовом url_config&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django_markdown&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;flatpages&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;URLS&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;

&lt;span class="c1"&gt;# Setup django admin and flatpages&lt;/span&gt;
&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autodiscover&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;flatpages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;r&amp;#39;^admin/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Мы импортируем flatpages из django_markdown вместо стандартного пути.&lt;/p&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="4"&gt;
&lt;li&gt;Настройка скинов wysiwyg (markitup по-умолчанию) settings.py:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;MARKDOWN_EDITOR_SKIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;simple&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Подробнее про настройку скинов и JS API читайте на сайте &lt;a class="reference external" href="http://markitup.jaysalvat.com/home/"&gt;Markitup&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Надеюсь это приложение и синтаксис будут вам полезны.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="markdown"></category><category term="django"></category></entry><entry><title>Настройка dnsmasq для локальной разработки (linux)</title><link href="http://klen.github.io/dnsmasq.html" rel="alternate"></link><published>2011-07-08T00:00:00+04:00</published><updated>2011-07-08T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-08:dnsmasq.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#dnsmasq" id="id4"&gt;Установка и использование Dnsmasq&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;В какой-то момент в процессе локальной разработки мне надоело постоянно
редактировать &lt;cite&gt;/etc/hosts&lt;/cite&gt; для тестирования очередного развернутого проекта.
Появилось желание настроить &amp;quot;wild local dns&amp;quot; — например чтобы все днс вида
*.dev.me вели на &lt;strong&gt;localhost&lt;/strong&gt;. К сожалению &lt;cite&gt;/etc/hosts&lt;/cite&gt; не поддерживает
записи вида *.bla.bla.bla. Поиск в интернете давал только ссылки на &lt;a class="reference external" href="http://www.bind9.net/manuals"&gt;BIND9&lt;/a&gt;,
настраивать который достаточно
&lt;a class="reference external" href="http://xgu.ru/wiki/%D0%9D%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0_DNS"&gt;сложно&lt;/a&gt;.
Но когда я уже был морально готов воевать с &lt;a class="reference external" href="http://www.bind9.net/manuals"&gt;BIND9&lt;/a&gt;, мне на глаза попался более
простой &lt;strong&gt;DNS&lt;/strong&gt; сервер — &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Dnsmasq"&gt;Dnsmasq&lt;/a&gt;. С его использованием задача сильно
упрощается, буквально до нескольких команд.&lt;/p&gt;
&lt;div class="section" id="dnsmasq"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id4"&gt;Установка и использование Dnsmasq&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;debian-based (вам понадобятся root права):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Устанавливаем dnsmasq&lt;/span&gt;
apt-get install dnsmasq

&lt;span class="c1"&gt;# Редактируем кнфигурацию dnsmasq&lt;/span&gt;
vim /etc/dnsmasq.conf
&lt;span class="c1"&gt;#   # Добавляем строку со своим &amp;quot;вируальным доменом для localhost&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#   address=/dev.me/127.0.0.1&lt;/span&gt;

&lt;span class="c1"&gt;# Перезапускаем сервис&lt;/span&gt;
/etc/init.d/dnsmasq restart

&lt;span class="c1"&gt;# Все, наши локальные поддомены готовы.&lt;/span&gt;
ping dev.me
ping something.dev.me
ping another.domain.dev.me
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Все работает!&lt;/p&gt;
&lt;/div&gt;
</summary><category term="linux"></category><category term="dnsmasq"></category></entry><entry><title>Создание сервера оповещений с использованием Tornado и Socket.IO</title><link href="http://klen.github.io/tornadio_socket-io.html" rel="alternate"></link><published>2011-07-06T00:00:00+04:00</published><updated>2011-07-06T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-06:tornadio_socket-io.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#tornadio" id="id8"&gt;Установка и настройка Tornadio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#tornado" id="id9"&gt;Создание приложения Tornado&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id10"&gt;Клиентская часть&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id11"&gt;Обработка подключений&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id12"&gt;Идентификация подключений&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id13"&gt;Использование&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Структура современных веб-приложений зачастую включает в себя некие &lt;strong&gt;HTTP API&lt;/strong&gt;
с которыми работают подключенные к серверу клиенты. Обычно для них важно
оперативно обновлять информацию о произошедших на сервере событиях.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Например оповещения о сообщениях в чатах, события в онлайн-играх,
поступление новых заказов, предложений в системах коммерции и тп.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;В данный момент на большинстве сайтов это реализуется постоянными &lt;strong&gt;AJAX&lt;/strong&gt;
запросами к серверу с клиентской стороны.  У этого способа существуют
недостатки:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Создается лишняя нагрузка на сервер;&lt;/li&gt;
&lt;li&gt;Информация может приходить с задержкой;&lt;/li&gt;
&lt;li&gt;Увеличивается трафик на клиенте.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В данной статье рассматривается способ с использованием &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/WebSocket"&gt;WebSocket&lt;/a&gt; и &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Comet"&gt;Comet&lt;/a&gt;
технологий. Мы создадим простейший сервер оповещений с использованием
&lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt; и &lt;a class="reference external" href="https://github.com/MrJoes/tornadio"&gt;Tornadio&lt;/a&gt;. С клиентской стороны будет создаваться постоянное
подключение к нашему серверу и в нужный момент мы будем посылать оповещения
о событиях.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt; — javascript библиотека предоставляющая единый интерфейс для
связи с &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/Comet"&gt;Comet&lt;/a&gt; сервером с использованием множества протоколов (&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/WebSocket"&gt;WebSocket&lt;/a&gt;,
Flashsocket, XHR multipart и тд.).&lt;/p&gt;
&lt;blockquote class="last"&gt;
&lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt; уже поставляется с модулем для &lt;strong&gt;nodejs&lt;/strong&gt;. С его помощью можно реализовать
необходимую функциональность на сервере используя &lt;a class="reference external" href="http://ru.wikipedia.org/wiki/JavaScript"&gt;JavaScript&lt;/a&gt;. В данной статье
рассматривается решение с использованием &lt;a class="reference external" href="http://python.org"&gt;Python&lt;/a&gt;.&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;&lt;a class="reference external" href="https://github.com/MrJoes/tornadio"&gt;Tornadio&lt;/a&gt; — python библиотека надстройка над &lt;a class="reference external" href="http://tornadoweb.org"&gt;Tornado&lt;/a&gt; для поддержки
интерфейса &lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Для дальнейшего чтения предполагается, что вы знакомы с &lt;a class="reference external" href="http://python.org"&gt;Python&lt;/a&gt;, &lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv"&gt;VirtualEnv&lt;/a&gt; и
&lt;a class="reference external" href="http://ru.wikipedia.org/wiki/JavaScript"&gt;JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tornadio"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Установка и настройка Tornadio&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Для начала создадим и активируем виртуальное окружение (&lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv"&gt;VirtualEnv&lt;/a&gt;) для нашего сервера:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Создаем директорию для сервера&lt;/span&gt;
mkdir -p ~/Projects/tornado_pushserver
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/tornado_pushserver

&lt;span class="c1"&gt;# Создаем виртульное окружение&lt;/span&gt;
virtualenv .ve

&lt;span class="c1"&gt;# Активируем виртуальное окружение&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; .ve/bin/activate
&lt;/pre&gt;&lt;/div&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Устанавливаем в созданное окружение &lt;a class="reference external" href="http://tornadoweb.org"&gt;Tornado&lt;/a&gt; и &lt;a class="reference external" href="https://github.com/MrJoes/tornadio"&gt;Tornadio&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;easy_install tornadio
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="tornado"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Создание приложения Tornado&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Напишем обработчик HTTP подключений к нашему серверу. Создадим и
отредактируем файл &lt;cite&gt;handler.py&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tornado.web&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BroadcastHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tornado&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello from tornadio!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Все, что он делает, это отдает строку 'Hello from tornadio!' при GET запросах.&lt;/p&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Создадим основной файл приложения &lt;cite&gt;app.py&lt;/cite&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tornado.web&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tornadio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;handler&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BroadcastHandler&lt;/span&gt;


&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;r&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BroadcastHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tornado&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SocketServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Здесь мы создали &lt;a class="reference external" href="http://tornadoweb.org"&gt;Tornado&lt;/a&gt; application использующее наш обработчик соединений.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Код для текущего состояния нашего сервера, вы можете посмотреть по адресу:
&lt;a class="reference external" href="https://github.com/klen/example_tornadio_project/tree/0.1.0"&gt;https://github.com/klen/example_tornadio_project/tree/0.1.0&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Запустите сервер с помощью команды &lt;cite&gt;python app.py&lt;/cite&gt; и откройте в браузере
&lt;a class="reference external" href="http://localhost:8001"&gt;http://localhost:8001&lt;/a&gt; вы должны увидеть ответ сервера. Если все в порядке
остановите его из консоли нажав Ctrl+C.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Клиентская часть&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Создайте страницу для клиентов &lt;cite&gt;console.html&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://cdn.socket.io/stable/socket.io.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;rememberTransport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;p&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;/p&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Console client&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Разберем, что в ней происходит. Подключается &lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt; скрипт реализации
протоколов. Создается объект &lt;cite&gt;socket&lt;/cite&gt; с параметрами подключения к нашему
серверу. Происходит подключение &lt;cite&gt;socket.connect&lt;/cite&gt;. И все полученные от сервера
сообщения выводятся в тело страницы.&lt;/p&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Теперь подключим этот шаблон на стороне сервера, изменив &lt;cite&gt;handler.py&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BroadcastHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tornado&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;console.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Код для текущего состояния нашего сервера, вы можете посмотреть по адресу:
&lt;a class="reference external" href="https://github.com/klen/example_tornadio_project/tree/0.2.0"&gt;https://github.com/klen/example_tornadio_project/tree/0.2.0&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Снова запустите сервер и подключитесь к нему в браузере: &lt;a class="reference external" href="http://localhost:8001"&gt;http://localhost:8001&lt;/a&gt;
Через какое то время в вы увидите ошибки в консоли. Они вызваны тем, что
протоколы &lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt; у нас сейчас никто не обрабатывает. Выключите сервер и
давайте создадим обработку подключений.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Обработка подключений&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Создайте файл &lt;cite&gt;connection.py&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tornadio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SocketConnection&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SocketConnection&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;client connected&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;client disconnected&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# **&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Здесь мы создали класс для наших подключений. При подключении он сохраняет
ссылку на себя в ClientConnection.client. При разрыве соединения удаляет.&lt;/p&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Подключим его в наше приложение &lt;cite&gt;app.py&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tornado.web&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tornadio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_router&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;connection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientConnection&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;handler&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BroadcastHandler&lt;/span&gt;


&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;r&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BroadcastHandler&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;get_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClientConnection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tornado&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SocketServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Сейчас мы создали ресурс &lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt; для клиентских подключений.&lt;/p&gt;
&lt;ol class="arabic simple" start="3"&gt;
&lt;li&gt;И доработаем на &lt;cite&gt;handler.py&lt;/cite&gt; для отправки сообщений подключенным клиентам:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tornado.web&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;connection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientConnection&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BroadcastHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tornado&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;console.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ClientConnection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message send.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Мы добавили обработчик POST запросов, отправляющий сообщения нашим подключениям.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Код для текущего состояния нашего сервера, вы можете посмотреть по адресу:
&lt;a class="reference external" href="https://github.com/klen/example_tornadio_project/tree/0.3.0"&gt;https://github.com/klen/example_tornadio_project/tree/0.3.0&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Запустите сервер и откройте несколько страниц по адресу &lt;a class="reference external" href="http://localhost:8001"&gt;http://localhost:8001&lt;/a&gt;
В консоли сервера вы должны увидеть сообщения о подключении. Давайте попробуем
отправить сообщения подключенным клиентам с помощью POST запросов к нашему
серверу.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;curl localhost:8001 -d &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Ping
curl localhost:8001 -d &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Another_ping
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Вы должны увидеть как сообщения появляются на открытых страницах. Сейчас самый
простой вариант нашего сервера сообщений уже работает. Добавим в него
возможность посылать сообщения конкретным клиентам. Остановите сервер.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id12"&gt;Идентификация подключений&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Есть несколько вариантов идентифицировать клиент. Например можно запрашивать
пользовательскую сессию если она существует. Сейчас мы просто будем сообщать
случайно генерированный ID серверу после подключения. Доработаем немного
&lt;cite&gt;console.html&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://cdn.socket.io/stable/socket.io.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;rememberTransport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// register client&lt;/span&gt;
            &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;connect&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;p&amp;gt;Connected.&amp;lt;/p&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;p&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;/p&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Console client&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Мы добавили реакцию на событие &lt;cite&gt;connect&lt;/cite&gt; и отправляем на сервер информацию о
нашем текущем ID.&lt;/p&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Добавим обработку идентификатора в наш класс подключений &lt;cite&gt;connection.py&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;tornadio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SocketConnection&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SocketConnection&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClientConnection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;client connected&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello client &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;client disconnected&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# **&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь при получении сообщения, мы регистрируем идентификатор клиента.&lt;/p&gt;
&lt;ol class="arabic simple" start="3"&gt;
&lt;li&gt;И добавим функциональности в &lt;cite&gt;handler.py&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tornado.web&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;connection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientConnection&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BroadcastHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tornado&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;console.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ClientConnection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message send.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Сейчас мы проверяем запрос на наличие параметра id и в этом случае отправляем
сообщение только конкретному подключенному клиенту.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Код для текущего состояния нашего сервера, вы можете посмотреть по адресу:
&lt;a class="reference external" href="https://github.com/klen/example_tornadio_project/tree/0.4.0"&gt;https://github.com/klen/example_tornadio_project/tree/0.4.0&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Запустите сервер и откройте несколько соединений в браузере. Вы должны увидеть
сообщения о подключении и ответы сервера с зарегистрированными ID:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Console client

Connected.

Hello client 63
&lt;/pre&gt;
&lt;p&gt;Отправим несколько сообщений:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;curl localhost:8001 -d &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Hello_all
curl localhost:8001 -d &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Hello_63&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;63
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Во-втором сообщении подставьте ID для одного из своих подключений&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Полученные сообщения должны отобразится в браузере. При чем первое для всех
подключенных соединений, а второе только для соединения с конкретным id.
Если все действительно так, то поздравляю ваш сервер сообщений работает.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id13"&gt;Использование&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Итак сервер мы написали, каким образом его можно использовать?
Например запустить на другом порту вашего основного
домена и подключаться к нему с клиентских страниц. При этом если на основном
сервере происходит какое то событие, делается &lt;strong&gt;POST&lt;/strong&gt; запрос (в идеале асинхронный)
к серверу оповещений который в свою очередь посылает сообщение клиенту.
В качестве сообщения можно отправлять &lt;strong&gt;JSON&lt;/strong&gt; с необходимой информацией.&lt;/p&gt;
&lt;p&gt;Но стоит учитывать, что данная статья является ознакомительной и
показывает создание &lt;strong&gt;простейшего&lt;/strong&gt;
сервера. В нем нет защиты от возможности злоумышленника рассылать собственные
оповещения, не используются ресурсы &lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt;, нет автоматических
пере-подключений с клиентской стороны при разрыве связи.&lt;/p&gt;
&lt;p&gt;Это несложно реализовать и вы можете доработать сервер самостоятельно.&lt;/p&gt;
&lt;p&gt;В заключении я рекомендую вам почитать документацию &lt;a class="reference external" href="http://socket.io/"&gt;SocketIO&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/MrJoes/tornadio"&gt;Tornadio&lt;/a&gt; и возможно &lt;a class="reference external" href="http://tornadoweb.org"&gt;Tornado&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="tornado"></category><category term="socketio"></category><category term="python"></category><category term="javascript"></category></entry><entry><title>Простейшая реализация протокола JSONP</title><link href="http://klen.github.io/simple-jsonp.html" rel="alternate"></link><published>2011-07-05T00:00:00+04:00</published><updated>2011-07-05T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-05:simple-jsonp.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id4"&gt;Пример в действии:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Когда в веб-проекте необходимы кроссдоменные &lt;a class="reference external" href="http://en.wikipedia.org/wiki/JSONP"&gt;JSONP&lt;/a&gt; запросы, а подключать
javascript фреймворки не хочется, можно воспользоваться этой простейшей
реализацией:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// simplest JSONP request implementation&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;jsonp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;jsonp&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;script&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;head&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/=\?/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;=&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Пример использования (получение информации о пользователе с использованием API twitter.com):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;http://api.twitter.com/1/statuses/user_timeline.json?screen_name=horneds&amp;amp;count=2&amp;amp;callback=?&amp;quot;&lt;/span&gt;

&lt;span class="nx"&gt;jsonp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id4"&gt;Пример в действии:&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Изменяйте значение поля ниже, на логин пользователя в твиттере, чтобы загрузить
изображение его профиля.&lt;/p&gt;
&lt;img src="#" id="img"/&gt;
&lt;br/&gt;
&lt;input value="horneds" onchange="get_image(this.value)" /&gt;
&lt;script&gt;
    // simplest JSONP request implementation
    function jsonp (url, callback) {
        var name = 'jsonp' +  + new Date,
            script = document.createElement('script'),
            head = document.getElementsByTagName('head')[0];
        window[name] = callback;
        script.src = url.replace(/=\?/, '=' + name);
        head.insertBefore(script, head.firstChild);
    }
    var link = "http://api.twitter.com/1/statuses/user_timeline.json?screen_name=horneds&amp;count=2&amp;callback=?"

    function my_callback( data ) {
        alert(data);
    }

    var image = document.getElementById('img');

    function get_image( value ) {
        var link = "http://api.twitter.com/1/statuses/user_timeline.json?screen_name=" + value + "&amp;count=2&amp;callback=?";
        jsonp(link, function (data) {
            image.src = data[0].user.profile_image_url;
        });
    }

    get_image( 'horneds' );
&lt;/script&gt;&lt;/div&gt;
</summary><category term="javascript"></category><category term="jsonp"></category></entry><entry><title>Django-gitrevision, автоматическое обновление статики на клиенте</title><link href="http://klen.github.io/git-revision.html" rel="alternate"></link><published>2011-07-04T00:00:00+04:00</published><updated>2011-07-04T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-04:git-revision.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id2" id="id5"&gt;Установка&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id6"&gt;Настройка и использование&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id7"&gt;Заключение&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;В современных веб-проектах, статические файлы (css, js ...) обычно жестко
кешируются с целью экономики трафика и для предупреждения лишней нагрузки на
сервер. В тоже время важно чтобы при обновлении статики, особенно JavaScript
файлов, старые клиенты получили обновленную версию.&lt;/p&gt;
&lt;p&gt;В моем проекте используется &lt;a class="reference external" href="http://git-scm.com"&gt;Git&lt;/a&gt; и &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt;. И вполне логично допустить, что
актуальная версия статики зависит от номера последнего коммита в &lt;a class="reference external" href="http://git-scm.com"&gt;Git&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Поэтому когда такая проблема возникла, я взял и написал очень простое
django-приложение для отслеживания текущего &lt;a class="reference external" href="http://git-scm.com"&gt;Git&lt;/a&gt; коммита в проекте.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;На текущий момент данное приложение неактуально тк я сделал &lt;a class="reference external" href="http://github.com/klen/dealer"&gt;http://github.com/klen/dealer&lt;/a&gt;
Последний пакет поддерживает несколько систем контроля версий включая git и mercurial.
И несколько python веб-фреймворков, а не только &lt;a class="reference external" href="http://django-project.com"&gt;Django&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id5"&gt;Установка&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Приложение можно установить из &lt;a class="reference external" href="http://pypi.python.org"&gt;PyPi&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;easy_install django-gitrevision

&lt;span class="c1"&gt;# Или с помощью pip&lt;/span&gt;

pip install django-gitrevision
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id6"&gt;Настройка и использование&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Необходимо добавить &lt;cite&gt;gitrevision&lt;/cite&gt; в &lt;strong&gt;INSTALLED_APPS&lt;/strong&gt;, settings.py:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gitrevision&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если вы предполагаете использовать &lt;cite&gt;gitrevision&lt;/cite&gt; шаблонах (для
обновления статики, этого достаточно) добавьте в settings.py:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gitrevision.context_processors.gitrevision&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И используйте переменную &lt;cite&gt;GIT_REVISION&lt;/cite&gt; в шаблонах:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/test.css?{{ GIT_REVISION }}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;media&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;screen&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/test.js?{{ GIT_REVISION }}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если идентификатор коммита нужен во views, хотя мне трудно представить такую
ситуацию, то подключите &lt;cite&gt;gitrevision&lt;/cite&gt; в MIDDLEWARE_CLASSES.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;MIDDLEWARE_CLASSES&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gitrevision.middleware.GitRevision&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Использование в views:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;superview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;gitrevision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git_revision&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В этом случае нет необходимости подключать CONTEXT_PROCESSOR так как в шаблонах
идентификатор будет доступен через &lt;cite&gt;request.git_revision&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Возможно вам понадобится добавить переменную с путем к вашему
репозиторию в settings:&lt;/p&gt;
&lt;div class="last"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;GIT_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;path_to_your_git_repository&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;Заключение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Приложение написано таким образом, что файловые операции происходят единожды
при компиляции проекта. Таким образом нагрузки на сервер не создается.&lt;/p&gt;
&lt;p&gt;Теперь в текущих проектах у меня не возникает головной боли по поводу
устаревшей версии статики на клиентах.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="git"></category><category term="django"></category></entry><entry><title>Создаем свой персональный сайт на Github.</title><link href="http://klen.github.io/github-blog.html" rel="alternate"></link><published>2011-07-03T00:00:00+04:00</published><updated>2011-07-03T00:00:00+04:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-07-03:github-blog.html</id><summary type="html">&lt;hr class="docutils" /&gt;
&lt;div class="contents topic" id="id1"&gt;
&lt;p class="topic-title first"&gt;Содержание:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id3" id="id8"&gt;Введение&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id4" id="id9"&gt;Использование генераторов статических сайтов&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#pelican" id="id10"&gt;Установка и настройка Pelican&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#id5" id="id11"&gt;Автоматизация&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Mало кто знает, что &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt; кроме превосходного хостинга ваших &lt;a class="reference external" href="http://git-scm.com"&gt;Git&lt;/a&gt; проектов
может также &lt;a class="reference external" href="http://pages.github.com/"&gt;хостить&lt;/a&gt; ваш персональный сайт.
Например на нем расположен этот блог. В своей первой статье я
расскажу как максимально удобно настроить эту функциональность.&lt;/p&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id8"&gt;Введение&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Для начала вам нужно быть зарегистрированным пользователем &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt; и уметь
работать с системой контроля версий &lt;a class="reference external" href="http://git-scm.com"&gt;Git&lt;/a&gt;. Предположим вы готовы.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Первое, что вам потребуется — это создать на &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt; репозиторий с именем вида: &lt;cite&gt;username.github.com&lt;/cite&gt;, где &lt;cite&gt;username&lt;/cite&gt; ваш логин на сервисе. Например для этого блога создан репозиторий &lt;a class="reference external" href="http://github.com/klen/klen.github.com"&gt;http://github.com/klen/klen.github.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Вторым шагом мы создадим локальный репозиторий и привяжем его к удаленному:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;В дальнейшем я буду приводить примеры для своего сайта: klen.github.com&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;mkdir ~/Projects/klen.github.com
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/klen.github.com
git init
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Hello world!&amp;#39;&lt;/span&gt; &amp;gt; index.html
git add .
git commit -m &lt;span class="s1"&gt;&amp;#39;Initial commit&amp;#39;&lt;/span&gt;
git remote add origin git@github.com:klen/klen.github.com.git
git push -u origin master
&lt;/pre&gt;&lt;/div&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Отлично, ваш статический сайт уже готов! В течении 10 минут он появится по адресу:&lt;/dt&gt;
&lt;dd&gt;&lt;cite&gt;username.github.com&lt;/cite&gt;. В дальнейшем он будет обновляться при коммитах в удаленный
репозиторий.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id9"&gt;Использование генераторов статических сайтов&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Созданный нами сайт не слишком удобен для работы, трудно писать содержание
используя HTML, сложно поддерживать целостность ссылок. Использовать его
например как блог очень затруднительно.&lt;/p&gt;
&lt;p&gt;Существует масса проектов генерации статических сайтов и блогов.
При работе с ними фактически вы пишете страницы и статьи в удобном для вас формате,
а затем генератор обновляет структуру сайта. По-умолчанию &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt; уже поддерживает
написанный на ruby генератор &lt;a class="reference external" href="http://github.com/mojombo/jekyll/"&gt;Jekyll&lt;/a&gt;. То есть вам необязательно использовать только
HTML синтаксис, из коробки вы можете писать в ваш сайт на &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt;. Подробнее читайте
в документации &lt;a class="reference external" href="http://pages.github.com/"&gt;Github Pages&lt;/a&gt; и &lt;a class="reference external" href="http://github.com/mojombo/jekyll/"&gt;Jekyll&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Я предпочитаю генерировать страницы локально и проверять результат без выгрузки
содержания на &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt;. Мне привычнее работать с &lt;a class="reference external" href="http://python.org"&gt;Python&lt;/a&gt; поэтому в качестве генератора
сайта мной используется &lt;a class="reference external" href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt;. Ниже я покажу как поставить его и настроить для работы.&lt;/p&gt;
&lt;p&gt;Если вы предпочитаете &lt;strong&gt;ruby&lt;/strong&gt; дальше можете не читать.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pelican"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id10"&gt;Установка и настройка Pelican&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Предполагается, что вы знакомы с &lt;a class="reference external" href="http://python.org"&gt;Python&lt;/a&gt; и &lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv"&gt;VirtualEnv&lt;/a&gt;.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Так как мы будем использовать &lt;a class="reference external" href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt; нам необходимо выключить встроенный в &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt; генератор &lt;a class="reference external" href="http://github.com/mojombo/jekyll/"&gt;Jekyll&lt;/a&gt;. Это делается добавлением в корень репозитория файла &lt;cite&gt;.nojekyll&lt;/cite&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;touch .nojekyll
git add .
git commit -m &lt;span class="s1"&gt;&amp;#39;Disable Jekyll&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Теперь создадим и активируем виртуальное окружение для модулей нашего сайта:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;virtualenv .ve_blog
&lt;span class="nb"&gt;source&lt;/span&gt; .ve_blog/bin/activate
&lt;/pre&gt;&lt;/div&gt;
&lt;ol class="arabic simple" start="3"&gt;
&lt;li&gt;Установим в созданный нами &lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv"&gt;VirtualEnv&lt;/a&gt; генератор &lt;a class="reference external" href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt; и необходимый для него движок шаблонов &lt;a class="reference external" href="http://jinja.pocoo.org/docs/"&gt;Jinja2&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;easy_install pelican
easy_install jinja2
&lt;/pre&gt;&lt;/div&gt;
&lt;ol class="arabic simple" start="4"&gt;
&lt;li&gt;Мы будем держать исходники сайта в поддиректории &lt;strong&gt;source&lt;/strong&gt;, а созданные статические страницы в корне репозитория, чтобы &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt; их видел.  Создадим файл &lt;cite&gt;source/hello.rst&lt;/cite&gt; с нашей первой статьей:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gh"&gt;Hello world!&lt;/span&gt;
&lt;span class="gh"&gt;############&lt;/span&gt;

&lt;span class="nc"&gt;:slug:&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;

Hello from Pelican!
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Я использую синтаксис &lt;a class="reference external" href="http://docutils.sourceforge.net/rst.html"&gt;RST&lt;/a&gt;, но вы можете использовать &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; для своего сайта. Просто сохраняйте файлы с расширением &lt;cite&gt;*.md&lt;/cite&gt;
&lt;a class="reference external" href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt; поддерживает и другие форматы, но надо ставить соответствующие модули.&lt;/p&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="6"&gt;
&lt;li&gt;И соберем статику:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pelican &lt;span class="nb"&gt;source&lt;/span&gt; -o .
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Опция &lt;em&gt;-o .&lt;/em&gt; заставляет &lt;a class="reference external" href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt; производить сборку статики в корне
проекта.&lt;/p&gt;
&lt;p class="last"&gt;Если все прошло успешно в корне проекта вы увидите несколько &lt;strong&gt;HTML&lt;/strong&gt; файлов.
Откройте &lt;cite&gt;index.html&lt;/cite&gt; в браузере и посмотрите на ваш сайт.&lt;/p&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="7"&gt;
&lt;li&gt;Теперь наши изменения можно сохранить в &lt;a class="reference external" href="http://git-scm.com"&gt;Git&lt;/a&gt; и отправить на &lt;a class="reference external" href="http://github.com"&gt;Github&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git add .
git commit -m &lt;span class="s1"&gt;&amp;#39;Add virtualenv and setup pelican.&amp;#39;&lt;/span&gt;
git push origin master
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;Автоматизация&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Настроим наш сайт и немного автоматизируем рутинные операции.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Создадим файл настроек нашего сайта &lt;cite&gt;source/settings.py&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;AUTHOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Kirill Klenov&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SITENAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;klen.github.com&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SITEURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://klen.github.com&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Подробнее про настройки &lt;a class="reference external" href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt;, можно прочитать в его документации.&lt;/p&gt;
&lt;/div&gt;
&lt;ol class="arabic simple" start="2"&gt;
&lt;li&gt;Для упрощения сборки создадим sh-файл &lt;cite&gt;.compile&lt;/cite&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nv"&gt;PRJ_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/klen/Projects/klen.github.com

&lt;span class="nv"&gt;VE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PRJ_DIR&lt;/span&gt;/.ve_blog

&lt;span class="c1"&gt;# Modify path&lt;/span&gt;
&lt;span class="nv"&gt;OLD_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$VE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PRJ_DIR&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt; PATH

&lt;span class="c1"&gt;# Compile static files&lt;/span&gt;
pelican &lt;span class="nv"&gt;$PRJ_DIR&lt;/span&gt;/source -o &lt;span class="nv"&gt;$PRJ_DIR&lt;/span&gt; -s &lt;span class="nv"&gt;$PRJ_DIR&lt;/span&gt;/source/settings.py -v

&lt;span class="c1"&gt;# Return PATH&lt;/span&gt;
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OLD_PATH&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt; PATH
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И дадим ему права на исполнение:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;chmod +x .compile
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Теперь в директории проекта можно вызывать &lt;cite&gt;./.compile&lt;/cite&gt; и собирать статику даже без активации
виртуального окружения.&lt;/p&gt;
&lt;ol class="arabic simple" start="3"&gt;
&lt;li&gt;Следующим шагом создадим &lt;a class="reference external" href="http://git-scm.com"&gt;Git&lt;/a&gt; хук для автоматической генерации сайта при коммитах. Создадим и отредактируем файл &lt;cite&gt;.git/hooks/pre-commit&lt;/cite&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nv"&gt;PRJ_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/klen/Projects/klen.github.com

&lt;span class="nv"&gt;$PRJ_DIR&lt;/span&gt;/.compile
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Не забудьте сделать его исполнемым.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;При каждом коммите изменений в репозиторий, проект будет пере-собран
автоматически.&lt;/p&gt;
&lt;p&gt;На этом нашу предварительную работу по созданию github-сайта можно считать
оконченной. В дальнейшем стоит поподробнее прочитать документацию &lt;a class="reference external" href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt;,
модифицировать стандартную или создать собственную тему оформления и подключить
какой нибудь сервис комментариев.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Как нетрудно догадаться исходники этого блога расположены по адресу: &lt;a class="reference external" href="http://github.com/klen/klen.github.com"&gt;http://github.com/klen/klen.github.com&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</summary><category term="python"></category><category term="git"></category><category term="github"></category></entry><entry><title>Sorry dude. Page not found</title><link href="http://klen.github.io/404.html" rel="alternate"></link><published>2011-01-01T00:00:00+03:00</published><updated>2011-01-01T00:00:00+03:00</updated><author><name>Kirill Klenov</name></author><id>tag:klen.github.io,2011-01-01:404.html</id><summary type="html">&lt;img alt="" src="http://klen.github.io/static/404.jpg" /&gt;
</summary></entry></feed>