<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7864195782654307449</id><updated>2011-10-25T11:18:03.130+03:00</updated><category term='BeautifulSoup'/><category term='удобно'/><category term='тесты'/><category term='парсеры'/><category term='моё'/><category term='monkeypatch'/><category term='теги'/><category term='скреперы'/><category term='CSS'/><category term='soupselect'/><category term='html'/><title type='text'>Пероксид на Джанге.</title><subtitle type='html'>Жаба на метле</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-825005525009910969</id><published>2011-10-25T11:02:00.004+03:00</published><updated>2011-10-25T11:18:03.147+03:00</updated><title type='text'>Uploadify на Mac OS X</title><content type='html'>&lt;p&gt;
Говорят, что у Mac OS X есть какие-то проблемы с Flash. Или наоборот, что у Flash есть какие-то проблемы с Mac OS X.
В общем, есть между ними какие-то трения. Я лично столкнулся с чем-то таким, используя Uploadify.
Этот плагин jQuery вносит метод uploadify, который принимает объект опций, среди которых есть scriptData.
scriptData -  объект, свойства которого будут добавлены к POSTу, когда flash-часть uploadify начнет загружать файл на сервер. В теории и документации вы увидите все, что передали в scriptData,  в request.POST (если, конечно, используете Django).
&lt;/p&gt;
&lt;p&gt;
На самом деле все не так. На Mac OS X вы увидите только первое свойство объекта scriptData. Так, если scriptData: {'size': '160x160', 'crop': true}, то вы request.POST будет только size. Обойти это можно так. Вместо {'scriptData': {'foo': 'bar', 'bar': 'foo'} }надо передать {'auto': false, 'onSelect': function(){ $x.uploadifySettings({'foo': 'bar', 'bar': 'foo'}); $x.uploadifyUpload();}}. Здесь $x - тот же объекта, метод котрого uploadify() вы вызываете. Так работает.
&lt;/p&gt;
&lt;p&gt;
Но это не вся правда. в request.POST будет элемент 'foo' и элемент 'amp;bar'. Ну это просто:

&lt;pre&gt;&lt;code class="python"&gt;
if 'amp;crop' in request.POST:
        request.POST['crop'] = request.POST['amp;crop']
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;
&lt;p&gt;
Почему так - не знаю. Но надеюсь, что моя небольшая находка сэкономит кому-то (мне) немного времени.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-825005525009910969?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/825005525009910969/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=825005525009910969' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/825005525009910969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/825005525009910969'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2011/10/uploadify-mac-os-x.html' title='Uploadify на Mac OS X'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-1923000120601416474</id><published>2011-10-18T00:33:00.004+03:00</published><updated>2011-10-18T00:39:31.120+03:00</updated><title type='text'>Важность правильных названий переменных</title><content type='html'>&lt;p&gt;Я никак не мог запомнить кто из аргументов функции, передаваемой reduce, аккумулятор, а кто очередной элемент обрабатываемой последовательности. Теперь-то я понимаю, что это не только потому, что я немного пустоголовый, но и потому, что в официальной документации Python в примере для функции reduce эти аргументы называются x и y.
&lt;/p&gt;
&lt;p&gt;
Но сегодня я прочел &lt;a href="http://odetocode.com/blogs/scott/archive/2011/08/17/underscore-js.aspx"&gt;заметку про underscore.js некоего кого-то&lt;/a&gt; и буду помнить, что аккумулятор - первый аргумент. Просто в примере использования reduce автор заметки назвал аргументы передаваемой в reduce функции sum и n. Вот так просто.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-1923000120601416474?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/1923000120601416474/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=1923000120601416474' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/1923000120601416474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/1923000120601416474'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2011/10/blog-post.html' title='Важность правильных названий переменных'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-6756085710497320159</id><published>2011-10-16T23:41:00.004+03:00</published><updated>2011-10-17T01:00:29.890+03:00</updated><title type='text'>node.js-like next</title><content type='html'>&lt;p&gt;Есть у нас профили. Отдельный профиль доступен по пути, который соответствует '^(\w+)/$'.  Хорошо.
Прошло полгода, появилась новая сущность (НС). Попросили, чтобы отдельная НС была доступна по пути, который соответствует '^(\w+)/$'.  Для простоты и красоты.&lt;/p&gt;
&lt;p&gt;
Можно в первой вьюшке генерировать 404, если по слагу не находится профиль, ловить 404 тут же и вызывать вторую вьюшку, передавая туда слаг.
&lt;/p&gt;
&lt;pre&gt;&lt;code class="python"&gt;
def profile_detail(request, slug):
    try:
        profile = get_object_or_404(Profile, slug=slug)
    except Profile.DoesNotExist:
        return another_detail(request, slug)
    else:
        ... 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
В node.js фреймворке Express &lt;a href="http://expressjs.com/guide.html#passing-route%20control"&gt;можно&lt;/a&gt; &lt;span style="font-style:italic;"&gt;передать контроль&lt;/span&gt; следующему обработчику пути. Вот бы Джанге что-то такое.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-6756085710497320159?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/6756085710497320159/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=6756085710497320159' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/6756085710497320159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/6756085710497320159'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2011/10/nodejs-like-next.html' title='node.js-like next'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-7928627493641298992</id><published>2009-10-30T12:15:00.005+02:00</published><updated>2009-10-30T18:06:18.038+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='удобно'/><category scheme='http://www.blogger.com/atom/ns#' term='теги'/><category scheme='http://www.blogger.com/atom/ns#' term='моё'/><title type='text'>load_object - пара к render_object</title><content type='html'>Я написал шаблонный load_object, который удобно использовать в паре с render_object. Тег парный, его назначение - загрузить в контекст под заданным именем нужный экземпляр нужной модели.Первые два аргумента как в contenttypes, третий - первичный ключ.

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Library&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TemplateSyntaxError&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.contenttypes.models&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ContentType&lt;/span&gt;


&lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Library&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodelist&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_label&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodelist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nodelist&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;LoadObjectNode&amp;gt;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;app_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DoesNotExist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_class&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_default_manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DoesNotExist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MultipleObjectsReturned&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
                &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodelist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_load_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Used get an object by content type app_name and model&lt;/span&gt;
&lt;span class="sd"&gt;    attributes and object primary key and inject it into context.&lt;/span&gt;
&lt;span class="sd"&gt;    &lt;/span&gt;
&lt;span class="sd"&gt;    Usage:&lt;/span&gt;
&lt;span class="sd"&gt;    {% load_object app_label model pk as name %}&lt;/span&gt;
&lt;span class="sd"&gt;    {{ varname }}&lt;/span&gt;
&lt;span class="sd"&gt;    {% endload_object %}&lt;/span&gt;
&lt;span class="sd"&gt;    &lt;/span&gt;
&lt;span class="sd"&gt;    Exceptionally usful in conjunction with render_object template tag&lt;/span&gt;
&lt;span class="sd"&gt;    http://www.djangosnippets.org/snippets/1745/&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split_contents&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mf"&gt;6&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;as&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;TemplateSyntaxError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%r&lt;/span&gt;&lt;span class="s"&gt; expected format is &amp;#39;load_object app_label model pk as name&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;
            &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;app_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;nodelist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;endload_object&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_first_token&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LoadObjectNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodelist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;do_load_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;load_object&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_load_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

Для ясности: как честный тщеславный человек я хотел выложить шаблонный тег на djangosnippets.org. Но я забыл пароль к своему аккаунту и восстановления пароля не нашел. На призывы о помощи ubernostrum не откликнулся (или они до него не дошли). Другой аккаунт там заводить не хочу, так что публикую тег тут.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-7928627493641298992?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/7928627493641298992/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=7928627493641298992' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/7928627493641298992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/7928627493641298992'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2009/10/loadobject-renderobject.html' title='load_object - пара к render_object'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-4255115913226209554</id><published>2009-10-29T11:02:00.007+02:00</published><updated>2009-10-30T12:09:12.037+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='удобно'/><category scheme='http://www.blogger.com/atom/ns#' term='теги'/><title type='text'>render_object</title><content type='html'>Есть шикарный шаблонный тег &lt;a href="http://www.djangosnippets.org/snippets/1745/"&gt;render_object&lt;/a&gt;.
Он очень разумный. Если в шаблоне есть экземпляр модели, то этот тег рендерит экземпляр, используя шаблон по соглашению:
&lt;ul&gt;
&lt;li&gt;"render/[position]/[app_label]_[model_name].html"&lt;/li&gt;
&lt;li&gt;"render/[position]/default.html"&lt;/li&gt;
&lt;li&gt;"render/[app_label]_[model_name].html"&lt;/li&gt;
&lt;li&gt;"render/default.html"&lt;/li&gt;
&lt;/ul&gt;
app_label и model_name понятно, как в contenttypes. position позволяет различать ситации, в которых рендерится экземпляр. Например, position может быть search_results или details, соответственно в первом случае можно рендерить кратко как-то, а во втором случае развёрнуто. 
jeffschenck - хороший человек - придумал и написал этот тег.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-4255115913226209554?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/4255115913226209554/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=4255115913226209554' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/4255115913226209554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/4255115913226209554'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2009/10/renderobject.html' title='render_object'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-8084989263762809233</id><published>2009-10-28T21:51:00.003+02:00</published><updated>2009-10-30T12:13:28.166+02:00</updated><title type='text'>Орднунг defaulttags.py</title><content type='html'>В документации про шаблонные теги делают так:
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_xxx&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="c"&gt;#или так:&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_xxx&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;xxx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_xxx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

А в defaulttags.py систематически делают так:
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_xxx&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;do_xxx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;xxx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_xxx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
Понятно, что вариант с простым вызовом register.tag работает, потому как нам в общем-то надо, чтобы тег был зарегистрирован в register. Ясно, что вариант с присваиванием более полно делает то, что делает декоратор. Интересно, кроме тяги к чистоте рук и помыслов, вариант с присваиванием еще чем-то вызван или нет? Ломается ли оно где-то из-за того, что я просто присваиваю, или это просто четкий строевой шаг такой?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-8084989263762809233?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/8084989263762809233/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=8084989263762809233' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/8084989263762809233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/8084989263762809233'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2009/10/register.html' title='Орднунг defaulttags.py'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-7873237860037037561</id><published>2009-10-28T21:20:00.005+02:00</published><updated>2009-10-30T12:14:38.400+02:00</updated><title type='text'></title><content type='html'>Вот так заходишь, видишь: "4 сообщ., последнее опубликовано  13.07.2008", а тут рядом - та-там! - "&lt;span class="stats"&gt;&lt;img src="http://www.blogger.com/img/icon_28_followers.png" alt="Постоянные читатели" /&gt;&lt;/span&gt; 1 Постоянный читатель&lt;span class="stats"&gt;&lt;/span&gt;". Спасибо тебе, неизвестный мне товарищ.

А вообще я хотел сказать, что когда смотришь в /django/template/context.py:6  и видишь там

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# We need the CSRF processor no matter what the user has in their settings,&lt;/span&gt;
&lt;span class="c"&gt;# because otherwise it is a security vulnerability, and we can&amp;#39;t afford to leave&lt;/span&gt;
&lt;span class="c"&gt;# this to human error or failure to read migration instructions.&lt;/span&gt;
&lt;span class="n"&gt;_builtin_context_processors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;django.core.context_processors.csrf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


то становится ясно, что этот проект будет сдаваться с Django 1.1.1.
Интересно, лидеры других успешных питоновых открытых проектов  тоже беневолентные диктаторы(пожизненные)?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-7873237860037037561?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/7873237860037037561/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=7873237860037037561' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/7873237860037037561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/7873237860037037561'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2009/10/4.html' title=''/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-7293813176606132877</id><published>2008-07-13T22:21:00.004+03:00</published><updated>2008-07-13T23:52:46.909+03:00</updated><title type='text'>Исключительная производительность по Yahoo? django-compress в помощь.</title><content type='html'>&lt;p&gt;На сайте Yahoo developers &lt;a href="http://developer.yahoo.com/performance/rules.html"&gt;опубликованы&lt;/a&gt; правила оптимизации сайтов по скорости отдачи страниц клиентам. Ключевая мысль: самая медленная часть цепочки от запроса страницы до готовой картинки в броузере - передача файлов по сети. Соответственно, первое, что там предагается сделать, - минимизировать число HTTP-запросов.&lt;/p&gt; 
&lt;p&gt;То есть попросту: конкатенировать все css/javascript, которые грузятся радом, в один файл, поставить в страницу ссылку на этот файл - и так для всех групп. Еще бывает хорошо пропустить полученный файл через YUI Compressor, CSSTidy, JSMin или то, к чему вы привыкли. Пару раз это можно сделать руками. Потом становится скучно и хочется автоматизации.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog.elsdoerfer.name/2008/06/13/django-assets/"&gt;Оказывается&lt;/a&gt;, есть минимум 4 приложения для Django, создатели которых озаботились этой задачей. &lt;a href="http://github.com/mallipeddi/django-yslow/tree/master"&gt;django-yslow&lt;/a&gt;, &lt;a href="http://code.google.com/p/django-assetpackager/"&gt;django-assetpackager&lt;/a&gt;, &lt;a href="http://code.google.com/p/django-assets/"&gt;django-assets&lt;/a&gt; и &lt;a href="http://code.google.com/p/django-compress/"&gt;django-compress&lt;/a&gt;. Сразу скажу, мне понравился django-compress, о нём подробнее чуть ниже.&lt;/p&gt; Принципы работы остальных следующие. yslow рекурсивно собирает скрипты и стили в проекте. Особых настроек в settings.py я не нашел. Мне это не понравилось, я его не стал пробовать. assetspacker хранит информацию об assets в моделях. Мне не очевидна необходимость их использования. assets использует шаблонный тег для определения группируемых файлов. Я хотел, чтобы эта информация описывалась одновременно, а не была размазана по шаблонам.&lt;/p&gt;
&lt;p&gt;Для django-compress надо:
&lt;ol&gt;
&lt;li&gt;добавить "compress" в установленные приложения,&lt;/li&gt;
&lt;li&gt;добавить в settings.py необходимые настройки. Вот пример настройки, имена переменных говорящие.
&lt;pre&gt;&lt;code class="python"&gt;COMPRESS = True
COMPRESS_AUTO = False
COMPRESS_CSS = {
    "group_one": {
        "source_filenames": ("../css/reset.css",
                             "../css/common.css",
                             "../css/header.css",
                             "../css/searchbox.css",
                             "../css/footer.css",
                             "../css/breadcrumb.css",
                             "../css/table.css",
                             "../css/nbh.css",
                             "../css/tabs.css",
                             "../css/search.css",
                             "../css/ibox.css",),
        "output_filename": "../css/group_one.css"},
    "group_two": {
        "source_filenames": ("../css/2sided/reset.css",
                             "../css/2sided/ht_form.css",
                             "../css/2sided/ht_formSteps.css",
                             "../css/2sided/star_rating.css"),
        "output_filename": "../css/group_two.css"},
    }

COMPRESS_JS = {
    "group_one": {
        "source_filenames": ("../js/tabcontent.js",
                             "../js/jquery121.js",
                             "../js/tableactions.js",
                             "../js/ibox.js",),
        "output_filename": "../js/group_one.js"},
    "group_two": {
        "source_filenames": ("../js/user_registration_form/jquery_1.2.3.p.js",
                             "../js/user_registration_form/validate.js",
                             "../js/user_registration_form/maskedinput.js",
                             "../js/user_registration_form/dimensions.js",
                             "../js/user_registration_form/accordion.js",
                             "../js/user_registration_form/star_rating.js",
                             "../js/user_registration_form/metadata.js"),
        "output_filename": "../js/group_two.js"},
    }
COMPRESS_YUI_BINARY = "java -jar /opt/yuicompressor/yuicompressor-2.3.4/build/yuicompressor-2.3.4.jar"
COMPRESS_CSS_FILTERS = ("compress.filters.yui.YUICompressorFilter",)
COMPRESS_JS_FILTERS = ("compress.filters.yui.YUICompressorFilter",)&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;в шаблонах загрузить compressed и заменить нужные script и link  тегами {% compressed_js group_one %} и {% compressed_css group_two %}.&lt;/li&gt;
&lt;li&gt;python manage.py synccompress &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Нужно пояснить значение COMPRESS_AUTO. Если она True, то во время вызова шаблонного тега, выводящего script или link для обработанного файла, будут проверяться исходные файлы для него, и, если они устарели, обработанный файл создаётся заново. Так вот я это отключил, а вообще сжатие включил.&lt;/p&gt;
&lt;p&gt;Для простой конкатенации COMPRESS_xxx_FILTERS надо присовить None или ().&lt;/p&gt;
&lt;p&gt;CSS можно сжимать CSSTidy или YUI Compressor, JavaScript - JSMin или YUI Compressor.&lt;/p&gt;
&lt;p&gt;У команды synccompress есть опция --force. Она заставляет генерировать файлы без оглядки на даты последнего изменения файлов.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-7293813176606132877?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/7293813176606132877/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=7293813176606132877' title='Комментарии: 6'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/7293813176606132877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/7293813176606132877'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2008/07/yahoo-django-compress.html' title='Исключительная производительность по Yahoo? django-compress в помощь.'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-5386701765489847698</id><published>2008-07-12T19:12:00.005+03:00</published><updated>2008-07-12T19:33:40.919+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='тесты'/><category scheme='http://www.blogger.com/atom/ns#' term='monkeypatch'/><title type='text'>assertRedirectsGentle</title><content type='html'>У django.test.TestCase есть хороший метод assertRedirects с такой вот сигнатурой:
&lt;pre&gt;&lt;code class="python"&gt;assertRedirects(self, response, expected_url, status_code=302,
                        target_status_code=200, host=None)&lt;/code&gt;&lt;/pre&gt;
Этот метод проверяет не только куда ведёт редирект и с каком кодом был выполнен редирект, но и код, который возвращает expected_url. Обычно такое поведение проблем не доставляет, но иногда нужно, чтобы код возврата expected_url не проверялся, потому что при его проверке изменяется содержимое сессии. Для этого у меня есть такой код:
&lt;pre&gt;&lt;code class="python"&gt;from urlparse import urlsplit, urlunsplit
def assertRedirectsGentle(self, response, expected_url, status_code=302,
                          host=None):
        self.assertEqual(response.status_code, status_code,
            ("Response didn't redirect as expected: Response code was %d"
             " (expected %d)" % (response.status_code, status_code)))
        url = response['Location']
        scheme, netloc, path, query, fragment = urlsplit(url)
        e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
        if not (e_scheme or e_netloc):
            expected_url = urlunsplit(('http', host or 'testserver', e_path,
                    e_query, e_fragment))
        self.assertEqual(url, expected_url,
            "Response redirected to '%s', expected '%s'" % (url, expected_url))
def monkeypatch(test_case_class):
    test_case_class.assertRedirectsGentle = assertRedirectsGentle
assertRedirectsGentle.monkeypatch = monkeypatch&lt;/code&gt;&lt;/pre&gt;
Пользоваться так:
&lt;pre&gt;&lt;code class="python"&gt;from django.test import TestCase
from project.utils import assertRedirectsGentle
assertRedirectsGentle.monkeypatch(TestCase)&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-5386701765489847698?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/5386701765489847698/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=5386701765489847698' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/5386701765489847698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/5386701765489847698'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2008/07/assertredirectsgentle.html' title='assertRedirectsGentle'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-865288559062719852</id><published>2008-07-12T18:27:00.011+03:00</published><updated>2008-07-12T19:34:58.563+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='тесты'/><category scheme='http://www.blogger.com/atom/ns#' term='BeautifulSoup'/><category scheme='http://www.blogger.com/atom/ns#' term='soupselect'/><category scheme='http://www.blogger.com/atom/ns#' term='CSS'/><category scheme='http://www.blogger.com/atom/ns#' term='скреперы'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='парсеры'/><title type='text'>Селекторы CSS и BeautifulSoup</title><content type='html'>&lt;p&gt;Бывает нужно проверить наличие элемента на странице, узнать значение атрибута какого-то тега или сделать ещё что-то такое с HTML-документом (HTML, а не XML). Я для таких вещей использовал &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt; - он работает примерно так, и делает это хорошо.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&gt;&gt;&gt; html = '&amp;lt;html lang="ru"&gt;...&amp;lt;/html&gt;'
&gt;&gt;&gt; from BeautifulSoup import BeautifulSoup
&gt;&gt;&gt; soup = BeautifulSoup(html)
&gt;&gt;&gt; soup.find("html").get("lang")
ru&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Но &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; приучил к тому, что должна быть возможность адресоваться к элементам с помощью CSS нотации. Видимо, не меня одного.&lt;/p&gt;
&lt;a href="http://code.google.com/p/soupselect/"&gt;soupselect&lt;/a&gt; добавляет нужную функциональность.
&lt;pre&gt;&lt;code class="python"&gt;&gt;&gt;&gt; html = '&amp;lt;html lang="ru"&gt;&amp;lt;div class="title"&gt;&amp;lt;h3&gt;foo&amp;lt;/h3&gt;&amp;lt;/div&gt;&amp;lt;/html&gt;'
&gt;&gt;&gt; from BeautifulSoup import BeautifulSoup as Soup
&gt;&gt;&gt; from soupselect import select
&gt;&gt;&gt; select(soup, 'div.title h3')
[&amp;lt;h3&gt;foo&amp;lt;/h3&gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-865288559062719852?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/865288559062719852/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=865288559062719852' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/865288559062719852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/865288559062719852'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2008/07/html-html-xml.html' title='Селекторы CSS и BeautifulSoup'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7864195782654307449.post-5172094665346478114</id><published>2008-07-11T11:39:00.021+03:00</published><updated>2008-07-12T19:36:14.879+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='тесты'/><title type='text'>Как заменить tests.py пакетом.</title><content type='html'>В Django есть соглашение относительно расположения тестов в приложениях. По нему в tests.py живут тесты, работающие с unittest. Однажды я задумался о размещении тестов в отдельных файлах, то есть о преобразовании модуля tests в пакет tests.
&lt;br /&gt;
Почитал django/test/simple.py. Получилось, что если в модуле tests определяется callable переменная suite, то значение, возвращенное suite, добавляется к уже обнаруженным тестам. Если такой переменной не обнаружено, то из модуля tests пытаются загрузить тесты стандартным лоадером unittest.defaultTestLoader.loadTestsFromModule.
&lt;a href="http://groups.google.com/group/django-russian/browse_thread/thread/5d3dec8c9b0f0a03#"&gt;Спросил&lt;/a&gt; в &lt;a href="http://groups.google.com/group/django-russian"&gt;django-russian&lt;/a&gt;. &lt;a href="https://223-223.ru/repos/bis/branches/esizikov/test_performance_django_vs_pylons/test_django/blog/tests/"&gt;Предложенное решение&lt;/a&gt; немного не устроило - в каждый новый пакет tests кладётся адаптированный test_all.py, из которого импортируется suite.
&lt;br /&gt;
Мне нужно было, чтобы:
&lt;ul&gt;
&lt;li&gt;решение позволяло вызывать тесты как по manage.py test app, так и по manage.py test app.TestCase,&lt;/li&gt;
&lt;li&gt;основной код решения лежал бы где-то в утилитах проекта, и в __init__.py было бы просто пара строчек.&lt;/li&gt;
&lt;/ul&gt;

Попробовал переработать решение esizikov с функцией suite. Получилось не очень хорошо.
&lt;pre&gt;
&lt;code class="python"&gt;import os.path
import glob
import unittest

def suite():
   suite = unittest.TestSuite()
   file_full_path = os.path.abspath(os.path.join(__file__, '..'))
   app_name = file_full_path.split("/")[-2]
   for filename in glob.glob(file_full_path + "/[a-z]*.py"):
       module_name = os.path.basename(filename).split(".py")[0]
       full_module_name = "%s.tests.%s" % (app_name, module_name)
       suite.addTest(
           unittest.defaultTestLoader.loadTestsFromModule(
               __import__(full_module_name, {}, {}, [full_module_name])
               )
           )
   return suite
&lt;/code&gt;
&lt;/pre&gt;
Работает manage.py test app, не работает manage.py test app.TestCase.
Попробовал просто импортировать всё из модулей пакета.
&lt;pre&gt;
&lt;code class="python"&gt;from autologin_realtor import *
from login_homeowner import *
&lt;/code&gt;
&lt;/pre&gt;
Хорошо, всё работает. Но вписывать каждый новый модуль беспокойно. Пробуем импортировать тест-кейсы из модулей пакета автоматически.
&lt;pre&gt;
&lt;code class="python"&gt;import os
import glob
import unittest
import inspect

fr = inspect.currentframe()
file_full_path = os.path.abspath(os.path.join(__file__, '..'))
app_name = file_full_path.split("/")[-2]
for filename in glob.glob(file_full_path + "/[a-z]*.py"):
   module_name = os.path.basename(filename).split(".py")[0]
   full_module_name = "%s.tests.%s" % (app_name, module_name)
   m = __import__(full_module_name, {}, {}, [module_name])
   mdir = dir(m)
   for name in mdir:
       attr = getattr(m, name)
       if type(attr) is type:
           if issubclass(attr, unittest.TestCase):
               fr.f_globals[name] = attr
&lt;/code&gt;
&lt;/pre&gt;

Немного о том, что происходит. Импортируем модули, имена которых начинаются с латинской буквы, во избежание рекурсивного импорта __init__.py. Проверяем имена, видимые в каждом модуле. Если под каким-то именем оказывается класс-потомок unittest.TestCase, то создаём в нашем модуле (то есть в пакете tests) переменную с таким именем, указывающую на этот самый класс-потомок. То есть фактически импортируем руками нужные классы в пакет.
Хорошо, всё работает. Но это кусок кода лежит прямо в __init__.py. Развяжем немного.
Оставим в __init__.py только импорт функции и её вызов. Определение функции и необходимые для её работы импорты переносим в project.utils. Попутно изменяем функцию: теперь используется f_globals не текущего фрейма, а предыдущего.
&lt;br /&gt;
project/app/tests/__init__.py:
&lt;pre&gt;
&lt;code class="python"&gt;from project.utils import theimport
theimport(__file__)
&lt;/code&gt;
&lt;/pre&gt;
project/utils.py:
&lt;pre&gt;
&lt;code class="python"&gt;import os
import glob
import unittest
import inspect
def theimport(module_path):
   fr = inspect.stack()[1][0]
   file_full_path = os.path.abspath(os.path.join(module_path, '..'))
   app_name = file_full_path.split("/")[-2]
   for filename in glob.glob(file_full_path + "/[a-z]*.py"):
       module_name = os.path.basename(filename).split(".py")[0]
       full_module_name = "%s.tests.%s" % (app_name, module_name)
       m = __import__(full_module_name, {}, {}, [module_name])
       mdir = dir(m)
       for name in mdir:
           attr = getattr(m, name)
           if type(attr) is type:
               if issubclass(attr, unittest.TestCase):
                   fr.f_globals[name] = attr
&lt;/code&gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7864195782654307449-5172094665346478114?l=peroksid-on-django.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peroksid-on-django.blogspot.com/feeds/5172094665346478114/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7864195782654307449&amp;postID=5172094665346478114' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/5172094665346478114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7864195782654307449/posts/default/5172094665346478114'/><link rel='alternate' type='text/html' href='http://peroksid-on-django.blogspot.com/2008/07/testspy.html' title='Как заменить tests.py пакетом.'/><author><name>Александр Пугачев</name><uri>http://www.blogger.com/profile/07444915499867198510</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
