Ural penguins - Сообщений в Pyramid
http://uralbash.ru/blog/category/pyramid/atom.xml
2014-09-05T16:33:00Z
ABlog
REST API для Pyramid при помощи Cornice и SACRUD
http://uralbash.ru/articles/2014/cornice/
2014-09-05T16:33:00Z
2014-09-05T16:33:00Z
Uralbash
<div class="section" id="rest-api-pyramid-cornice-sacrud">
<p><a class="reference external" href="https://github.com/Mozilla">Mozilla</a> использует в своих проектах <a class="reference external" href="http://pylonsproject.org/">Pyramid</a> и у них есть
отличный модуль для создания <code class="docutils literal"><span class="pre">REST</span> <span class="pre">API</span></code>
<a class="reference external" href="https://cornice.readthedocs.org/en/latest/">https://cornice.readthedocs.org/en/latest/</a></p>
<p><code class="docutils literal"><span class="pre">REST</span> <span class="pre">API</span></code> обычно меняет, создает и удаляет записи, которые хранятся в БД.
Что бы не писать много кода на <a class="reference external" href="http://sqlalchemy.org/">SQLAlchemy</a> я использую заготовленные
функции из <a class="reference external" href="https://pypi.python.org/pypi/sacrud">sacrud</a>.</p>
<p>Итак поехали, представим сервис <code class="docutils literal"><span class="pre">REST</span> <span class="pre">API</span></code> для платежных карт с моделью типа:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Card</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s1">'card'</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">number</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">BigInteger</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">uid</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">BYTEA</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">balance</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Numeric</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">preference</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">GUID</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">__json__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span><span class="s1">'id'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
<span class="s1">'number'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span><span class="p">,</span>
<span class="s1">'uid'</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uid</span><span class="p">),</span>
<span class="s1">'balance'</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">balance</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Создадим отдельную папку в <a class="reference external" href="http://pylonsproject.org/">pyramid</a> проекте с названием <code class="docutils literal"><span class="pre">rest</span></code>, где будут
храниться функции api, и пропишем это в основном конфиге.</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="c1"># REST API</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s2">"cornice"</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s2">"myapp.rest"</span><span class="p">,</span> <span class="n">route_prefix</span><span class="o">=</span><span class="s2">"/api"</span><span class="p">)</span>
</pre></div>
</div>
<p>В pyramid’е есть такая штука как <code class="docutils literal"><span class="pre">config.scan()</span></code> она смотрит все файлы в
проекте с расширением <code class="docutils literal"><span class="pre">*.py</span></code> (от текущей директории) и ищет вьюхи обернутые
декоратором <code class="docutils literal"><span class="pre">@view_config</span></code>. Я предпочитаю включать в основной конфиг папочки
через инклуд как в примере выше, а локально в каждой папке уже вызывать
<code class="docutils literal"><span class="pre">config.scan()</span></code>.</p>
<p>Структура папки <code class="docutils literal"><span class="pre">rest</span></code>:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>rest/
├── card.py
├── __init__.py
└── validators.py
</pre></div>
</div>
<p>Файл <code class="docutils literal"><span class="pre">__init__.py</span></code>:</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text">__init__.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">includeme</span><span class="p">(</span><span class="n">config</span><span class="p">):</span>
<span class="n">config</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span>
</pre></div>
</div>
</div>
<p>В <code class="docutils literal"><span class="pre">card.py</span></code> сама реализация <code class="docutils literal"><span class="pre">REST</span> <span class="pre">API</span></code>:</p>
<div class="section" id="get">
<h2>GET</h2>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">json</span>
<span class="kn">from</span> <span class="nn">cornice</span> <span class="kn">import</span> <span class="n">Service</span>
<span class="kn">from</span> <span class="nn">myapp.models</span> <span class="kn">import</span> <span class="n">DBSession</span>
<span class="kn">from</span> <span class="nn">myapp.models</span> <span class="kn">import</span> <span class="n">Card</span>
<span class="kn">from</span> <span class="nn">myapp.rest.validators</span> <span class="kn">import</span> <span class="n">_400</span><span class="p">,</span> <span class="n">_404</span><span class="p">,</span> <span class="n">valid_hex</span>
<span class="kn">from</span> <span class="nn">sacrud.action</span> <span class="kn">import</span> <span class="n">CRUD</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.exc</span> <span class="kn">import</span> <span class="n">DataError</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.orm.exc</span> <span class="kn">import</span> <span class="n">NoResultFound</span>
<span class="n">card_api</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'card'</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s1">'/card/{UID}'</span><span class="p">,</span>
<span class="n">description</span><span class="o">=</span><span class="s2">"REST API for card"</span><span class="p">)</span>
<span class="nd">@card_api.get</span><span class="p">(</span><span class="n">validators</span><span class="o">=</span><span class="n">valid_hex</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_card</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="sd">"""``GET``: возвращает данные о карте.</span>
<span class="sd"> .. code-block:: bash</span>
<span class="sd"> $ curl -D - http://0.0.0.0:6543/api/card/07a8e29d</span>
<span class="sd"> HTTP/1.1 200 OK</span>
<span class="sd"> Content-Length: 69</span>
<span class="sd"> Content-Type: application/json; charset=UTF-8</span>
<span class="sd"> Date: Sat, 02 Aug 2014 11:22:52 GMT</span>
<span class="sd"> Server: waitress</span>
<span class="sd"> {"uid": "07a8e29d", "balance": 507.05, "id": 3, "number": 8224548674}</span>
<span class="sd"> """</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">'UID'</span><span class="p">]</span>
<span class="n">card</span> <span class="o">=</span> <span class="n">DBSession</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Card</span><span class="p">)</span>
<span class="k">if</span> <span class="n">key</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">card</span> <span class="o">=</span> <span class="n">card</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">uid</span><span class="o">=</span><span class="n">key</span><span class="p">)</span><span class="o">.</span><span class="n">one</span><span class="p">()</span>
<span class="k">except</span> <span class="n">NoResultFound</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">_404</span><span class="p">()</span>
<span class="k">except</span> <span class="n">DataError</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">_400</span><span class="p">()</span>
<span class="k">return</span> <span class="n">card</span><span class="o">.</span><span class="n">__json__</span><span class="p">()</span>
</pre></div>
</div>
<p>Декоратор <code class="docutils literal"><span class="pre">card_api</span></code> превращает функцию <code class="docutils literal"><span class="pre">get_card</span></code> во вьюху, ожидающею HTTP
метод GET, и <code class="docutils literal"><span class="pre">config.scan()</span></code> её автоматически подхватит. Внутри можно
реализовывать все что угодно. Про метод validated ниже.</p>
</div>
<div class="section" id="post">
<h2>POST</h2>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="nd">@card_api.post</span><span class="p">(</span><span class="n">validators</span><span class="o">=</span><span class="n">valid_hex</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">set_card</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="sd">"""``POST``: передает параметры для редактирования карты.</span>
<span class="sd"> .. code-block:: bash</span>
<span class="sd"> $ curl -H 'Accept: application/json'\\</span>
<span class="sd"> -H 'Content-Type: application/json'\\</span>
<span class="sd"> http://0.0.0.0:6543/api/card/07a8e29d\\</span>
<span class="sd"> -d '{"number": "8224548674", "balance": "507.05"}'</span>
<span class="sd"> {"uid": "07a8e29d", "balance": 507.05, "id": 3, "number": 8224548674}</span>
<span class="sd"> Если карты нету, то создается новая</span>
<span class="sd"> .. code-block:: bash</span>
<span class="sd"> $ curl -H 'Accept: application/json'\\</span>
<span class="sd"> -H 'Content-Type: application/json'\\</span>
<span class="sd"> http://0.0.0.0:6543/api/card/550e8400\\</span>
<span class="sd"> -d '{"balance": "100.11", "preference": "956bfc40"}'</span>
<span class="sd"> {"uid": "550e8400", "balance": 100.11, "id": 7, "number": 91328937986}</span>
<span class="sd"> """</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">'UID'</span><span class="p">]</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'request'</span><span class="p">:</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">body</span><span class="p">)}</span>
<span class="n">card</span> <span class="o">=</span> <span class="n">DBSession</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Card</span><span class="p">)</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">uid</span><span class="o">=</span><span class="n">key</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="n">card</span><span class="p">:</span>
<span class="n">data</span><span class="p">[</span><span class="s1">'pk'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'id'</span><span class="p">:</span> <span class="n">card</span><span class="o">.</span><span class="n">id</span><span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">data</span><span class="p">[</span><span class="s1">'request'</span><span class="p">][</span><span class="s1">'uid'</span><span class="p">]</span> <span class="o">=</span> <span class="n">key</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">CRUD</span><span class="p">(</span><span class="n">DBSession</span><span class="p">,</span> <span class="n">Card</span><span class="p">,</span> <span class="o">**</span><span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">add</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">_400</span><span class="p">(</span><span class="n">msg</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">message</span><span class="p">))</span>
<span class="n">card</span> <span class="o">=</span> <span class="n">DBSession</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Card</span><span class="p">)</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">uid</span><span class="o">=</span><span class="n">key</span><span class="p">)</span><span class="o">.</span><span class="n">one</span><span class="p">()</span>
<span class="k">return</span> <span class="n">card</span><span class="o">.</span><span class="n">__json__</span><span class="p">()</span>
</pre></div>
</div>
<p>Метод POST меняет параметры карты или создает новую если такой нету. Карта
создается при помощи мега модуля <a class="reference external" href="https://pypi.python.org/pypi/sacrud">sacrud</a>. Подробнее о создании записей
через <a class="reference external" href="https://pypi.python.org/pypi/sacrud">sacrud</a> здесь
<a class="reference external" href="http://sacrud.readthedocs.org/en/latest/plain_usage.html#create-action">http://sacrud.readthedocs.org/en/latest/plain_usage.html#create-action</a></p>
</div>
<div class="section" id="delete">
<h2>DELETE</h2>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="nd">@card_api.delete</span><span class="p">(</span><span class="n">validators</span><span class="o">=</span><span class="n">valid_hex</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">del_card</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="sd">"""``DELETE``: удаляет карту по UID</span>
<span class="sd"> .. code-block:: bash</span>
<span class="sd"> $ curl -H 'Accept: application/json'\\</span>
<span class="sd"> -H 'Content-Type: application/json'\\</span>
<span class="sd"> http://0.0.0.0:6543/api/card/550e8400 -X DELETE</span>
<span class="sd"> {"Goodbye": "550e8400"}</span>
<span class="sd"> """</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">'UID'</span><span class="p">]</span>
<span class="n">card</span> <span class="o">=</span> <span class="n">DBSession</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Card</span><span class="p">)</span><span class="o">.</span><span class="n">filter_by</span><span class="p">(</span><span class="n">uid</span><span class="o">=</span><span class="n">key</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">card</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">_404</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">CRUD</span><span class="p">(</span><span class="n">DBSession</span><span class="p">,</span> <span class="n">Card</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="p">{</span><span class="s1">'id'</span><span class="p">:</span> <span class="n">card</span><span class="o">.</span><span class="n">id</span><span class="p">})</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
<span class="k">except</span> <span class="n">DataError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">_400</span><span class="p">(</span><span class="n">msg</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">message</span><span class="p">))</span>
<span class="k">return</span> <span class="p">{</span><span class="s1">'Goodbye'</span><span class="p">:</span> <span class="n">key</span><span class="p">}</span>
</pre></div>
</div>
<p>Удаление происходит при вызове HTTP метода DELETE. <a class="reference external" href="https://pypi.python.org/pypi/sacrud">sacrud</a> опять же
несколько упрощает эту операцию
<a class="reference external" href="http://sacrud.readthedocs.org/en/latest/plain_usage.html#delete-action">http://sacrud.readthedocs.org/en/latest/plain_usage.html#delete-action</a></p>
<p>В файле <code class="docutils literal"><span class="pre">validators.py</span></code> хранятся всякие исключения и сами проверки. Если в
декораторе card_api указан валидатор, то валидные данные нужно будет выбирать
не через <code class="docutils literal"><span class="pre">request.matchdict</span></code> а через <code class="docutils literal"><span class="pre">request.validated</span></code>.</p>
<p>Пример файла <code class="docutils literal"><span class="pre">validators.py</span></code></p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">string</span>
<span class="kn">from</span> <span class="nn">webob</span> <span class="kn">import</span> <span class="n">exc</span><span class="p">,</span> <span class="n">Response</span>
<span class="k">class</span> <span class="nc">_404</span><span class="p">(</span><span class="n">exc</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="o">=</span><span class="s1">'Not Found'</span><span class="p">):</span>
<span class="n">body</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'status'</span><span class="p">:</span> <span class="mi">404</span><span class="p">,</span> <span class="s1">'message'</span><span class="p">:</span> <span class="n">msg</span><span class="p">}</span>
<span class="n">Response</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">body</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="mi">404</span>
<span class="bp">self</span><span class="o">.</span><span class="n">content_type</span> <span class="o">=</span> <span class="s1">'application/json'</span>
<span class="k">class</span> <span class="nc">_400</span><span class="p">(</span><span class="n">exc</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="o">=</span><span class="s1">'Bad Request'</span><span class="p">):</span>
<span class="n">body</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'status'</span><span class="p">:</span> <span class="mi">400</span><span class="p">,</span> <span class="s1">'message'</span><span class="p">:</span> <span class="n">msg</span><span class="p">}</span>
<span class="n">Response</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">body</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="mi">400</span>
<span class="bp">self</span><span class="o">.</span><span class="n">content_type</span> <span class="o">=</span> <span class="s1">'application/json'</span>
<span class="k">def</span> <span class="nf">valid_hex</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">matchdict</span><span class="p">[</span><span class="s1">'UID'</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">all</span><span class="p">(</span><span class="n">c</span> <span class="ow">in</span> <span class="n">string</span><span class="o">.</span><span class="n">hexdigits</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">key</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">_400</span><span class="p">(</span><span class="n">msg</span><span class="o">=</span><span class="s2">"Not valid UID '</span><span class="si">%s</span><span class="s2">'"</span> <span class="o">%</span> <span class="n">key</span><span class="p">)</span>
<span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">'UID'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
</pre></div>
</div>
<p>Таким образом можно довольно просто создать API для вашего проекта на пирамиде.
Cornice делает много за вас, создает вьюхи, пути, валидацию, может генерить
автоматически <a class="reference external" href="https://pypi.python.org/pypi/Sphinx">Sphinx</a> документацию, а <a class="reference external" href="https://pypi.python.org/pypi/SACRUD">SACRUD</a> упрощает работу с БД.</p>
</div>
</div>
Обновление sacrud. Версия 0.1.2
http://uralbash.ru/articles/2014/sacrud_release_0.1.2/
2014-03-10T14:34:00Z
2014-03-10T14:34:00Z
Uralbash
<div class="section" id="sacrud-0-1-2">
<img alt="_static/2014/sacrud_0_1_2.png" class="align-left" src="_static/2014/sacrud_0_1_2.png" />
<div class="line-block">
<div class="line">Код: <a class="reference external" href="https://github.com/uralbash/sacrud">https://github.com/uralbash/sacrud</a></div>
<div class="line">Описание: <a class="reference external" href="http://sacrud.readthedocs.org/">http://sacrud.readthedocs.org/</a></div>
</div>
<p>В этой версии делался упор на кастомизацию интерфейса.</p>
<p>Что нового?</p>
<ul class="simple">
<li>добавлена пагинация</li>
<li>теперь pk показывается по умолчанию в форме создания/редактирования</li>
<li>новая опция <code class="docutils literal"><span class="pre">sacrud_detail_col</span></code> где можно задать отображаемые поля в форме редактирования</li>
<li>новая опция <code class="docutils literal"><span class="pre">sacrud_list_col</span></code> где можно задать отображаемые поля в списке записей</li>
<li>новая опция <code class="docutils literal"><span class="pre">verbose_name</span></code> для полей и таблиц</li>
<li>опция <code class="docutils literal"><span class="pre">sacrud_css_class</span></code>, назначает <code class="docutils literal"><span class="pre">CSS</span></code> стили полям</li>
<li>новый атрибут колонки <code class="docutils literal"><span class="pre">sacrud_position</span></code>: “inline” (см. реализацию <code class="docutils literal"><span class="pre">horizontal_fields</span></code>)</li>
<li>новая функция <code class="docutils literal"><span class="pre">horizontal_fields</span></code></li>
<li>новый тип <code class="docutils literal"><span class="pre">exttype.GUID</span></code></li>
<li>для переопределения <code class="docutils literal"><span class="pre">base.html</span></code> создан шаблон <code class="docutils literal"><span class="pre">redefineme.html</span></code></li>
<li>вывод флеш уведомлений если задана <code class="docutils literal"><span class="pre">sesion_factory</span></code></li>
<li>исправлены названия классов в шаблонах у полей</li>
<li>исправлен шаблон <code class="docutils literal"><span class="pre">ForeignKey.jinja2</span></code></li>
<li><code class="docutils literal"><span class="pre">sa_create</span></code>, <code class="docutils literal"><span class="pre">sa_read</span></code>, <code class="docutils literal"><span class="pre">sa_update</span></code>, <code class="docutils literal"><span class="pre">sa_delete</span></code> view вынесены в общий класс <code class="docutils literal"><span class="pre">CRUD</span></code></li>
<li>в example добавлены <a class="reference external" href="https://pypi.python.org/pypi/pyramid_beaker">pyramid_beaker</a>, примеры с кастомизацией, FileField и все
остальные поля которые есть в sacrud/templates/sacrud/types.</li>
</ul>
<p>Пример кастомизации:</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text">models.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">TestCustomizing</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"test_customizing"</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">)</span>
<span class="n">date</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Date</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="p">{</span><span class="s2">"verbose_name"</span><span class="p">:</span> <span class="s1">'date JQuery-ui'</span><span class="p">})</span>
<span class="n">name_ru</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="p">{</span><span class="s2">"verbose_name"</span><span class="p">:</span> <span class="sa">u</span><span class="s1">'Название'</span><span class="p">,</span> <span class="p">})</span>
<span class="n">name_fr</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="p">{</span><span class="s2">"verbose_name"</span><span class="p">:</span> <span class="sa">u</span><span class="s1">'nom'</span><span class="p">,</span> <span class="p">})</span>
<span class="n">name_bg</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="p">{</span><span class="s2">"verbose_name"</span><span class="p">:</span> <span class="sa">u</span><span class="s1">'Име'</span><span class="p">,</span> <span class="p">})</span>
<span class="n">name_cze</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="p">{</span><span class="s2">"verbose_name"</span><span class="p">:</span> <span class="sa">u</span><span class="s1">'název'</span><span class="p">,</span> <span class="p">})</span>
<span class="n">description</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Text</span><span class="p">)</span>
<span class="n">description2</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Text</span><span class="p">)</span>
<span class="n">visible</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Boolean</span><span class="p">)</span>
<span class="n">in_menu</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Boolean</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="p">{</span><span class="s2">"verbose_name"</span><span class="p">:</span> <span class="sa">u</span><span class="s1">'menu?'</span><span class="p">,</span> <span class="p">})</span>
<span class="n">in_banner</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Boolean</span><span class="p">,</span> <span class="n">info</span><span class="o">=</span><span class="p">{</span><span class="s2">"verbose_name"</span><span class="p">:</span> <span class="sa">u</span><span class="s1">'on banner?'</span><span class="p">,</span> <span class="p">})</span>
<span class="c1"># SACRUD</span>
<span class="n">verbose_name</span> <span class="o">=</span> <span class="sa">u</span><span class="s1">'Customizing table'</span>
<span class="n">sacrud_css_class</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'tinymce'</span><span class="p">:</span> <span class="p">[</span><span class="n">description</span><span class="p">,</span> <span class="n">description2</span><span class="p">],</span>
<span class="s1">'content'</span><span class="p">:</span> <span class="p">[</span><span class="n">description</span><span class="p">],</span>
<span class="s1">'name'</span><span class="p">:</span> <span class="p">[</span><span class="n">name</span><span class="p">],</span> <span class="s1">'Date'</span><span class="p">:</span> <span class="p">[</span><span class="n">date</span><span class="p">]}</span>
<span class="n">sacrud_list_col</span> <span class="o">=</span> <span class="p">[</span><span class="n">name</span><span class="p">,</span> <span class="n">name_ru</span><span class="p">,</span> <span class="n">name_cze</span><span class="p">]</span>
<span class="n">sacrud_detail_col</span> <span class="o">=</span> <span class="p">[</span><span class="n">name</span><span class="p">,</span>
<span class="n">hosrizontal_field</span><span class="p">(</span><span class="n">name_ru</span><span class="p">,</span> <span class="n">name_bg</span><span class="p">,</span> <span class="n">name_fr</span><span class="p">,</span> <span class="n">name_cze</span><span class="p">,</span>
<span class="n">sacrud_name</span><span class="o">=</span><span class="sa">u</span><span class="s2">"i18n names"</span><span class="p">),</span>
<span class="n">description</span><span class="p">,</span> <span class="n">date</span><span class="p">,</span>
<span class="n">hosrizontal_field</span><span class="p">(</span><span class="n">in_menu</span><span class="p">,</span> <span class="n">visible</span><span class="p">,</span> <span class="n">in_banner</span><span class="p">,</span>
<span class="n">sacrud_name</span><span class="o">=</span><span class="sa">u</span><span class="s2">"Расположение"</span><span class="p">),</span>
<span class="n">description2</span><span class="p">]</span>
</pre></div>
</div>
</div>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text">templates/sacrud/redefineme.jinja2</span></div>
<div class="highlight-html+jinja"><div class="highlight"><pre><span></span><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">"sacrud/base.jinja2"</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">userspace</span> <span class="cp">%}</span>
<span class="cp">{{</span> <span class="nb">super</span><span class="o">()</span> <span class="cp">}}</span>
<span class="c"><!-- Date field --></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"http://code.jquery.com/jquery-1.10.2.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"http://code.jquery.com/ui/1.10.4/jquery-ui.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">href</span><span class="o">=</span><span class="s">"//code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css"</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span><span class="p">></</span><span class="nt">link</span><span class="p">></span>
<span class="p"><</span><span class="nt">style</span><span class="p">></span>
<span class="p">.</span><span class="nc">content</span> <span class="p">{</span>
<span class="k">height</span><span class="p">:</span> <span class="mi">550</span><span class="kt">px</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">.</span><span class="nc">name</span> <span class="p">{</span>
<span class="k">width</span><span class="p">:</span> <span class="mi">400</span><span class="kt">px</span><span class="p">;</span>
<span class="p">}</span>
<span class="p"></</span><span class="nt">style</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">".Date"</span><span class="p">).</span><span class="nx">datepicker</span><span class="p">({</span> <span class="nx">dateFormat</span><span class="o">:</span> <span class="s1">'yy-mm-dd'</span> <span class="p">});</span>
<span class="p">});</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"//tinymce.cachefly.net/4.0/tinymce.min.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="nx">tinymce</span><span class="p">.</span><span class="nx">init</span><span class="p">({</span>
<span class="nx">selector</span><span class="o">:</span><span class="s1">'textarea.tinymce'</span><span class="p">,</span>
<span class="nx">plugins</span><span class="o">:</span> <span class="s2">"image link"</span><span class="p">,</span>
<span class="nx">file_browser_callback</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">field_name</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="nx">type</span><span class="p">,</span> <span class="nx">win</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">tinymce</span><span class="p">.</span><span class="nx">activeEditor</span><span class="p">.</span><span class="nx">windowManager</span><span class="p">.</span><span class="nx">open</span><span class="p">({</span>
<span class="nx">title</span><span class="o">:</span> <span class="s2">"SACRUD file browser"</span><span class="p">,</span>
<span class="nx">url</span><span class="o">:</span> <span class="s2">"/image/filebrowser"</span><span class="p">,</span>
<span class="nx">width</span><span class="o">:</span> <span class="mi">600</span><span class="p">,</span>
<span class="nx">height</span><span class="o">:</span> <span class="mi">400</span><span class="p">,</span>
<span class="p">},</span> <span class="p">{</span>
<span class="nx">oninsert</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">field_name</span><span class="p">).</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">url</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">},</span>
<span class="p">});</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">sa_body</span> <span class="cp">%}</span>
<span class="cp">{{</span> <span class="nb">super</span><span class="o">()</span> <span class="cp">}}</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
</pre></div>
</div>
</div>
<p>Результат:</p>
<div class="figure align-center" id="id3">
<img alt="_static/2014/sacrud_0.1.2_example.png" src="_static/2014/sacrud_0.1.2_example.png" />
<p class="caption"><span class="caption-text">Пример кастомного CRUD интерфейса для SQLAlchemy</span></p>
</div>
<p>Демо приложение: <a class="reference external" href="https://github.com/uralbash/pyramid_sacrud_example">https://github.com/uralbash/pyramid_sacrud_example</a></p>
</div>
Своя панель в pyramid_debugtoolbar
http://uralbash.ru/articles/2014/panel_pyramid_dt/
2014-02-28T22:03:00Z
2014-02-28T22:03:00Z
Uralbash
<div class="section" id="pyramid-debugtoolbar">
<div class="line-block">
<div class="line">В <a class="reference external" href="https://pypi.python.org/pypi/pyramid_debugtoolbar">pyramid_debugtoolbar</a> можно создавать панели для своих нужд.</div>
<div class="line">Документация здесь: <a class="reference external" href="http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/en/latest/#adding-custom-panels">http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/en/latest/#adding-custom-panels</a></div>
</div>
<p>Я создал небольшой пример, как это сделать на примере <a class="reference external" href="https://pypi.python.org/pypi/sadisplay">sadisplay</a>.
<a class="reference external" href="https://pypi.python.org/pypi/sadisplay">sadisplay</a> - это модуль который отображает модели <a class="reference external" href="http://sqlalchemy.org/">SQLAlchemy</a> в виде
<code class="docutils literal"><span class="pre">UML</span></code> диаграммы.</p>
<img alt="_static/2014/sadisplay.png" class="align-center" src="_static/2014/sadisplay.png" />
<p>Было бы удобно видеть схему БД проекта в дебаг панеле. Для этого создадим папку
проекта <code class="docutils literal"><span class="pre">pyramid_debugtoolbar_sadisplay</span></code> со структурой:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>├── __init__.py
├── panel.py
└── templates
└── base.dbtmako
</pre></div>
</div>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text">panel.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="ch">#! /usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*-</span>
<span class="c1"># vim:fenc=utf-8</span>
<span class="c1">#</span>
<span class="c1"># Copyright © 2014 uralbash <root uralbash.ru=""></span>
<span class="c1">#</span>
<span class="c1"># Distributed under terms of the MIT license.</span>
<span class="sd">"""</span>
<span class="sd">sadisplay in pyramid_debugtoolbar</span>
<span class="sd">"""</span>
<span class="kn">import</span> <span class="nn">pydot</span>
<span class="kn">import</span> <span class="nn">sadisplay</span>
<span class="kn">import</span> <span class="nn">sqlalchemy</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">engine_from_config</span>
<span class="kn">from</span> <span class="nn">pyramid_debugtoolbar.panels</span> <span class="kn">import</span> <span class="n">DebugPanel</span>
<span class="n">_</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span>
<span class="k">def</span> <span class="nf">get_sa_base</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">settings</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">settings</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">settings</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">registry</span><span class="o">.</span><span class="n">settings</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">engine_from_config</span><span class="p">(</span><span class="n">settings</span><span class="p">,</span> <span class="s1">'sqlalchemy.'</span><span class="p">)</span>
<span class="n">sabase</span> <span class="o">=</span> <span class="n">sqlalchemy</span><span class="o">.</span><span class="n">ext</span><span class="o">.</span><span class="n">declarative</span><span class="o">.</span><span class="n">declarative_base</span><span class="p">()</span>
<span class="n">sabase</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">reflect</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
<span class="k">return</span> <span class="n">sabase</span>
<span class="k">class</span> <span class="nc">SadisplayDebugPanel</span><span class="p">(</span><span class="n">DebugPanel</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd">debug panel</span>
<span class="sd">"""</span>
<span class="n">name</span> <span class="o">=</span> <span class="s1">'SADisplay'</span>
<span class="n">has_content</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">template</span> <span class="o">=</span> <span class="s1">'pyramid_debugtoolbar_sadisplay:templates/base.dbtmako'</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">request</span> <span class="o">=</span> <span class="n">request</span>
<span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="p">{}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">Base</span> <span class="o">=</span> <span class="n">get_sa_base</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">nav_title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">_</span><span class="p">(</span><span class="s1">'SADisplay'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">url</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s1">''</span>
<span class="k">def</span> <span class="nf">title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">_</span><span class="p">(</span><span class="s1">'SADisplay'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">render_vars</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
<span class="n">tables</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">Base</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">tables</span><span class="o">.</span><span class="n">values</span><span class="p">()</span>
<span class="n">desc</span> <span class="o">=</span> <span class="n">sadisplay</span><span class="o">.</span><span class="n">describe</span><span class="p">(</span><span class="n">tables</span><span class="p">)</span>
<span class="n">dot_data</span> <span class="o">=</span> <span class="n">sadisplay</span><span class="o">.</span><span class="n">dot</span><span class="p">(</span><span class="n">desc</span><span class="p">)</span>
<span class="n">graph</span> <span class="o">=</span> <span class="n">pydot</span><span class="o">.</span><span class="n">graph_from_dot_data</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">dot_data</span><span class="p">))</span>
<span class="n">svg_img</span> <span class="o">=</span> <span class="n">graph</span><span class="o">.</span><span class="n">create_svg</span><span class="p">()</span>
<span class="k">return</span> <span class="p">{</span><span class="s1">'svg_img'</span><span class="p">:</span> <span class="n">svg_img</span><span class="p">}</span>
<span class="k">def</span> <span class="nf">content</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">vars</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">render_vars</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">render</span><span class="p">(</span>
<span class="s1">'pyramid_debugtoolbar_sadisplay:templates/base.dbtmako'</span><span class="p">,</span>
<span class="nb">vars</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Функция <code class="docutils literal"><span class="pre">get_sa_base</span></code> создает <code class="docutils literal"><span class="pre">base</span></code> объект и заполняет его метаданными по
строке подключения к БД, в дальнейшем мы сможем получить все модели проекта.
Метод <code class="docutils literal"><span class="pre">render_vars</span></code> при помощи <a class="reference external" href="https://pypi.python.org/pypi/sadisplay">sadisplay</a> генерит текст формата
<code class="docutils literal"><span class="pre">dot</span></code> и при помощи <a class="reference external" href="https://pypi.python.org/pypi/pydot">pydot</a> конвертит его в <code class="docutils literal"><span class="pre">svg</span></code>.</p>
<p>В версии <a class="reference external" href="https://pypi.python.org/pypi/pyramid_debugtoolbar">pyramid_debugtoolbar</a> 1.0.* нужно рендерить шаблон в методе
content. В 2.0.* метод <code class="docutils literal"><span class="pre">content</span></code> не нужен, шаблон указывается в виде атрибута
<code class="docutils literal"><span class="pre">template</span></code>, а параметры отдаются в методе <code class="docutils literal"><span class="pre">render_vars</span></code>. В примере
используются оба метода, что бы работало во всех версиях.</p>
<p>шаблон <code class="docutils literal"><span class="pre">base.dbtmako</span></code>:</p>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text">base.dbtmako</span></div>
<div class="highlight-html+mako"><div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">h4</span><span class="p">></span>SQLAlchemy models preview<span class="p"></</span><span class="nt">h4</span><span class="p">></span>
<span class="cp">${</span> <span class="n">svg_img</span><span class="o">|</span><span class="n">n</span> <span class="cp">}</span>
link: <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"https://github.com/uralbash/pyramid_debugtoolbar_sadisplay"</span> <span class="na">style</span><span class="o">=</span><span class="s">"color: blue;"</span><span class="p">></span>pyramid_debugtoolbar_sadisplay<span class="p"></</span><span class="nt">a</span><span class="p">></span>
</pre></div>
</div>
</div>
<p>Подключаем к приложению:</p>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text">__init__.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="c1"># SADisplay in pyramid_debugtoolbar</span>
<span class="kn">from</span> <span class="nn">pyramid_debugtoolbar_sadisplay.panel</span> <span class="kn">import</span> <span class="n">SadisplayDebugPanel</span>
<span class="n">config</span><span class="o">.</span><span class="n">registry</span><span class="o">.</span><span class="n">settings</span><span class="p">[</span><span class="s1">'debugtoolbar.panels'</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">SadisplayDebugPanel</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Результат:</p>
<img alt="_static/2014/dt_sadisplay1.png" src="_static/2014/dt_sadisplay1.png" />
<img alt="_static/2014/dt_sadisplay2.png" src="_static/2014/dt_sadisplay2.png" />
<p>Код примера здесь: <a class="reference external" href="https://github.com/uralbash/pyramid_debugtoolbar_sadisplay">https://github.com/uralbash/pyramid_debugtoolbar_sadisplay</a></p>
</div>
Обновление sacrud. Версия 0.1.1
http://uralbash.ru/articles/2014/sacrud_release_0.1.1/
2014-02-23T18:02:00Z
2014-02-23T18:02:00Z
Uralbash
<div class="section" id="sacrud-0-1-1">
<img alt="_static/2014/sacrud_0_1_1.png" class="align-left" src="_static/2014/sacrud_0_1_1.png" />
<div class="line-block">
<div class="line">Код: <a class="reference external" href="https://github.com/uralbash/sacrud">https://github.com/uralbash/sacrud</a></div>
<div class="line">Описание: <a class="reference external" href="http://sacrud.readthedocs.org/">http://sacrud.readthedocs.org/</a></div>
</div>
<p>Что нового?</p>
<ul>
<li><p class="first">в расширении для pyramid параметр “sacrud_models” переименован в “sacrud.models”</p>
</li>
<li><p class="first">“sacrud.models” теперь словарь, а не список:</p>
<blockquote>
<div><div class="highlight-python"><div class="highlight"><pre><span></span><span class="n">settings</span><span class="p">[</span><span class="s1">'sacrud.models'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'Company'</span><span class="p">:</span> <span class="p">[</span><span class="n">Company</span><span class="p">,</span> <span class="n">User</span><span class="p">,</span> <span class="n">EmployeeType</span><span class="p">],</span>
<span class="s1">'Auth'</span><span class="p">:</span> <span class="p">[</span><span class="n">Group</span><span class="p">,</span> <span class="n">GroupPermission</span><span class="p">,</span> <span class="n">UserGroup</span><span class="p">,</span>
<span class="n">GroupResourcePermission</span><span class="p">,</span> <span class="n">Resource</span><span class="p">,</span> <span class="n">UserPermission</span><span class="p">,</span>
<span class="n">UserResourcePermission</span><span class="p">,</span> <span class="n">ExternalIdentity</span><span class="p">],</span>
<span class="s1">''</span><span class="p">:</span> <span class="p">[</span><span class="n">Company</span><span class="p">]</span>
<span class="p">}</span>
</pre></div>
</div>
</div></blockquote>
</li>
<li><p class="first">в словаре можно поделить модели на группы</p>
</li>
<li><p class="first">тесты исправлены для <a class="reference external" href="http://pylonsproject.org/">Pyramid</a> <code class="docutils literal"><span class="pre">1.5</span></code>:</p>
<div class="deprecated">
<p><span class="versionmodified">Не рекомендуется, начиная с версии pyramid: </span>1.5</p>
<p>Removed the ability to influence and query a pyramid.request.Request object as
if it were a dictionary. Previously it was possible to use methods like
__getitem__, get, items, and other dictlike methods to access values in the
WSGI environment. This behavior had been deprecated since Pyramid 1.1. Use
methods of request.environ (a real dictionary) instead.</p>
</div>
</li>
</ul>
</div>
миграции в Pyramid
http://uralbash.ru/articles/2014/alembic_pyramid/
2014-02-16T02:04:00Z
2014-02-16T02:04:00Z
Uralbash
<div class="section" id="pyramid">
<p>Пример миграций в пирамиде:</p>
<p>В <a class="reference external" href="https://www.djangoproject.com/">Django</a> <a class="reference external" href="https://pypi.python.org/pypi/south">south</a> в <a class="reference external" href="http://pylonsproject.org/">Pyramid</a> <a class="reference external" href="https://pypi.python.org/pypi/alembic">alembic</a>. Создаём файл
<code class="docutils literal"><span class="pre">alembic.ini</span></code> и указываем путь до настроек :, т.е. <code class="docutils literal"><span class="pre"><имя</span> <span class="pre">проекта>:<название</span>
<span class="pre">папки></span></code> например <code class="docutils literal"><span class="pre">hlp:alembic</span></code>:</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text">alembic.ini</span></div>
<div class="highlight-ini"><div class="highlight"><pre><span></span><span class="na">pyramid.includes</span> <span class="o">=</span><span class="s"></span>
<span class="s"> pyramid_debugtoolbar</span>
<span class="s"> pyramid_tm</span>
<span class="s"> ziggurat_foundations.ext.pyramid.sign_in</span>
<span class="na">ziggurat_foundations.model_locations.User</span> <span class="o">=</span> <span class="s">hlp.models.auth:User</span>
<span class="c1">#sqlalchemy.url = sqlite:///%(here)s/hlp.sqlite</span>
<span class="na">sqlalchemy.url</span> <span class="o">=</span> <span class="s">postgresql://postgres:postgres@localhost:5432/hlp</span>
<span class="c1"># By default, the toolbar only appears for clients from IP addresses</span>
<span class="c1"># '127.0.0.1' and '::1'.</span>
<span class="c1"># debugtoolbar.hosts = 127.0.0.1 ::1</span>
<span class="c1">###</span>
<span class="c1"># wsgi server configuration</span>
<span class="c1">###</span>
<span class="k">[server:main]</span>
<span class="na">use</span> <span class="o">=</span> <span class="s">egg:waitress#main</span>
<span class="na">host</span> <span class="o">=</span> <span class="s">0.0.0.0</span>
<span class="na">port</span> <span class="o">=</span> <span class="s">6543</span>
<span class="hll"><span class="k">[alembic]</span>
</span><span class="hll"><span class="c1"># path to migration scripts</span>
</span><span class="hll"><span class="na">script_location</span> <span class="o">=</span> <span class="s">hlp:alembic</span>
</span></pre></div>
</div>
</div>
<p>в проекте создаем папку <code class="docutils literal"><span class="pre">alembic</span></code> со следующей структурой:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span> alembic/$ tree
.
├── env.py
├── script.py.mako
└── versions
└── 29ac938c56f_starting.py
</pre></div>
</div>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text">env.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">with_statement</span>
<span class="kn">from</span> <span class="nn">alembic</span> <span class="kn">import</span> <span class="n">context</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">engine_from_config</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.engine.base</span> <span class="kn">import</span> <span class="n">Engine</span>
<span class="kn">from</span> <span class="nn">pyramid.paster</span> <span class="kn">import</span> <span class="n">setup_logging</span>
<span class="c1"># this is the Alembic Config object, which provides</span>
<span class="c1"># access to the values within the .ini file in use.</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">config</span>
<span class="kn">from</span> <span class="nn">hlp.models</span> <span class="kn">import</span> <span class="n">Base</span>
<span class="n">setup_logging</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">config_file_name</span><span class="p">)</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">engine_from_config</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">get_section</span><span class="p">(</span><span class="s1">'app:main'</span><span class="p">),</span> <span class="s1">'sqlalchemy.'</span><span class="p">)</span>
<span class="n">target_metadata</span> <span class="o">=</span> <span class="n">Base</span><span class="o">.</span><span class="n">metadata</span>
<span class="k">def</span> <span class="nf">run_migrations_offline</span><span class="p">():</span>
<span class="sd">"""Run migrations in 'offline' mode.</span>
<span class="sd"> This configures the context with just a URL</span>
<span class="sd"> and not an Engine, though an Engine is acceptable</span>
<span class="sd"> here as well. By skipping the Engine creation</span>
<span class="sd"> we don't even need a DBAPI to be available.</span>
<span class="sd"> Calls to context.execute() here emit the given string to the</span>
<span class="sd"> script output.</span>
<span class="sd"> """</span>
<span class="n">context</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">url</span><span class="o">=</span><span class="n">engine</span><span class="o">.</span><span class="n">url</span><span class="p">)</span>
<span class="k">with</span> <span class="n">context</span><span class="o">.</span><span class="n">begin_transaction</span><span class="p">():</span>
<span class="n">context</span><span class="o">.</span><span class="n">run_migrations</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">run_migrations_online</span><span class="p">():</span>
<span class="sd">"""Run migrations in 'online' mode.</span>
<span class="sd"> In this scenario we need to create an Engine</span>
<span class="sd"> and associate a connection with the context.</span>
<span class="sd"> """</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">engine</span><span class="p">,</span> <span class="n">Engine</span><span class="p">):</span>
<span class="n">connection</span> <span class="o">=</span> <span class="n">engine</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span>
<span class="s1">'Expected engine instance got </span><span class="si">%s</span><span class="s1"> instead'</span> <span class="o">%</span> <span class="nb">type</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span>
<span class="n">connection</span><span class="o">=</span><span class="n">connection</span><span class="p">,</span>
<span class="n">target_metadata</span><span class="o">=</span><span class="n">target_metadata</span>
<span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">with</span> <span class="n">context</span><span class="o">.</span><span class="n">begin_transaction</span><span class="p">():</span>
<span class="n">context</span><span class="o">.</span><span class="n">run_migrations</span><span class="p">()</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">if</span> <span class="n">context</span><span class="o">.</span><span class="n">is_offline_mode</span><span class="p">():</span>
<span class="n">run_migrations_offline</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">run_migrations_online</span><span class="p">()</span>
</pre></div>
</div>
</div>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text">script.py.mako</span></div>
<div class="highlight-mako"><div class="highlight"><pre><span></span><span class="x">"""</span><span class="cp">${</span><span class="n">message</span><span class="cp">}</span><span class="x"></span>
<span class="x">Revision ID: </span><span class="cp">${</span><span class="n">up_revision</span><span class="cp">}</span><span class="x"></span>
<span class="x">Revises: </span><span class="cp">${</span><span class="n">down_revision</span><span class="cp">}</span><span class="x"></span>
<span class="x">Create Date: </span><span class="cp">${</span><span class="n">create_date</span><span class="cp">}</span><span class="x"></span>
<span class="x">"""</span>
<span class="x"># revision identifiers, used by Alembic.</span>
<span class="x">revision = </span><span class="cp">${</span><span class="nb">repr</span><span class="p">(</span><span class="n">up_revision</span><span class="p">)</span><span class="cp">}</span><span class="x"></span>
<span class="x">down_revision = </span><span class="cp">${</span><span class="nb">repr</span><span class="p">(</span><span class="n">down_revision</span><span class="p">)</span><span class="cp">}</span><span class="x"></span>
<span class="x">from alembic import op</span>
<span class="x">import sqlalchemy as sa</span>
<span class="cp">${</span><span class="n">imports</span> <span class="k">if</span> <span class="n">imports</span> <span class="k">else</span> <span class="s2">""</span><span class="cp">}</span><span class="x"></span>
<span class="x">def upgrade():</span>
<span class="cp">${</span><span class="n">upgrades</span> <span class="k">if</span> <span class="n">upgrades</span> <span class="k">else</span> <span class="s2">"pass"</span><span class="cp">}</span><span class="x"></span>
<span class="x">def downgrade():</span>
<span class="cp">${</span><span class="n">downgrades</span> <span class="k">if</span> <span class="n">downgrades</span> <span class="k">else</span> <span class="s2">"pass"</span><span class="cp">}</span><span class="x"></span>
</pre></div>
</div>
</div>
<p>Создаем первую миграцию:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>$ alembic revision --autogenerate -m <span class="s2">"create table"</span>
</pre></div>
</div>
<p>переходим к последней миграции:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>$ alembic upgrade head
</pre></div>
</div>
<p>Есть ешё ништиковые команды типа:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>$ alembic upgrade +2
$ alembic downgrade -1
$ alembic upgrade 29ac
$ alembic <span class="nb">history</span>
$ alembic current
</pre></div>
</div>
<div class="line-block">
<div class="line">пример исключения таблиц из миграций <a class="reference external" href="http://blog.utek.pl/2013/ignoring-tables-in-alembic/">http://blog.utek.pl/2013/ignoring-tables-in-alembic/</a></div>
<div class="line">подробнее здесь <a class="reference external" href="https://alembic.readthedocs.org/en/latest/tutorial.html">https://alembic.readthedocs.org/en/latest/tutorial.html</a></div>
</div>
</div>
Демо репозитарий Pyramid + sacrud
http://uralbash.ru/articles/2013/sacrud_demo/
2013-09-29T05:21:00Z
2013-09-29T05:21:00Z
Uralbash
<div class="section" id="pyramid-sacrud">
<p>pyramid_sacrud_example - пример работы <a class="reference external" href="https://pypi.python.org/pypi/sacrud">sacrud</a> в <a class="reference external" href="http://pylonsproject.org/">Pyramid</a> вместе с
<code class="docutils literal"><span class="pre">PostgreSQL</span></code>.</p>
<img alt="_static/2013/sacrud_demo.png" src="_static/2013/sacrud_demo.png" />
</div>
Запись в БД через sacrud используя SQLAlchemy session.
http://uralbash.ru/articles/2013/sacrud_howto/
2013-08-26T17:06:00Z
2013-08-26T17:06:00Z
Uralbash
<div class="section" id="sacrud-sqlalchemy-session">
<p>Для простых <code class="docutils literal"><span class="pre">CRUD</span></code> действий с БД, можно воспользоваться модулем
<a class="reference external" href="https://sacrud.readthedocs.io/en/master/api.html#module-sacrud.action" title="(в sacrud v)"><code class="xref py py-mod docutils literal"><span class="pre">action</span></code></a> из <a class="reference external" href="https://pypi.python.org/pypi/sacrud">sacrud</a>. Это немного сократит код и добавит
некоторой универсальности в ПО со сложной логикой.</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">sacrud</span> <span class="kn">import</span> <span class="n">action</span>
<span class="kn">from</span> <span class="nn">models</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">DBSession</span><span class="p">,</span>
<span class="n">TestTable</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">hstore_data</span> <span class="o">=</span> <span class="nb">str</span><span class="p">({</span><span class="s1">'param1'</span><span class="p">:</span> <span class="s1">'bla bla bla'</span><span class="p">,</span>
<span class="s1">'param2'</span><span class="p">:</span> <span class="s1">'bla bla bla2'</span><span class="p">,</span>
<span class="s1">'param3'</span><span class="p">:</span> <span class="s1">'7389a498-9347-48e3-835d-c3900dcd2566'</span><span class="p">,</span>
<span class="s1">'patam4'</span><span class="p">:</span> <span class="s1">'dddddddd'</span><span class="p">})</span>
<span class="n">param</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'value'</span><span class="p">:</span> <span class="p">(</span><span class="s1">'123'</span><span class="p">,),</span>
<span class="s1">'description'</span><span class="p">:</span> <span class="p">(</span><span class="s1">'test description'</span><span class="p">,),</span>
<span class="s1">'myhash'</span><span class="p">:</span> <span class="p">[</span><span class="n">hstore_data</span><span class="p">,</span> <span class="p">],</span>
<span class="p">}</span>
<span class="c1"># записывает транзакцию в БД</span>
<span class="n">action</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">DBSession</span><span class="p">,</span> <span class="n">TestTable</span><span class="p">,</span> <span class="n">param</span><span class="p">)</span>
</pre></div>
</div>
<p>параметры в виде списка сделаны для того что бы можно было принимать
множественные значения поля с HTML формы.</p>
<p>UPD: в новой версии можно делать так:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="n">param</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'value'</span><span class="p">:</span> <span class="s1">'123'</span><span class="p">,</span>
<span class="s1">'description'</span><span class="p">:</span> <span class="s1">'test description'</span><span class="p">,</span>
<span class="s1">'myhash'</span><span class="p">:</span> <span class="n">hstore_data</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># записывает транзакцию в БД</span>
<span class="n">action</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">DBSession</span><span class="p">,</span> <span class="n">TestTable</span><span class="p">,</span> <span class="n">param</span><span class="p">)</span>
</pre></div>
</div>
</div>
Обновление sacrud и pyramid_ext
http://uralbash.ru/articles/2013/sacrud_release_0_0_3/
2013-08-26T16:24:00Z
2013-08-26T16:24:00Z
Uralbash
<div class="section" id="sacrud-pyramid-ext">
<p>Новая версия <a class="reference external" href="https://pypi.python.org/pypi/sacrud">sacrud</a> <code class="docutils literal"><span class="pre">0.0.3</span></code>. В ней поправлены некоторые баги,
добавлены <span class="strike">нескучные обои</span> элементы дизайна в расширении для
<code class="docutils literal"><span class="pre">Pyramid</span></code> и создан отдельный репозитарий с примерами работы разных типов
полей (pyramid_sacrud_example). Pyramid_sacrud_example работает только с
<a class="reference external" href="http://postgresql.org">Postgres</a> потому что включает в себя примеры полей специфичных именно для этой
БД (таких как <code class="docutils literal"><span class="pre">hstore</span></code>).</p>
<img alt="_static/2013/sacrud_0_0_3.png" class="align-center" src="_static/2013/sacrud_0_0_3.png" />
</div>
CRUD интерфейс для SQLAlchemy и подключение к Pyramid
http://uralbash.ru/articles/2013/sacrud/
2013-03-09T02:55:00Z
2013-03-09T02:55:00Z
Uralbash
<div class="section" id="crud-sqlalchemy-pyramid">
<p>Запилил Yet another CRUD интерфейс для <a class="reference external" href="http://sqlalchemy.org/">SQLAlchemy</a>. По сути это аналог
<a class="reference external" href="https://www.djangoproject.com/">Django</a> админки или <a class="reference external" href="http://docs.formalchemy.org/">FormAlchemy</a>, но ОЧЕНЬ сильно упрощенный, ничего
лишнего. Есть поддержка большинства полей + кастомные поля типа файл (для
загрузки файлов, изображений) и <code class="docutils literal"><span class="pre">GUID</span></code>. Довольно просто подключить к
<a class="reference external" href="http://pylonsproject.org/">Pyramid</a> проекту и сразу начать работать по адресу
<a class="reference external" href="http://localhost:6543/sacrud">http://localhost:6543/sacrud</a></p>
<p>Проект доступен на github <a class="reference external" href="https://github.com/uralbash/sacrud">https://github.com/uralbash/sacrud</a></p>
<p>В след. релизах планирую добавить новые типы полей, кастомные поля типа
<code class="docutils literal"><span class="pre">tree</span></code> и <code class="docutils literal"><span class="pre">btree</span></code> с <code class="docutils literal"><span class="pre">AJAX</span></code> обработкой в интерфейсе, расширение для других
фреймворков (например <a class="reference external" href="http://flask.pocoo.org/">flask</a>), кастомные фильтры, пагинацию итд</p>
<div class="section" id="id1">
<h2>Установка</h2>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text">PyPi</span></div>
<div class="highlight-bash"><div class="highlight"><pre><span></span>$ pip install sacrud
</pre></div>
</div>
</div>
<div class="literal-block-wrapper docutils container" id="id4">
<div class="code-block-caption"><span class="caption-text">source</span></div>
<div class="highlight-bash"><div class="highlight"><pre><span></span>$ python setup.py install
</pre></div>
</div>
</div>
</div>
<div class="section" id="pyramid">
<h2>Пример использования в Pyramid</h2>
<p>Add to your project config:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="c1"># pyramid_jinja2 configuration</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'pyramid_jinja2'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_jinja2_search_path</span><span class="p">(</span><span class="s2">"myprojectname:templates"</span><span class="p">)</span>
<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="p">(</span><span class="n">Model1</span><span class="p">,</span> <span class="n">Model2</span><span class="p">,</span> <span class="n">Model3</span><span class="p">,)</span>
<span class="c1"># add sacrud and project models</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'sacrud.pyramid_ext'</span><span class="p">)</span>
<span class="n">settings</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">registry</span><span class="o">.</span><span class="n">settings</span>
<span class="n">settings</span><span class="p">[</span><span class="s1">'sacrud_models'</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">Model1</span><span class="p">,</span> <span class="n">Model2</span><span class="p">,</span> <span class="n">Model3</span><span class="p">)</span>
</pre></div>
</div>
<p>go to <a class="reference external" href="http://localhost:6543/sacrud">http://localhost:6543/sacrud</a></p>
</div>
<div class="section" id="id2">
<h2>Скриншоты</h2>
<div class="figure align-center" id="id5">
<img alt="_static/2013/sacrud_index.png" src="_static/2013/sacrud_index.png" />
<p class="caption"><span class="caption-text">список таблиц</span></p>
</div>
<div class="figure align-center" id="id6">
<img alt="_static/2013/sacrud_rows.png" src="_static/2013/sacrud_rows.png" />
<p class="caption"><span class="caption-text">список записей в таблице</span></p>
</div>
<div class="figure align-center" id="id7">
<img alt="_static/2013/sacrud_edit.png" src="_static/2013/sacrud_edit.png" />
<p class="caption"><span class="caption-text">редактирование записи</span></p>
</div>
</div>
</div>
Использование SQLAlchemy в дополнениях к Pyramid
http://uralbash.ru/articles/2013/dbsession_includeme/
2013-02-17T17:43:00Z
2013-02-17T17:43:00Z
Uralbash
<div class="section" id="sqlalchemy-pyramid">
<p>У меня есть дополнение к pyramid которое я включаю почти в каждый проект при
помощи <code class="docutils literal"><span class="pre">includeme</span></code> (<a class="reference external" href="https://docs.pylonsproject.org/projects/pyramid/en/latest/api/config.html#pyramid.config.Configurator.include" title="(в The Pyramid Web Framework v2.0)"><code class="xref py py-meth docutils literal"><span class="pre">include()</span></code></a>). Приложение
это дает мне простой <code class="docutils literal"><span class="pre">CRUD</span></code> интерфейс с <a class="reference external" href="http://jinja.pocoo.org/">Jinja</a> шаблонами малой кровью.
Что позволяет избавиться от монстра <a class="reference external" href="http://docs.formalchemy.org/">FormAlchemy</a>. Естественно каждый
проект имеет свое название поэтому пришлось применить немного магии что бы
создать универсальный механизм получения <code class="docutils literal"><span class="pre">DBSession</span></code> в своем подключаемом
дополнении.</p>
<p>Примерная структура папок дополнения:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>pyramid_ext_plugin
<span class="p">|</span>-- __init__.py
<span class="sb">`</span>-- action.py
</pre></div>
</div>
<p>В <code class="docutils literal"><span class="pre">__init__.py</span></code> находятся все настройки <code class="docutils literal"><span class="pre">includeme</span></code>, а в <code class="docutils literal"><span class="pre">action.py</span></code>
действия с базой. Для получения <code class="docutils literal"><span class="pre">DBSession</span></code> в <code class="docutils literal"><span class="pre">action.py</span></code> исправим сначала
<code class="docutils literal"><span class="pre">__init__.py</span></code>:</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text">__init__.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">sqlalchemy</span>
<span class="kn">import</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">as</span> <span class="nn">orm</span>
<span class="kn">from</span> <span class="nn">zope.sqlalchemy</span> <span class="kn">import</span> <span class="n">ZopeTransactionExtension</span>
<span class="n">DBSession</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">includeme</span><span class="p">(</span><span class="n">config</span><span class="p">):</span>
<span class="k">global</span> <span class="n">DBSession</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">sqlalchemy</span><span class="o">.</span><span class="n">engine_from_config</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">registry</span><span class="o">.</span><span class="n">settings</span><span class="p">)</span>
<span class="k">if</span> <span class="n">DBSession</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">DBSession</span> <span class="o">=</span> <span class="n">orm</span><span class="o">.</span><span class="n">scoped_session</span><span class="p">(</span>
<span class="n">orm</span><span class="o">.</span><span class="n">sessionmaker</span><span class="p">(</span><span class="n">extension</span><span class="o">=</span><span class="n">ZopeTransactionExtension</span><span class="p">()))</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Все Ж) теперь можно в <code class="docutils literal"><span class="pre">action.py</span></code> писать так:</p>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text">action.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pyramid_ext_pugin</span> <span class="kn">import</span> <span class="n">DBSession</span>
</pre></div>
</div>
</div>
</div>
Меняем заголовок ответа в Pyramid/Pylons
http://uralbash.ru/articles/2012/http_header_pyramid/
2012-12-04T15:27:00Z
2012-12-04T15:27:00Z
Uralbash
<div class="section" id="pyramid-pylons">
<p>Обычно в заголовке находится что то типа:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>Content-Length <span class="m">6657</span>
Content-Type text/html<span class="p">;</span> <span class="nv">charset</span><span class="o">=</span>UTF-8
итд
</pre></div>
</div>
<p>Изменить заголовок можно например в настройках <a class="reference external" href="http://www.apache.org">Apache</a> или <a class="reference external" href="http://nginx.org/">nginx</a> или в
самом приложении.</p>
<p>Для <a class="reference external" href="http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/">Pylons</a> пишем <code class="docutils literal"><span class="pre">WSGI</span> <span class="pre">middleware</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">HeaderMiddleware</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="sd">'''WSGI middleware'''</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">application</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">application</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">Request</span><span class="p">(</span><span class="n">environ</span><span class="p">)</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">get_response</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="p">)</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s1">'X-Frame-Options'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'DENY'</span>
<span class="k">return</span> <span class="n">resp</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
</pre></div>
</div>
<p>Для <a class="reference external" href="http://pylonsproject.org/">Pyramid</a> можно обойтись без <code class="docutils literal"><span class="pre">WSGI</span></code>:</p>
<div class="literal-block-wrapper docutils container" id="id1">
<div class="code-block-caption"><span class="caption-text">view.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pyramid.events</span> <span class="kn">import</span> <span class="n">subscriber</span>
<span class="kn">from</span> <span class="nn">pyramid.events</span> <span class="kn">import</span> <span class="n">NewRequest</span>
<span class="nd">@subscriber</span><span class="p">(</span><span class="n">NewRequest</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">add_header</span><span class="p">(</span><span class="n">event</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">event</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">response</span>
<span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">'X-Frame-Options'</span><span class="p">,</span> <span class="s1">'DENY'</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
CMS на python-pyramid
http://uralbash.ru/articles/2012/cms_pyramid2/
2012-11-21T15:41:00Z
2012-11-21T15:41:00Z
Uralbash
<div class="section" id="cms-python-pyramid">
<p>Мне иногда приходится писать сайты знакомым, друзьям, знакомым друзей, знакомым знакомых друзей...
И почти всегда всем нужен простой сайт нахаляву визитка, но что бы можно было
вносить изменения периодически. Идеально для этого наверно подходит CMS, но CMS
слишком тяжелая и имеет зачастую слишком много ненужного. Поэтому я написал
движок сайта-визитки с выпиленным ненужным.</p>
<p><a class="reference external" href="https://pypi.python.org/pypi/pyramid_promosite">pyramid_promosite</a> - это не CMS это движок сайта написанный на Pyramid.
Этот движок дает вам из коробки редактор страниц как <a class="reference external" href="https://www.djangoproject.com/">django</a> <code class="docutils literal"><span class="pre">flatpages</span></code>,
простую админку, возможность переводить страницу на другие языки и ВАЙСВИГ
редактор с возможностью загружать изображения</p>
<p>Способ применения:</p>
<ul class="simple">
<li>устанавливаем <a class="reference external" href="https://pypi.python.org/pypi/pyramid_promosite">pyramid_promosite</a></li>
<li>создаем свой проект</li>
<li>включаем <a class="reference external" href="https://pypi.python.org/pypi/pyramid_promosite">pyramid_promosite</a> в свой проект через <code class="docutils literal"><span class="pre">include</span></code></li>
<li>делаем свои шаблоны</li>
<li>пишем свои страницы со вьюхами (если нужны)</li>
</ul>
<p>все...</p>
<p>Пример сайта <a class="reference external" href="https://github.com/uralbash/platonaps.ru">https://github.com/uralbash/platonaps.ru</a>:</p>
<img alt="_static/2012/platon.png" class="align-center" src="_static/2012/platon.png" />
<div class="line-block">
<div class="line">Подробнее на <a class="reference external" href="https://github.com/uralbash/pyramid_promosite">https://github.com/uralbash/pyramid_promosite</a></div>
<div class="line">Документация в планах, если что то непонятно то отвечу здесь либо в github’е</div>
</div>
</div>
Redactor-js WYSIWYG редактор и python
http://uralbash.ru/articles/2012/redactor-js/
2012-11-02T16:11:00Z
2012-11-02T16:11:00Z
Uralbash
<div class="section" id="redactor-js-wysiwyg-python">
<p><a class="reference external" href="https://github.com/dybskiy/redactor-js">Redactor-js</a> - это простой вайсвиг с
возможностью загрузки изображений.</p>
<img alt="_static/2012/redactor-js.png" class="align-center" src="_static/2012/redactor-js.png" />
<div class="line-block">
<div class="line">demo: <a class="reference external" href="http://imperavi.com/redactor/">http://imperavi.com/redactor/</a></div>
<div class="line">github: <a class="reference external" href="https://github.com/dybskiy/redactor-js">https://github.com/dybskiy/redactor-js</a></div>
</div>
<p>Как сочленить это с <a class="reference external" href="http://php.net/">php</a> можно прочитать из документации
<a class="reference external" href="http://imperavi.com/redactor/docs/images/">http://imperavi.com/redactor/docs/images/</a>. Там же есть ссылка на <a class="reference external" href="http://paltman.com/2012/08/15/how-to-setup-upload-handler-for-redactor/">пример с
Django от Patrick Altman</a>. Я
же постараюсь описать как загружать картинки при помощи <a class="reference external" href="http://pylonsproject.org/">pyramid</a> фреймворка,
думаю переписать на что то другое не составит труда.</p>
<p>js функция redactor’а у меня выглядит вот так:</p>
<div class="highlight-html"><div class="highlight"><pre><span></span><span class="c"><!-- Redactor is here --></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/static/redactor/redactor/redactor.css"</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">"/static/redactor/redactor/redactor.js"</span><span class="p">></</span><span class="nt">script</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/javascript"</span><span class="p">></span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span>
<span class="kd">function</span><span class="p">()</span>
<span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s1">'#redactor_content'</span><span class="p">).</span><span class="nx">redactor</span><span class="p">({</span>
<span class="nx">imageUpload</span><span class="o">:</span> <span class="s1">'/image/upload'</span><span class="p">,</span>
<span class="nx">imageGetJson</span><span class="o">:</span> <span class="s1">'/image/GetJson'</span><span class="p">,</span>
<span class="nx">mobile</span><span class="o">:</span> <span class="kc">true</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">);</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
</pre></div>
</div>
<p>вьюха для загрузки изображений:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'upload_image'</span><span class="p">,</span> <span class="n">renderer</span><span class="o">=</span><span class="s1">'json'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">upload_image</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="c1"># путь к директории куда загружать картинку</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">registry</span><span class="o">.</span><span class="n">settings</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'redactor_images'</span><span class="p">)</span>
<span class="n">filename</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">())</span> <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="n">POST</span><span class="p">[</span><span class="s1">'file'</span><span class="p">]</span><span class="o">.</span><span class="n">filename</span>
<span class="n">input_file</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">POST</span><span class="p">[</span><span class="s1">'file'</span><span class="p">]</span><span class="o">.</span><span class="n">file</span>
<span class="c1"># Using the filename like this without cleaning it is very</span>
<span class="c1"># insecure so please keep that in mind when writing your own</span>
<span class="c1"># file handling.</span>
<span class="n">file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">filename</span><span class="p">)</span>
<span class="n">output_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s1">'wb'</span><span class="p">)</span>
<span class="c1"># Finally write the data to the output file</span>
<span class="n">input_file</span><span class="o">.</span><span class="n">seek</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">input_file</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">2</span> <span class="o"><<</span> <span class="mi">16</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">output_file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">output_file</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">"filelink"</span><span class="p">:</span> <span class="s2">"/static/uploaded/images/"</span> <span class="o">+</span> <span class="n">filename</span><span class="p">}</span>
</pre></div>
</div>
<p>И вьюха для выбора изображений из уже загруженных:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'GetJson'</span><span class="p">),</span> <span class="n">renderer</span><span class="o">=</span><span class="s1">'json'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">imageGetJson</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">registry</span><span class="o">.</span><span class="n">settings</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'redactor_images'</span><span class="p">)</span>
<span class="n">types</span> <span class="o">=</span> <span class="p">(</span><span class="s1">'*.jpg'</span><span class="p">,</span> <span class="s1">'*.jpeg'</span><span class="p">,</span> <span class="s1">'*.gif'</span><span class="p">)</span> <span class="c1"># the tuple of file types</span>
<span class="n">files_grabbed</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">types</span><span class="p">:</span>
<span class="n">files_grabbed</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">path</span> <span class="o">+</span> <span class="s2">"/"</span> <span class="o">+</span> <span class="n">files</span><span class="p">))</span>
<span class="n">images</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="nb">file</span> <span class="ow">in</span> <span class="n">files_grabbed</span><span class="p">:</span>
<span class="nb">file</span> <span class="o">=</span> <span class="nb">file</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span>
<span class="n">images</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s2">"thumb"</span><span class="p">:</span> <span class="s2">"/static/uploaded/images/"</span> <span class="o">+</span> <span class="nb">file</span><span class="p">,</span>
<span class="s2">"image"</span><span class="p">:</span> <span class="s2">"/static/uploaded/images/"</span> <span class="o">+</span> <span class="nb">file</span><span class="p">,</span>
<span class="s2">"title"</span><span class="p">:</span> <span class="nb">file</span><span class="p">,</span> <span class="s2">"folder"</span><span class="p">:</span> <span class="s2">"images"</span><span class="p">})</span>
<span class="k">return</span> <span class="n">images</span>
</pre></div>
</div>
<p>Пользуйтесь, хороший редактор.</p>
</div>
Перевод шаблонов Jinja в Pyramid
http://uralbash.ru/articles/2012/jinja_pyramid_i18n/
2012-09-28T17:58:00Z
2012-09-28T17:58:00Z
Uralbash
<div class="section" id="jinja-pyramid">
<p>Основная документация как это делать <a class="reference external" href="http://pyramid.readthedocs.org/en/1.2-branch/narr/i18n.html">здесь</a>. Но как обычно
есть нюансы.</p>
<p>Рабочий пример можно посмотреть установив шаблон. Устанавливаем <a class="reference external" href="https://pypi.python.org/pypi/pyramid_jinja2">pyramid_jinja2</a>
и пишем:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>$ pcreate -t pyramid_jinja2_starter yoyoyoyo
</pre></div>
</div>
<div class="line-block">
<div class="line">Если лень устанавливать пример, то нужно сделать следующее:</div>
<div class="line">Добавить файл <code class="docutils literal"><span class="pre">message-extraction.ini</span></code></div>
</div>
<div class="highlight-ini"><div class="highlight"><pre><span></span><span class="k">[python: **.py]</span>
<span class="k">[jinja2: **.jinja2]</span>
<span class="na">encoding</span> <span class="o">=</span> <span class="s">utf-8</span>
</pre></div>
</div>
<p>Добавить в <code class="docutils literal"><span class="pre">setup.cfg</span></code>:</p>
<div class="highlight-ini"><div class="highlight"><pre><span></span><span class="k">[extract_messages]</span>
<span class="na">add_comments</span> <span class="o">=</span> <span class="s">TRANSLATORS:</span>
<span class="na">output_file</span> <span class="o">=</span> <span class="s">myprojectname/locale/myprojectname.pot</span>
<span class="na">width</span> <span class="o">=</span> <span class="s">80</span>
<span class="na">mapping_file</span> <span class="o">=</span> <span class="s">message-extraction.ini</span>
</pre></div>
</div>
<p>далее в <code class="docutils literal"><span class="pre">__init__.py</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="n">settings</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">settings</span><span class="p">)</span>
<span class="n">settings</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s1">'jinja2.i18n.domain'</span><span class="p">,</span> <span class="s1">'myprojectname'</span><span class="p">)</span>
<span class="o">...</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_translation_dirs</span><span class="p">(</span><span class="s2">"myprojectname:locale/"</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'pyramid_jinja2'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_jinja2_search_path</span><span class="p">(</span><span class="s2">"myprojectname:templates"</span><span class="p">)</span>
</pre></div>
</div>
<p>В шаблоне:</p>
<div class="highlight-jinja"><div class="highlight"><pre><span></span><span class="cp">{{</span> <span class="nv">gettext</span><span class="o">(</span><span class="s1">'Logo'</span><span class="o">)</span> <span class="cp">}}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">trans</span> <span class="cp">%}</span><span class="x">Home</span><span class="cp">{%</span> <span class="k">endtrans</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</div>
<p>все остальное по документации... Я для упрощения использую скрипт <code class="docutils literal"><span class="pre">i18n.sh</span></code>:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env bash</span>
<span class="nv">py</span><span class="o">=</span>python
<span class="nv">$py</span> setup.py extract_messages
<span class="nv">$py</span> setup.py update_catalog
<span class="nv">$py</span> setup.py compile_catalog
<span class="c1"># vim:set et sts=4 ts=4 tw=80:</span>
</pre></div>
</div>
</div>
CMS на Pyramid фреймворке
http://uralbash.ru/articles/2012/cms_pyramid/
2012-09-26T15:30:00Z
2012-09-26T15:30:00Z
Uralbash
<div class="section" id="cms-pyramid">
<p><a class="reference external" href="http://www.nive.co/cms/root.html">Poolyx CMS</a> - малоизвестная система
управления сайтом написанная на Python с применением <a class="reference external" href="http://pylonsproject.org/">Pyramid</a> немецкими
кодерами.</p>
<img alt="_static/2012/poolyx.jpeg" class="align-center" src="_static/2012/poolyx.jpeg" />
<p>В будущем будет называться <a class="reference external" href="https://pypi.python.org/pypi/nive">nive</a>. Советую попробовать, по крайне мере
мне показалась намного удобнее <a class="reference external" href="https://pypi.python.org/pypi/Kotti">Kotti</a>.</p>
</div>
Простой блог на фреймворке Pyramid
http://uralbash.ru/articles/2012/pyramid_blog/
2012-09-11T17:14:00Z
2012-09-11T17:14:00Z
Uralbash
<div class="section" id="pyramid">
<p>Пример простого блога на Pyramid <a class="reference external" href="https://github.com/uralbash/pyramid_easy_blog">https://github.com/uralbash/pyramid_easy_blog</a></p>
<p>Фьючерсы:</p>
<ul class="simple">
<li>wysiwyg редактор</li>
<li>загрузка изображений</li>
<li>мобайл фон реди</li>
</ul>
<img alt="_static/2012/blog1.png" class="align-center" src="_static/2012/blog1.png" style="width: 300px;" />
<p>Туду:</p>
<ul class="simple">
<li>коменты</li>
<li>теги</li>
<li>удаление изображений (файл менеджер)</li>
</ul>
<p>пул реквест реди</p>
<img alt="_static/2012/blog2.png" class="align-center" src="_static/2012/blog2.png" style="width: 600px;" />
</div>
Структура Pyramid приложений как в Django
http://uralbash.ru/articles/2012/pyramid_as_django/
2012-07-05T10:42:00Z
2012-07-05T10:42:00Z
Uralbash
<div class="section" id="pyramid-django">
<p>Одной из причин отказа развивать ветку <a class="reference external" href="http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/">Pylons</a> стала его архитектура
проекта. Все контроллеры хранятся в директории <code class="docutils literal"><span class="pre">controllers</span></code>, модели в
<code class="docutils literal"><span class="pre">models</span></code>, шаблоны в <code class="docutils literal"><span class="pre">templates</span></code>. Это очень удобно когда у вас маленький
проект, но если он разрастается до десятков и сотен сущностей, то становится
крайне сложно скакать по этим папкам выискивая нужный файл, относящийся именно
к этой сущности. В <a class="reference external" href="https://www.djangoproject.com/">Django</a> сделано по другому, в проекте хранятся
приложения (<code class="docutils literal"><span class="pre">application</span></code>) - это такие маленькие подпрограммы которые
отвечают за конкретный функционал проекта (например фотогалерея
<a class="reference external" href="https://pypi.python.org/pypi/django-photologue">django-photologue</a> или дерево сайта <a class="reference external" href="https://pypi.python.org/pypi/django-sitetree">django-sitetree</a> и прочее).
Каждое такое приложение имеет свою папку и уже в ней хранятся контроллеры
(<code class="docutils literal"><span class="pre">views</span></code> в данном случае) и модели (<code class="docutils literal"><span class="pre">models</span></code>). Т.е. вместо такой
архитектуры <a class="reference external" href="http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/">Pylons</a>:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>Project/
controllers/
controllers1.py
controllers2.py
model/
models1.py
models2.py
templates/
templates1/
templates2/
routes.py
</pre></div>
</div>
<p>мы получаем:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>Project/
app1/
models.py
routes.py
views.py
app2/
models.py
routes.py
views.py
templates/
templates1/
templates2/
</pre></div>
</div>
<p>Разработчики не стали менять архитектуру <a class="reference external" href="http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/">Pylons</a> и создавать Pylons 2.0, а
начали развивать новый проект <a class="reference external" href="http://pylonsproject.org/">Pyramid</a> с похожей на <a class="reference external" href="https://www.djangoproject.com/">Django</a> структурой
проекта. При этом <a class="reference external" href="http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/">Pylons</a> не считается устаревшим, а просто имеет немного
другой подход к разработке.</p>
<p>Стоит ли сейчас выбирать <a class="reference external" href="http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/">Pylons</a>? Да, если вы его хорошо знаете и не
планируете создавать слишком масштабное приложение, иначе лучше все таки
выбрать <a class="reference external" href="http://pylonsproject.org/">Pyramid</a>.</p>
<p>Посмотрим как в нем организовать структуру проекта:</p>
<p>После создания проект выглядит так:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>MyProject/
├── CHANGES.txt
├── development.ini
├── MANIFEST.in
├── myproject
│ ├── __init__.py
│ ├── models.py
│ ├── scripts/
│ ├── static/
│ ├── templates/
│ ├── tests.py
│ └── views.py
├── production.ini
├── README.txt
├── setup.cfg
└── setup.py
</pre></div>
</div>
<p>Наша задача перенести <code class="docutils literal"><span class="pre">models.py</span></code>, <code class="docutils literal"><span class="pre">views.py</span></code>, <code class="docutils literal"><span class="pre">tests.py</span></code> в <code class="docutils literal"><span class="pre">app1</span></code>.
Создаем папку <code class="docutils literal"><span class="pre">app1</span></code> и переносим файлы.</p>
<p>Должно получится так:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>MyProject/
.
├── CHANGES.txt
├── development.ini
├── MANIFEST.in
├── myproject
│ ├── app1
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── tests.py
│ │ └── views.py
│ ├── __init__.py
│ ├── scripts
│ │ ├── initializedb.py
│ │ └── __init__.py
│ ├── static
│ │ ├── favicon.ico
│ │ ├── footerbg.png
│ │ ├── headerbg.png
│ │ ├── ie6.css
│ │ ├── middlebg.png
│ │ ├── pylons.css
│ │ ├── pyramid.png
│ │ ├── pyramid-small.png
│ │ └── transparent.gif
│ └── templates
│ └── mytemplate.pt
├── production.ini
├── README.txt
├── setup.cfg
└── setup.py
</pre></div>
</div>
<p>Меняем <code class="docutils literal"><span class="pre">__init__.py</span></code> в проекте:</p>
<p>Вместо</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pyramid.config</span> <span class="kn">import</span> <span class="n">Configurator</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">engine_from_config</span>
<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">DBSession</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">global_config</span><span class="p">,</span> <span class="o">**</span><span class="n">settings</span><span class="p">):</span>
<span class="sd">""" This function returns a Pyramid WSGI application.</span>
<span class="sd"> """</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">engine_from_config</span><span class="p">(</span><span class="n">settings</span><span class="p">,</span> <span class="s1">'sqlalchemy.'</span><span class="p">)</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">Configurator</span><span class="p">(</span><span class="n">settings</span><span class="o">=</span><span class="n">settings</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_static_view</span><span class="p">(</span><span class="s1">'static'</span><span class="p">,</span> <span class="s1">'static'</span><span class="p">,</span> <span class="n">cache_max_age</span><span class="o">=</span><span class="mi">3600</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'home'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span>
<span class="k">return</span> <span class="n">config</span><span class="o">.</span><span class="n">make_wsgi_app</span><span class="p">()</span>
</pre></div>
</div>
<p>Делаем</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pyramid.config</span> <span class="kn">import</span> <span class="n">Configurator</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">engine_from_config</span>
<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">DBSession</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">global_config</span><span class="p">,</span> <span class="o">**</span><span class="n">settings</span><span class="p">):</span>
<span class="sd">""" This function returns a Pyramid WSGI application.</span>
<span class="sd"> """</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">engine_from_config</span><span class="p">(</span><span class="n">settings</span><span class="p">,</span> <span class="s1">'sqlalchemy.'</span><span class="p">)</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">Configurator</span><span class="p">(</span><span class="n">settings</span><span class="o">=</span><span class="n">settings</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_static_view</span><span class="p">(</span><span class="s1">'static'</span><span class="p">,</span> <span class="s1">'static'</span><span class="p">,</span> <span class="n">cache_max_age</span><span class="o">=</span><span class="mi">3600</span><span class="p">)</span>
<span class="c1"># add config for each of your subapps</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'myproject.app1'</span><span class="p">)</span>
<span class="c1"># pyramid_jinja2 configuration</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'pyramid_jinja2'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_jinja2_search_path</span><span class="p">(</span><span class="s2">"myproject:templates"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">config</span><span class="o">.</span><span class="n">make_wsgi_app</span><span class="p">()</span>
</pre></div>
</div>
<p>Теперь в этом <code class="docutils literal"><span class="pre">__init__.py</span></code> мы будем хранить глобальные настройки, а в инитах
апликайшинах настройки самих апликайшинов.</p>
<p>Добавляем <code class="docutils literal"><span class="pre">models.py</span></code> в основной проект:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>MyProject/
.
├── CHANGES.txt
├── development.ini
├── MANIFEST.in
├── myproject
│ ├── app1
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── tests.py
│ │ └── views.py
│ ├── __init__.py
│ ├── scripts
│ │ ├── initializedb.py
│ │ └── __init__.py
│ ├── models.py
│ ├── static
│ │ ├── favicon.ico
│ │ ├── footerbg.png
│ │ ├── headerbg.png
│ │ ├── ie6.css
│ │ ├── middlebg.png
│ │ ├── pylons.css
│ │ ├── pyramid.png
│ │ ├── pyramid-small.png
│ │ └── transparent.gif
│ └── templates
│ └── mytemplate.pt
├── production.ini
├── README.txt
├── setup.cfg
└── setup.py
</pre></div>
</div>
<p>В нем будет хранится сессия <a class="reference external" href="http://sqlalchemy.org/">SQLAlchemy</a> общая для всех проектов:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">zope.sqlalchemy</span> <span class="kn">import</span> <span class="n">ZopeTransactionExtension</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.ext.declarative</span> <span class="kn">import</span> <span class="n">declarative_base</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">scoped_session</span><span class="p">,</span>
<span class="n">sessionmaker</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">DBSession</span> <span class="o">=</span> <span class="n">scoped_session</span><span class="p">(</span><span class="n">sessionmaker</span><span class="p">(</span><span class="n">extension</span><span class="o">=</span><span class="n">ZopeTransactionExtension</span><span class="p">()))</span>
<span class="n">Base</span> <span class="o">=</span> <span class="n">declarative_base</span><span class="p">()</span>
</pre></div>
</div>
<p>Меняем <code class="docutils literal"><span class="pre">__init__.py</span></code> в <code class="docutils literal"><span class="pre">app1</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">includeme</span><span class="p">(</span><span class="n">config</span><span class="p">):</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'home'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span>
</pre></div>
</div>
<p>Меняем <code class="docutils literal"><span class="pre">models.py</span></code> в <code class="docutils literal"><span class="pre">app1</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">Column</span><span class="p">,</span>
<span class="n">Integer</span><span class="p">,</span>
<span class="n">Text</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.ext.declarative</span> <span class="kn">import</span> <span class="n">declarative_base</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">scoped_session</span><span class="p">,</span>
<span class="n">sessionmaker</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">..models</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">DBSession</span><span class="p">,</span>
<span class="n">Base</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">class</span> <span class="nc">MyModel</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s1">'models'</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Text</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">)</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
</pre></div>
</div>
<p>Меняем <code class="docutils literal"><span class="pre">tests.py</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">import</span> <span class="nn">transaction</span>
<span class="kn">from</span> <span class="nn">pyramid</span> <span class="kn">import</span> <span class="n">testing</span>
<span class="kn">from</span> <span class="nn">..models</span> <span class="kn">import</span> <span class="n">DBSession</span>
<span class="k">class</span> <span class="nc">TestMyView</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">config</span> <span class="o">=</span> <span class="n">testing</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">create_engine</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">create_engine</span><span class="p">(</span><span class="s1">'sqlite://'</span><span class="p">)</span>
<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">Base</span><span class="p">,</span>
<span class="n">MyModel</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
<span class="n">Base</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">create_all</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
<span class="k">with</span> <span class="n">transaction</span><span class="o">.</span><span class="n">manager</span><span class="p">:</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">MyModel</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'one'</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">55</span><span class="p">)</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">tearDown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span>
<span class="n">testing</span><span class="o">.</span><span class="n">tearDown</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">test_it</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="kn">from</span> <span class="nn">.views</span> <span class="kn">import</span> <span class="n">my_view</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">testing</span><span class="o">.</span><span class="n">DummyRequest</span><span class="p">()</span>
<span class="n">info</span> <span class="o">=</span> <span class="n">my_view</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">info</span><span class="p">[</span><span class="s1">'one'</span><span class="p">]</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="s1">'one'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">info</span><span class="p">[</span><span class="s1">'project'</span><span class="p">],</span> <span class="s1">'MyProject'</span><span class="p">)</span>
</pre></div>
</div>
<p>Меняем <code class="docutils literal"><span class="pre">views.py</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">sqlalchemy.exc</span> <span class="kn">import</span> <span class="n">DBAPIError</span>
<span class="kn">from</span> <span class="nn">..models</span> <span class="kn">import</span> <span class="n">DBSession</span>
<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">MyModel</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'home'</span><span class="p">,</span> <span class="n">renderer</span><span class="o">=</span><span class="s1">'../templates/mytemplate.pt'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">my_view</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">one</span> <span class="o">=</span> <span class="n">DBSession</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">MyModel</span><span class="p">)</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">MyModel</span><span class="o">.</span><span class="n">name</span><span class="o">==</span><span class="s1">'one'</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">except</span> <span class="n">DBAPIError</span><span class="p">:</span>
<span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">conn_err_msg</span><span class="p">,</span> <span class="n">content_type</span><span class="o">=</span><span class="s1">'text/plain'</span><span class="p">,</span> <span class="n">status_int</span><span class="o">=</span><span class="mi">500</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span><span class="s1">'one'</span><span class="p">:</span><span class="n">one</span><span class="p">,</span> <span class="s1">'project'</span><span class="p">:</span><span class="s1">'MyProject'</span><span class="p">}</span>
<span class="n">conn_err_msg</span> <span class="o">=</span> <span class="s2">"""</span><span class="se">\</span>
<span class="s2">Pyramid is having a problem using your SQL database. The problem</span>
<span class="s2">might be caused by one of the following things:</span>
<span class="s2">A. You may need to run the "initialize_MyProject_db" script</span>
<span class="s2"> to initialize your database tables. Check your virtual</span>
<span class="s2"> environment's "bin" directory for this script and try to run it.</span>
<span class="s2">B. Your database server may not be running. Check that the</span>
<span class="s2"> database server referred to by the "sqlalchemy.url" setting in</span>
<span class="s2"> your "development.ini" file is running.</span>
<span class="s2">After you fix the problem, please restart the Pyramid application to</span>
<span class="s2">try it again.</span>
<span class="s2">"""</span>
</pre></div>
</div>
<p>Меняем <code class="docutils literal"><span class="pre">initializedb.py</span></code> в <code class="docutils literal"><span class="pre">scripts</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">transaction</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">engine_from_config</span>
<span class="kn">from</span> <span class="nn">pyramid.paster</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">get_appsettings</span><span class="p">,</span>
<span class="n">setup_logging</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">..models</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">DBSession</span><span class="p">,</span>
<span class="n">Base</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">..app1.models</span> <span class="kn">import</span> <span class="n">MyModel</span>
<span class="k">def</span> <span class="nf">usage</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
<span class="n">cmd</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'usage: </span><span class="si">%s</span><span class="s1"> <config_uri></span><span class="se">\n</span><span class="s1">'</span>
<span class="s1">'(example: "</span><span class="si">%s</span><span class="s1"> development.ini")'</span> <span class="o">%</span> <span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">cmd</span><span class="p">))</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">:</span>
<span class="n">usage</span><span class="p">(</span><span class="n">argv</span><span class="p">)</span>
<span class="n">config_uri</span> <span class="o">=</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">setup_logging</span><span class="p">(</span><span class="n">config_uri</span><span class="p">)</span>
<span class="n">settings</span> <span class="o">=</span> <span class="n">get_appsettings</span><span class="p">(</span><span class="n">config_uri</span><span class="p">)</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">engine_from_config</span><span class="p">(</span><span class="n">settings</span><span class="p">,</span> <span class="s1">'sqlalchemy.'</span><span class="p">)</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
<span class="n">Base</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">create_all</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
<span class="k">with</span> <span class="n">transaction</span><span class="o">.</span><span class="n">manager</span><span class="p">:</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">MyModel</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">'one'</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
</pre></div>
</div>
<div class="line-block">
<div class="line">Выполняем <code class="docutils literal"><span class="pre">python</span> <span class="pre">setup.py</span> <span class="pre">install</span></code>, запускаем проект, профит!</div>
<div class="line">Остальные апликайшины добавляются по аналогии.</div>
<div class="line">Навеянно этим <a class="reference external" href="http://stackoverflow.com/questions/6012991/pyramid-project-structure">http://stackoverflow.com/questions/6012991/pyramid-project-structure</a></div>
</div>
</div>
pyramid_formalchemy
http://uralbash.ru/articles/2012/pyramid_formalchemy/
2012-07-04T11:51:00Z
2012-07-04T11:51:00Z
Uralbash
<div class="section" id="pyramid-formalchemy">
<p><a class="reference external" href="https://pypi.python.org/pypi/FormAlchemy">FormAlchemy</a> это <code class="docutils literal"><span class="pre">CRUD</span></code> для <a class="reference external" href="http://sqlalchemy.org/">SQLAlchemy</a>. Для <a class="reference external" href="http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/">Pylons</a>
существовало расширение прямо в самом модуле в разделе ext. Для Pyramid создали
отдельный пакет <a class="reference external" href="https://pypi.python.org/pypi/pyramid_formalchemy">pyramid_formalchemy</a>. Посмотрим как это работает:</p>
<p>Создаем проект:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span>$ pcreate -s alchemy -s pyramid_fa myapp
</pre></div>
</div>
<p>добавляем в проект файл <code class="docutils literal"><span class="pre">forms.py</span></code>:</p>
<div class="literal-block-wrapper docutils container" id="id2">
<div class="code-block-caption"><span class="caption-text">forms.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">formalchemy</span> <span class="kn">import</span> <span class="n">FieldSet</span><span class="p">,</span> <span class="n">Grid</span>
</pre></div>
</div>
</div>
<p>структура файлов должна выглядеть так:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span><span class="p">|</span> <span class="p">|</span>+static/
<span class="p">|</span> <span class="p">|</span>+templates/
<span class="p">|</span> <span class="p">|</span>-__init__.py
<span class="p">|</span> <span class="p">|</span>-faforms.py
<span class="p">|</span> <span class="p">|</span>-fainit.py
<span class="p">|</span> <span class="p">|</span>-faroutes.py
<span class="p">|</span> <span class="p">|</span>-forms.py
<span class="p">|</span> <span class="p">|</span>-models.py
<span class="p">|</span> <span class="p">|</span>-tests.py
<span class="p">|</span> <span class="sb">`</span>-views.py
</pre></div>
</div>
<p>изменяем <code class="docutils literal"><span class="pre">__init__.py</span></code>:</p>
<div class="literal-block-wrapper docutils container" id="id3">
<div class="code-block-caption"><span class="caption-text">__init__.py</span></div>
<div class="highlight-python"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pyramid.config</span> <span class="kn">import</span> <span class="n">Configurator</span>
<span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">engine_from_config</span>
<span class="kn">from</span> <span class="nn">.models</span> <span class="kn">import</span> <span class="n">DBSession</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">global_config</span><span class="p">,</span> <span class="o">**</span><span class="n">settings</span><span class="p">):</span>
<span class="sd">""" This function returns a Pyramid WSGI application.</span>
<span class="sd"> """</span>
<span class="n">engine</span> <span class="o">=</span> <span class="n">engine_from_config</span><span class="p">(</span><span class="n">settings</span><span class="p">,</span> <span class="s1">'sqlalchemy.'</span><span class="p">)</span>
<span class="n">DBSession</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">Configurator</span><span class="p">(</span><span class="n">settings</span><span class="o">=</span><span class="n">settings</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_static_view</span><span class="p">(</span><span class="s1">'static'</span><span class="p">,</span> <span class="s1">'static'</span><span class="p">,</span> <span class="n">cache_max_age</span><span class="o">=</span><span class="mi">3600</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'home'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span>
<span class="c1"># pyramid_formalchemy's configuration</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'pyramid_fanstatic'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'pyramid_formalchemy'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">include</span><span class="p">(</span><span class="s1">'fa.jquery'</span><span class="p">)</span>
<span class="c1"># register an admin UI</span>
<span class="n">config</span><span class="o">.</span><span class="n">formalchemy_admin</span><span class="p">(</span><span class="s1">'/admin'</span><span class="p">,</span> <span class="n">package</span><span class="o">=</span><span class="s1">'youAppName'</span><span class="p">,</span>
<span class="n">view</span><span class="o">=</span><span class="s1">'fa.jquery.pyramid.ModelView'</span><span class="p">)</span>
<span class="k">return</span> <span class="n">config</span><span class="o">.</span><span class="n">make_wsgi_app</span><span class="p">()</span>
</pre></div>
</div>
</div>
<p>Заходим в <a class="reference external" href="http://0.0.0.0:6543/admin/">http://0.0.0.0:6543/admin/</a> и радуемся.</p>
<img alt="_static/2012/pyramid_formalchemy.png" class="align-center" src="_static/2012/pyramid_formalchemy.png" />
<p>Online демо находится <a class="reference external" href="http://docs.formalchemy.org/demo/admin/">здесь</a>.</p>
</div>