<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>LXj&#039;s blog</title>
	<atom:link href="http://lxj.endofinternet.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://lxj.endofinternet.net/blog</link>
	<description>*scratch*</description>
	<lastBuildDate>Mon, 12 Nov 2012 15:30:19 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Злободневное</title>
		<link>http://lxj.endofinternet.net/blog/2012/11/blacklistthis/</link>
		<comments>http://lxj.endofinternet.net/blog/2012/11/blacklistthis/#comments</comments>
		<pubDate>Mon, 12 Nov 2012 15:30:19 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Политика]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=452</guid>
		<description><![CDATA[Здание, в котором мы находимся, исчезнет с лица земли через десять минут. Возьмите одну часть 98%-ной дымящей азотной кислоты, и смешайте с тремя частями концентрированной серной кислоты. Делать это надо на ледяной бане.  Затем добавляйте глицерин по капле из глазной пипетки.  Вы получили нитроглицерин. 
Я знаю это, потому что это знает Тайлер. 
Смешайте [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>Здание, в котором мы находимся, исчезнет с лица земли через десять минут. Возьмите одну часть 98%-ной дымящей азотной кислоты, и смешайте с тремя частями концентрированной серной кислоты. Делать это надо на ледяной бане.  Затем добавляйте глицерин по капле из глазной пипетки.  Вы получили нитроглицерин. </p>
<p>Я знаю это, потому что это знает Тайлер. </p>
<p>Смешайте нитроглицерин с опилками, и вы получите отличный пластит. Некоторые предпочитают смешивать нитроглицерин с ватой и английской солью. Это тоже дает неплохой результат. А некоторые мешают нитру с парафином. Но при этом получается ненадежная взрывчатка. </p>
<p>Мы с Тайлером находимся на вершине небоскреба “Паркер-Моррис”.  Ствол пистолета засунут мне в рот.  Издалека доносится звук бьющегося стекла.<br />
Загляни за край крыши. День пасмурный, даже на этой высоте солнца не видно.  Это самое высокое здание в мире, так что на вершине его холодно даже летом. </p></blockquote>
<p>Чак Паланик, «Бойцовский клуб»</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2012/11/blacklistthis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django и Bootstrap</title>
		<link>http://lxj.endofinternet.net/blog/2012/11/django-and-bootstrap/</link>
		<comments>http://lxj.endofinternet.net/blog/2012/11/django-and-bootstrap/#comments</comments>
		<pubDate>Sun, 04 Nov 2012 15:22:02 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=450</guid>
		<description><![CDATA[Все любят Bootstrap. В своё время я его тоже полюбил, и начал искать, как бы его лучше всего прикрутить для форм в Django. Оказалось, что есть сразу несколько пакетов, которые в этом помогают &#8212; тогда мне лень было разбираться, и я просто начал использовать вот этот django-bootstrap
Сегодня я решил ещё раз взглянуть на всё это [...]]]></description>
			<content:encoded><![CDATA[<p>Все любят Bootstrap. В своё время я его тоже полюбил, и начал искать, как бы его лучше всего прикрутить для форм в Django. Оказалось, что есть сразу несколько пакетов, которые в этом помогают &#8212; тогда мне лень было разбираться, и я просто начал использовать <a href="https://github.com/earle/django-bootstrap">вот этот django-bootstrap</a></p>
<p>Сегодня я решил ещё раз взглянуть на всё это разнообразие, и оказалось, что сам разработчик django-bootstrap уже забросил его развитие, и рекомендует <a href="http://django-crispy-forms.readthedocs.org/">django-crispy-forms</a>. django-crispy-forms &#8212; это продолжение развития django-uni-form. django-uni-form в своё время хвалили в докладе Advanced <a href="http://pyvideo.org/video/82/djangocon-2011--advanced-django-form-usage">Django Form Usage на DjangoCon 2011</a>, на который я уже <a href="http://lxj.endofinternet.net/blog/2011/10/advanced-django-forms-usage/">ссылался</a>. Если вы этот доклад не видели &#8212; очень советую посмотреть, там рекомендуют довольно много чего полезного.</p>
<p>В общем, взглянув на описание django-crispy-forms, мне даже лень стало углубляться в различия <a href="https://github.com/tzangms/django-bootstrap-form">django-bootstrap-form</a> и <a href="https://github.com/dyve/django-bootstrap-toolkit">django-bootstrap-toolkit</a>. Оба можно поставить через pip, оба добавляют template filter для вывода формы (кстати, django-bootstrap вместо этого определял свои Form-классы). Но django-crispy-forms умеет всё это, плюс позволяет удобно управлять группировкой элементов форм, определять кнопки непосредственно в коде, поддерживает различные CSS-фреймворки и вообще выглядит как must-have пакет.</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2012/11/django-and-bootstrap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Почему AppEngine всё ещё сырой</title>
		<link>http://lxj.endofinternet.net/blog/2012/06/appengine-not-mature/</link>
		<comments>http://lxj.endofinternet.net/blog/2012/06/appengine-not-mature/#comments</comments>
		<pubDate>Thu, 21 Jun 2012 11:13:25 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Django]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=445</guid>
		<description><![CDATA[AppEngine существует уже 4 года, но до сих пор страдает от каких-то странных детских болезней. Самое очевидное &#8212; это &#171;голые&#187; домены: они просто не поддерживаются. Можно только разместить сайт на www.ваш-домен.com и изощряться с редиректами. Казалось бы, в 2012 году уже пора бы избавляться от www в адресах, но тут от этого никуда не деться. [...]]]></description>
			<content:encoded><![CDATA[<p>AppEngine существует уже 4 года, но до сих пор страдает от каких-то странных детских болезней. Самое очевидное &#8212; это &laquo;голые&raquo; домены: они просто <a href="https://developers.google.com/appengine/kb/general#naked_domain">не поддерживаются</a>. Можно только разместить сайт на www.ваш-домен.com и изощряться с редиректами. Казалось бы, в 2012 году уже пора бы избавляться от www в адресах, но тут от этого никуда не деться. <a href="http://code.google.com/p/googleappengine/issues/detail?id=777">Запрос в багтрекере</a> AppEngine&#8217;а висит с 2008 года, ни о каких планах Гугла это исправить не слышно.</p>
<p>Или вот в прошлом году выпустили поддержку Python 2.7, а вместе с ней &#8212; поддержку нескольких полезных библиотек (в том числе PIL) и возможность обрабатывать запросы одним инстансом в несколько потоков (что довольно важно, поскольку чем больше инстансов запускается, тем дороже обходится сайт). Казалось бы, можно радоваться? Как бы не так, перейдя на новую версию Python&#8217;а разработчики с удивлением обнаруживают, что время выполнения скриптов увеличивается в 2-3 раза, а когда для ответа на запрос AppEngine запускает новый инстанс, то это может занимать 30 секунд и больше для тяжёлых фреймворков, вроде Django-nonrel. <a href="http://stackoverflow.com/a/8405785/292080">Вот этот</a> ответ на StackOverflow отлично описывает ситуацию. Главная проблема в том, что использование многопоточности должно было сгладить увеличение расходов из-за новой модели ценообразования, введённой в прошлом году, но в результате от Python 2.7 толку до сих пор немного.</p>
<p>Есть и другие проблемы &#8212; тормозной SDK, отсутствие поддержки SSL и некоторые смешные баги, вроде <a href="http://code.google.com/p/googleappengine/issues/detail?id=2749">такого</a>. </p>
<p>При всём этом, AppEngine по-прежнему остаётся во многом интересной и перспективной платформой. Но скорость, с которой его разработчики решают все эти проблемы, сильно настораживает.</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2012/06/appengine-not-mature/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AppEngine: стоимость хранилища данных</title>
		<link>http://lxj.endofinternet.net/blog/2012/05/appengine-storage/</link>
		<comments>http://lxj.endofinternet.net/blog/2012/05/appengine-storage/#comments</comments>
		<pubDate>Fri, 11 May 2012 12:26:55 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Django]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=430</guid>
		<description><![CDATA[Когда обсуждают Google AppEngine и его стоимость, то обычно ведут речь об оплате процессорного времени. Изначально биллинг считал, сколько процессорного времени тратилось на обработку запросов, и за эти самые секунды выставлял счёт. В последствии, когда AppEngine вышел из стадии бета-тестирования, модель поменялась: Google стали брать за время работы инстансов, обслуживающих запросы. Но это всё на [...]]]></description>
			<content:encoded><![CDATA[<p>Когда обсуждают Google AppEngine и его стоимость, то обычно ведут речь об оплате процессорного времени. Изначально биллинг считал, сколько процессорного времени тратилось на обработку запросов, и за эти самые секунды выставлял счёт. В последствии, когда AppEngine вышел из стадии бета-тестирования, модель поменялась: Google стали брать за время работы инстансов, обслуживающих запросы. Но это всё на самом деле не интересно, и заметка будет совсем о другом :)</p>
<p>Один из основных компонентов AppEngine &#8212; это хранилище данных. Стандартным для новых приложений является <a href="https://developers.google.com/appengine/docs/python/datastore/overview">High Replication Datastore</a>, о нём и пойдёт речь. Замечу, что есть ещё Google Cloud SQL (по сути &#8212; &laquo;MySQL в облаке&raquo;), но он <del datetime="2012-05-11T12:13:00+00:00">пока что находится на стадии тестирования, и сколько он будет стоить через год, ещё неизвестно</del> довольно <a href="https://developers.google.com/cloud-sql/docs/billing">дорогой</a>, да и кому этот SQL нужен (шутка). <a href="https://developers.google.com/appengine/docs/billing#Billable_Resource_Unit_Costs">Модель рассчётов за использование хранилища данных такова</a>:</p>
<table>
<tr>
<th>
</th>
<th>
Стоимость
</th>
<th>
Бесплатная квота
</th>
</tr>
<tr>
<th>
Хранение данных
</th>
<td>
$0.24/гигабайт/месяц
</td>
<td>
1 гигабайт
</td>
</tr>
<tr>
<th>
Операции записи
</th>
<td>
$0.10 за 100k операций
</td>
<td>
50k операций/день
</td>
</tr>
<tr>
<th>
Операции чтения
</th>
<td>
$0.07 за 100k операций
</td>
<td>
50k операций/день
</td>
</tr>
<tr>
<th>
&laquo;Малые&raquo; операции
</th>
<td>
$0.01 за 100k операций
</td>
<td>
50k операций/день
</td>
</tr>
</table>
<p>В приведённой выше таблице под операцией чтения или записи имеются в виду низкоуровневые операции. К примеру, выполнение запроса к базе &#8212; это одна операция чтения + операция чтения на каждую возвращённую запись. Если же делать запрос, возвращающий не записи, а только ключи, то это будет посчитано как операция чтения + малая операция на каждую запись. Создание новой записи &#8212; это 2 операции записи + 2 операции записи на каждое индексированное поле + 1 операция записи на каждый композитный индекс.</p>
<p>Тут мы приходим к интересному выводу: если вы пробуете делать различные &laquo;игрушечные&raquo; проекты на AppEngine (чем я занимался в последнее время), и не хотите выходить за пределы бесплатных квот, то приходится довольно сильно ограничивать себя в количестве объектов, которые хочется поместить в базу: стоит туда запихнуть какую-то тысячу записей, как вы уже приближаетесь к суточному пределу операций записи. А если эти объекты ещё и должны несколько раз обрабатываться в течении дня, то уж точно нужно задумываться о переходе на платную модель. </p>
<p>Для серьёзных проектов, конечно, желательно приблизительно знать, с каким объёмом данных вам придётся работать в течении дня, и во сколько это будет обходиться. В принципе, можно <em>очень приблизительно</em> прикинуть, что запись несколько тысяч объектов (в пределах одной Django-модели) будет обходиться в 10 центов, что в принципе не так уж и много. Учитывая, что перейдя на платную модель, приходится тратить не менее 8 долларов в месяц, в пределах этого бюджета можно оперировать довольно большими объёмами данных.</p>
<p>На самом деле, при определённых объёмах данных (несколько тысяч записей) довольно легко хранить их в обыкновенных списках и словарях, которые можно сериализовать в JSON или pickle-объекты, и хранить в memcache, время от времени сохраняя в базу. Для волатильных данных такой подход будет очень даже актуален.</p>
<p>Самое главное &#8212; данная модель ценообразования основана на принципе &laquo;плачу за то, что использую&raquo;. Если в один день у вас большой поток данных, то на следующий вы вполне можете уложиться в бесплатные квоты, никакие настройки при этом менять не надо. Amazon Dynamo DB, к примеру требует резервировать определённый набор ресурсов, и постоянно за них платить. Если на том же Amazon EC2 хостить инстансы с MongoDB, то принцип тарификации будет тот же. В итоге, использование AWS требует определять максимальную нагрузку, и всегда её оплачивать, с AppEngine&#8217;овским же датастором вы задаёте лимиты, но платите только за те ресурсы, которые потребили.</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2012/05/appengine-storage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Now I&#8217;m the One That&#8217;s Cool</title>
		<link>http://lxj.endofinternet.net/blog/2012/04/now-im-the-one-thats-cool/</link>
		<comments>http://lxj.endofinternet.net/blog/2012/04/now-im-the-one-thats-cool/#comments</comments>
		<pubDate>Tue, 03 Apr 2012 10:32:23 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[In the web]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=428</guid>
		<description><![CDATA[Like, totally!

Новый канал Фелиши Дэй и её друзей
]]></description>
			<content:encoded><![CDATA[<p>Like, totally!</p>
<p><iframe width="560" height="315" src="http://www.youtube.com/embed/jFhgupR565Q" frameborder="0" allowfullscreen></iframe></p>
<p><a href="http://www.youtube.com/user/geekandsundry?feature=watch">Новый канал Фелиши Дэй и её друзей</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2012/04/now-im-the-one-thats-cool/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Андроидные приложения</title>
		<link>http://lxj.endofinternet.net/blog/2011/12/android-apps/</link>
		<comments>http://lxj.endofinternet.net/blog/2011/12/android-apps/#comments</comments>
		<pubDate>Fri, 09 Dec 2011 23:09:00 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=417</guid>
		<description><![CDATA[В начале года я купил Android-смартфон LG Optimus One, а на днях какой-то андроидный телефон купил мой брат. Для него я сделал список приложений, которые за это время накопились на моём телефоне, и заодно публикую этот список здесь. В него не входят клиенты для всяких твиттеров, дропбоксов и прочих форскверов, ну и те приложения, которые [...]]]></description>
			<content:encoded><![CDATA[<p>В начале года я купил Android-смартфон LG Optimus One, а на днях какой-то андроидный телефон купил мой брат. Для него я сделал список приложений, которые за это время накопились на моём телефоне, и заодно публикую этот список здесь. В него не входят клиенты для всяких твиттеров, дропбоксов и прочих форскверов, ну и те приложения, которые стоят на LG-шных смартфонах по умолчанию.</p>
<p><a href="https://market.android.com/details?id=com.rechild.advancedtaskkiller">Advanced Task Killer</a><br />
Многие говорят, что это самое нужное приложение на андроиде :) Если честно, я от его использования особой разницы в &laquo;тормозах&raquo; не заметил. Выключить-включить обычно надёжнее :) (а ещё лучше просто удалить виновное приложение)</p>
<p><a href="https://market.android.com/details?id=com.rovio.angrybirds">Angry Birds</a><br />
Птички, куда же без них.</p>
<p><a href="https://market.android.com/details?id=com.splunchy.android.alarmclock">AlarmDroid</a><br />
Настраиваемый будильник</p>
<p><a href="https://market.android.com/details?id=mobi.beyondpod">BeyondPod</a><br />
Подкаст-фетчер, бесплатная версия не умеет сама по расписанию проверять и скачивать подкасты, и ограничена одной закачкой одновременно. У меня на Оптимусе ощутимо подтормаживает, но какое-то другое приложение для подкастов, которое я пробовал, было ещё тормознее, а скачивать вручную и слушать встроенным плеером &#8212; неудобно.</p>
<p><a href="https://market.android.com/details?id=com.bbb.btr">Burn the Rope</a><br />
Прикольная игрушка. В принципе, я редко играю на мобильнике, но если что-то и запускаю &#8212; то обычно именно её</p>
<p><a href="https://market.android.com/details?id=com.google.android.apps.chrometophone">Chrome to Phone</a><br />
Или же <a href="https://market.android.com/details?id=fi.xd.android.apps.khrometophone">Khrome to Phone</a><br />
Приложение + экстеншн для Хрома для быстрого открытия ссылки из &laquo;большого&raquo; браузера на телефоне. Первый был почему-то недоступен для установки, но второй замечательно у меня работает</p>
<p>Собственно, экстеншн для Хрома брать <a href="https://chrome.google.com/webstore/detail/oadboiipflhobonjjffjbfekfjcgkhco">тут</a></p>
<p><a href="https://market.android.com/details?id=com.apksoftware.compass">Compass</a><br />
Компас. Duh.</p>
<p><a href="https://market.android.com/details?id=com.zeptolab.ctr.paid">Cut the Rope</a><br />
Ещё одна игрушка. Я её ставил из какого-то альтернативного аппстора, там она по акции раздавалось бесплатно.</p>
<p><a href="https://market.android.com/details?id=mobi.mgeek.TunnyBrowser">Dolphin Browser HD</a><br />
Альтернативный браузер, есть вкладки и поддерживает экстеншены. Собственно, поставил я его ради экстеншена LastPass for Dolphin HD (LastPass &#8212; сервис для хранения и синхронизации паролей)</p>
<p>Слышал, как хвалили/ругали андроидную Оперу, не пробовал</p>
<p><a href="https://market.android.com/details?id=org.geometerplus.zlibrary.ui.android">FBReader</a><br />
Читалка</p>
<p><a href="https://market.android.com/details?id=com.mediawoz.goweather">GO Weather</a><br />
Красивый виджет с погодой. Также умеет менять фон &laquo;рабочего стола&raquo; в зависимости от погоды и времени суток</p>
<p><a href="https://market.android.com/details?id=com.google.android.apps.unveil">Google Goggles</a><br />
Фотопоиск. Умеет сканировать штрихкоды, решать судоку и теоретически находить информацию о чём угодно, на что ты направил камеру. Лично я немного с ним поигрался и оставил как сканер QR-кодов</p>
<p><a href="https://market.android.com/details?id=com.mxtech.videoplayer.ad">MX Video Player</a><br />
Проигрыватель видео со своими кодеками. Нужен, потому что встроенный плеер понимает далеко не все видеоформаты/кодеки (скажем, DivX). Естественно, на смартфоне с маленьким экраном и слабым процессором особо не насмотришься, но при желании (и везении) &#8212; можно :)</p>
<p><a href="https://market.android.com/details?id=com.bfs.papertoss">Paper Toss</a><br />
Игрушка</p>
<p><a href="https://market.android.com/details?id=ua.privatbank.ap24">Privat24</a><br />
Мобильная версия Приват24. У приложения гвоздями прибит портретный режим, из-за этого очень неудобно набирать пароль (а возможности запомнить нет), поэтому почти не пользуюсь</p>
<p><a href="https://market.android.com/details?id=com.skype.raider">Skype</a><br />
Skype для андроида. В своё время были проблемы с устновкой через маркет, так что пришлось качать пакет с форума 4pda.ru. У меня он тормозил и иногда просто зависал, так что не пользуюсь</p>
<p><a href="https://market.android.com/details?id=ua.gradsoft.android.tpoptimizer">Tariff Plan Optimizer UA</a><br />
Анализирует статистику звонков/смсок и показывает, сколько бы ты денег потратил на разных тарифах.</p>
<p><a href="https://market.android.com/details?id=com.iba.ussdchecker">USSD Checker</a><br />
Проверяет состояние счёта через USSD-запрос и выводит на виджет. Платная версия может автоматически обновлять счёт после каждого исходящего звонка/SMS, бесплатная проверяет только раз в сутки в 23:55 (можно проверить вручную в любой момент в два касания)</p>
<p>Ну и на выбор две киноафиши:<br />
<a href="https://market.android.com/details?id=ua.in.leopard.androidCoocooAfisha">PopCornUA</a><br />
<a href="https://market.android.com/details?id=ru.yandex.afisha">Яндекс.Киноафиша</a></p>
<p>Преимущество PopCornUA &#8212; он умеет загрузить информацию на телефон, и потом работать без подключения к интернету. Для Яндекс.Киноафиши постоянно требуется инет, зато она более удобная, красивая, и от православного Яндекса</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2011/12/android-apps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>django-mediagenerator</title>
		<link>http://lxj.endofinternet.net/blog/2011/12/django-mediagenerator/</link>
		<comments>http://lxj.endofinternet.net/blog/2011/12/django-mediagenerator/#comments</comments>
		<pubDate>Fri, 09 Dec 2011 23:24:17 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=415</guid>
		<description><![CDATA[django-mediagenerator &#8212; менеджер статических файлов &#171;от создателей&#187; Django-nonrel. Сейчас уже все знают, что для управления статическими файлами есть django.contrib.staticfiles, но на самом деле стандартным он стал совсем недавно, в Django 1.3, а до этого успели написать несколько разных решений. Что же такого интересного в django-mediagenerator, что отличает его от staticfiles?
Во-первых, JS и CSS-файлы можно собирать [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.allbuttonspressed.com/projects/django-mediagenerator">django-mediagenerator</a> &#8212; менеджер статических файлов &laquo;от создателей&raquo; Django-nonrel. Сейчас уже все знают, что для управления статическими файлами есть django.contrib.staticfiles, но на самом деле стандартным он стал совсем недавно, в Django 1.3, а до этого успели написать несколько разных <a href="http://djangopackages.com/grids/g/asset-managers/">решений</a>. Что же такого интересного в django-mediagenerator, что отличает его от staticfiles?</p>
<p>Во-первых, JS и CSS-файлы можно собирать в &laquo;бандлы&raquo; &#8212; проще говоря, множество файлов можно собрать в один, что уменьшит необходимое количество запросов. При этом прозрачно поддерживаются всевозможные SASS, CoffeeScript и прочие &laquo;улучшатели&raquo; CSS и JS, вплоть до того, что итоговый CSS файл можно собрать из комбинации нескольких файлов CSS, SASS, LESS и т.д. Заодно при генерации итогового файла для заливки на продакшн-сервер его можно автоматически сжать YUICompressor или другим компрессором (на отладочном сервере они так и будут отдаваться как множество файлов и без всякой компрессии)</p>
<p>Во-вторых, абсолютно все файлы версионируются. Другими словами, больше не будет ситуации когда заказчик после обновления сайта по-прежнему видит старый дизайн, поскольку, к примеру, <code>main.css</code> в одной версии автоматически переименовывается в <code>main-4f3fcb40.css</code>, а в следующей &#8212; уже в <code>main-390ba896.css</code>. Заодно можно указать в настройках кэширования, чтобы файлы в каталоге со статическими файлами никогда не устаревали</p>
<p>При этом на отладочном сервере вся &laquo;магия&raquo; происходит через middleware и генерируемые файлы никуда не складируются, а перед установкой на продакшн достаточно запустить <code>manage.py generatemedia</code>.</p>
<p>Увы, пока что django-mediagenerator имеет кучу проблем, если пытаться использовать его на серьёзных проектах.</p>
<p>Во-первых, на отладочном сервере он уж очень сильно увеличивает время генерации страницы, когда количество статических файлов начинает исчисляться сотнями. В обсуждении на багтрекере уже даже <a href="https://bitbucket.org/wkornewald/django-mediagenerator/issue/29/making-mediagenerator-blazingly-fast-in#comment-727880">разобрались</a>, почему так происходит, но пока что для исправления этой проблемы есть только сторонний патч, требующий перезапуска сервера после каждого изменения в статических файлах.</p>
<p>Во-вторых, к отдельным CSS и JS-файлам по идеологии django-mediagenerator просто так не обратиться, нужно обязательно определять бандл. А бандл, к тому же, можно задавать только в корневом settings.py проекта, что усложняет написание повторно используемых приложений.</p>
<p>Ну и в-третьих необходимость особым образом работать с именами всех статических файлов (а не просто добавлять <code>{{ STATIC_URL }}</code> в путь) добавляет головной боли. В конечном итоге, я прикинул, как бы мне добавить поддержку django-mediagenerator в django-tinymce, и очень быстро раздумал это делать :)</p>
<p>Всё те же разработчики сделали CMS <a href="http://www.allbuttonspressed.com/projects/allbuttonspressed">All Buttons Pressed</a> (поскольку другие CMS на Django не совместимы с Django-nonrel), в которой, конечно же, использовали django-mediagenerator. Примечательно, что статических файлов с ней идёт немного, и вместо WYSIWYG-редактора предполагается использовать разновидность вики-разметки. Сейчас я переделываю эту CMS под себя, и уже использую staticfiles и django-tinymce.</p>
<p>Ну и самое главное &#8212; авторы Django-nonrel совсем недавно объявили, что прекращают заниматься своими Django-проектами. Для самого Django-nonrel уже нашёлся новый мейнтейнер, но будут ли дальше развивать ABP и mediagenerator, пока непонятно</p>
<p>Добавка: <a href="http://stackoverflow.com/a/8498467/292080">расписал на StackOverflow</a>, как правильно использовать staticfiles c Django-nonrel/djangoappengine</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2011/12/django-mediagenerator/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Django и AppEngine: даты и диапазоны</title>
		<link>http://lxj.endofinternet.net/blog/2011/10/django-gae-range-filter/</link>
		<comments>http://lxj.endofinternet.net/blog/2011/10/django-gae-range-filter/#comments</comments>
		<pubDate>Tue, 25 Oct 2011 08:28:35 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=407</guid>
		<description><![CDATA[В последнее время активно осваиваю Google AppEngine и Django-nonrel. Писать под nosql-БД &#8212; это, оказывается, весьма интересно и познавательно. Если реляционные БД хорошо изучены, и различные аспекты представления данных в табличном виде давно задокументированы, то переориентировашись на BigTable или Mongo, приходится думать самому &#8212; как бы так распихать данные по таблицам, чтобы было и эффективно, [...]]]></description>
			<content:encoded><![CDATA[<p>В последнее время активно осваиваю Google AppEngine и Django-nonrel. Писать под nosql-БД &#8212; это, оказывается, весьма интересно и познавательно. Если реляционные БД хорошо изучены, и различные аспекты представления данных в табличном виде давно задокументированы, то переориентировашись на BigTable или Mongo, приходится думать самому &#8212; как бы так распихать данные по таблицам, чтобы было и эффективно, и без JOIN&#8217;ов.</p>
<p>Но нереляционные БД &#8212; это не только денормализация табличек, это ещё и дополнительные ограничения. Например, через некоторое время работы с AppEngine, я наткнулся на <a href="http://code.google.com/intl/ru/appengine/docs/python/datastore/queriesandindexes.html#Restrictions_on_Queries">вот такое</a>:</p>
<blockquote><p>Запрос может использовать фильтры неравенства (<, <=, >=,>, !=) только по одному свойству среди всех своих фильтров.</p></blockquote>
<p>Дело в том, что в разрабатываемом мной сайте есть несколько моделей с полями вроде date1 и date2, и, соответственно, время от времени нужно выводить список объектов по дате date, у которых date1 <= date <= date2. Спрашивается, как это делать используя не более двух операций неравенства?</p>
<p>Я решил воспользоваться тем, что BigTable поддерживает поля типа <a href="http://code.google.com/intl/ru/appengine/docs/python/datastore/typesandpropertyclasses.html#ListProperty">ListProperty</a> (Django-nonrel, соответственно, <a href="http://www.allbuttonspressed.com/projects/djangoappengine#field-types">поддерживает поля типа ListField</a>, вот только документации по нему нет ^^), при этом <a href="http://code.google.com/intl/ru/appengine/docs/python/datastore/entitiesandmodels.html#Lists">вхождение в список</a> в GQL проверяется вовсе не оператором IN (который ограничен 30-ю значениями), а оператором &#8216;=&#8217;</p>
<p>Непосредственно в джанго-приложении это выразилось таким кодом:</p>
<pre><code># models.py

def _date_range(date1, date2):
    result = []
    while (date1 <= date2):
        result.append(date1)
        date1 += datetime.timedelta(1)
    return result

class MyModel(models.Model):
    # ...
    date1 = models.DateField('Дата начала')
    date2 = models.DateField('Дата окончания')
    date_range = ListField(models.DateField(), blank=True)
    # ...
    def save(self):
        self.date_range = _date_range(self.date1, self.date2)
        super(MyModel, self).save()</code></pre>
<pre><code># views.py

def transp(request, day, month, year):
    # ...
    objects = Transport.objects.order_by('-updated')
    if (day):
        the_date = datetime.datetime(int(year), int(month), int(day))
        objects = objects.filter(date_range__exact = the_date)
    # ...
</code></pre>
<p>Тут есть один странный подводный камень -- нужно использовать именно datetime.datetime, а не datetime.date (не смотря на то, что у меня везде DateField, а не DateTimeField). </p>
<p>На первый взгляд кажется, что такое представление данных -- очень неэффективно. Но если посмотреть на эту модель с точки зрения того, каким образом будут работать индексы, то окажется, что поиск с такой моделью будет работать быстрее! Не случайно в AppEngine ограничены именно фильтры неравенства: фильтр равенства сводится к простой и эффективной выборке по определённому индексу.</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2011/10/django-gae-range-filter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Advanced Django Forms Usage</title>
		<link>http://lxj.endofinternet.net/blog/2011/10/advanced-django-forms-usage/</link>
		<comments>http://lxj.endofinternet.net/blog/2011/10/advanced-django-forms-usage/#comments</comments>
		<pubDate>Fri, 14 Oct 2011 15:40:30 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Django]]></category>
		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=385</guid>
		<description><![CDATA[Один из самых познавательных докладов на недавнем DjangoCon&#8217;е &#8212; Advanced Django Forms Usage
Вот, к примеру код, подобный которому можно встретить в любом туториале по Django: 

Автор доклада показывает, как тоже самое можно описать более кратко и наглядно:

Число строк кода сократилось с 9 до 6, и форма инстанцируется всего в одном месте. Ниже я привёл несколько [...]]]></description>
			<content:encoded><![CDATA[<p>Один из самых познавательных докладов на недавнем DjangoCon&#8217;е &#8212; <a href="http://blip.tv/djangocon/advanced-django-form-usage-5573287">Advanced Django Forms Usage</a></p>
<p>Вот, к примеру код, подобный которому можно встретить в <a href="http://www.djangobook.com/en/2.0/chapter07/#cn149">любом туториале по Django</a>: </p>
<p><a href="http://lxj.endofinternet.net/blog/2011/10/advanced-django-forms-usage/djangoforms1/" rel="attachment wp-att-390"><img src="http://lxj.endofinternet.net/blog/wp-content/uploads/2011/10/djangoforms1-500x371.png" alt="" title="djangoforms1" width="500" height="371" class="aligncenter size-large wp-image-390" /></a></p>
<p>Автор доклада показывает, как тоже самое можно описать более кратко и наглядно:</p>
<p><a href="http://lxj.endofinternet.net/blog/2011/10/advanced-django-forms-usage/djangoforms2/" rel="attachment wp-att-391"><img src="http://lxj.endofinternet.net/blog/wp-content/uploads/2011/10/djangoforms2-500x375.png" alt="" title="djangoforms2" width="500" height="375" class="aligncenter size-large wp-image-391" /></a></p>
<p>Число строк кода сократилось с 9 до 6, и форма инстанцируется всего в одном месте. Ниже я привёл несколько полезных сниппетов из того же доклада:</p>
<pre><code>
# view with a form

def my_view(request, template_name='myapp/myform.html'):
    form = MyForm(request.POST or None)
    if form.is_valid():
        do_x()
        return redirect('home')
    return render(request, template_name, {'form': form})

# view with files

def my_view(request, template_name='myapp/myform.html'):
    form = MyForm(request.POST or None, request.FILES or None)
    if form.is_valid():
        do_x()
        return redirect('home')
    return render(request, template_name, {'form': form})

# view for editing a model instance

def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):
    mymodel = get_object_or_404(MyModel, slug=slug)
    form = MyModelForm(request.POST or None, instance=mymodel)
    if form.is_valid():
        mymodel = form.save()
        // ...
        mymodel.save()
        return redirect('home')
    return render(request, template_name, {'form': form, 'mymodel': mymodel})

# view for creating a model instance

def my_model_add(request, slug=slug, template_name='myapp/my_model_form.html'):
    form = MyModelForm(request.POST or None)
    if form.is_valid():
        form.save()
        return redirect('home')
    return render(request, template_name, {'form': form})

# forms overloading

class MyModelForm(forms.ModelForm):
    def __init__(self):
        super(MyModelForm, self).__init__(*args, **kwargs)
        self.fields['name'].required = True
        self.fields['age'].required = True
        self.fields['profession'].required = True
        self.fields['profession'].help_text = _('Hi professor')

    class Meta:
        model = MyModel  

# dynamically adding fields to a form

def my_view(request, template_name='myapp/my_model_form.html'):
    form = MyModelForm(request.POST or None)

    # Let's add a field on the go, needs to be done before validating it
    form.fields['favorite_color'] = forms.ChoiceField(
        ...
    )

    if form.is_valid():
        # Let's get user's favorite color,
        # you can do whatever you want with it
        favorite_color = form.cleaned_data['favorite_color']

        form.save()
        return redirect('home')

    return render(request, template_name, {'form': form})
</code></pre>
<p>Добавлено: <code>render</code> и <code>redirect</code> импортятся, конечно же, из <a href="https://docs.djangoproject.com/en/1.3/topics/http/shortcuts/">django.shortcuts</a>. Обратите внимание, что render добавлен в Django 1.3 (и его удобно использовать вместо <code>render_to_response</code> и <code>direct_to_template</code>).</p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2011/10/advanced-django-forms-usage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Amazon Kindle 3G: впечатления</title>
		<link>http://lxj.endofinternet.net/blog/2011/08/kindle/</link>
		<comments>http://lxj.endofinternet.net/blog/2011/08/kindle/#comments</comments>
		<pubDate>Mon, 22 Aug 2011 14:58:12 +0000</pubDate>
		<dc:creator>LXj</dc:creator>
				<category><![CDATA[Гаджеты]]></category>

		<guid isPermaLink="false">http://lxj.endofinternet.net/blog/?p=367</guid>
		<description><![CDATA[Что-то я с апреля никак не опубликую эту заметку. Надо бы наконец-то привести её в порядок и выложить :)
Экран, конечно, не идеально белый, и требует хорошего освещения (впрочем, как и обычные книги). Но читать с него приятно. Размер 6&#8243; меня вполне устраивает &#8212; для чтения PDFов всё равно лучше накопить на планшет. Для художественных книг [...]]]></description>
			<content:encoded><![CDATA[<p>Что-то я с апреля никак не опубликую эту заметку. Надо бы наконец-то привести её в порядок и выложить :)</p>
<p><strong>Экран</strong>, конечно, не идеально белый, и требует хорошего освещения (впрочем, как и обычные книги). Но читать с него приятно. Размер 6&#8243; меня вполне устраивает &#8212; для чтения PDFов всё равно лучше накопить на планшет. Для художественных книг в PDF также помогает <a href="http://www.willus.com/archive/">вот эта программа</a> (см. также <a href="http://vyazovoi.blogspot.com/2011/07/e-ink-pdf.html">эту заметку</a>)</p>
<p><strong>Управление</strong> не идеальное, ибо в 2011 году устройство без тача кажется слегка устаревшим. Но совсем неудобным его не назвать. Наверное, в следующем Kindle будет тач, но учитывая периодичность выхода новых поколений Kindle (кроме DX) &#8212; они сменяются примерно раз в полтора года, так что следующий Kindle нужно было бы ждать ещё год (купил я его в марте).</p>
<p>Кожаная <strong>обложка</strong> смотрится весьма солидно, хорошо держится на креплении, и главное &#8212; имеет выдвижной фонарик, который питается от самого устройства. Этой подсветки почти хватает для комфортного чтения (всё-таки правый верхний угол освещён хорошо, а левый нижний &#8212; слабо). За такую обложку не жалко и 60$&#8230; Почти :) Не хватает только <a href="http://xkcd.com/548/">надписи &laquo;DON&#8217;T PANIC&raquo;</a></p>
<p>(Есть варианты обложки подешевле, но без встроенной подсветки)</p>
<p><strong>Браузер</strong> действительно позволяет бесплатно смотреть любые сайты. Даже если там есть куча джаваскрипта и анимация. Естественно, анимация выглядит весьма забавно на таком экране, прокрутка экрана ощутимо тормозит, а ссылки приходится выбирать джойстиком. Но всё равно &#8212; иметь возможность бесплатно выйти в интернет по всему миру &#8212; это просто круто. Можно проверять почту и писать в твиттер. Наверняка можно общаться через какой-нибудь GTalk, хотя я не пробовал.</p>
<p>Понятное дело, скачать никакие файлы не дают. Но самое неприятное другое &#8212; из-за отсутствия вкладок браузер не переваривает ссылки, которые должны открываться в новой вкладке/окне. Потому, например, если вы пришлёте себе ссылку по электронке, а затем на Киндле попробуете зайти на GMail и &laquo;кликнуть&raquo; по этой ссылке, то увидите только окошко с ошибкой. Та же проблема &#8212; и с Google Reader.</p>
<p>Зато чем хорош Google Reader &#8212; так это своим клавиатурным управлением: j/k для предыдущей/следующей заметки (или n/p для выбора и o для отрытия), u для скрытия панели со списком каталогов/лент &#8212; и читать из него становится относительно удобно. Но только если в соответствующих лентах тексты заметок отдаются полностью. Мне как раз в одно время с покупкой Kindle брат показал <a href="http://storyofgrubas.livejournal.com/">вот этот блог</a>, я подписался на него в Google Reader, занёс все заметки в starred, и большую часть прочитал именно на Kinde&#8217;е.</p>
<p>Шрифт, правда, на большинстве веб-сайтов всё-таки мелковат для долгого чтения. Можно, конечно, увеличить его до 150%, но на том же Google Reader тогда сильно расползается вёрстка и браузер начинает ощутимо тормозить.</p>
<p>&laquo;Умной&raquo; подгонки ширины текста в многоколоночной вёрстке, как на современных мобильных браузерах, тоже нет. Так что на некоторых сайтах (к примеру, <a href="http://teamliquid.net">TeamLiquid</a>), если не сделать приближения, то текст будем слишком мелким, а если приблизить &#8212; часть текста будем обрезана по левому или правому краю. </p>
<p><strong>Kindle Store</strong> вполне удобен. Что касается цен &#8212; вот, например, <a href="http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=pd_sim_b_1">Design Patterns: Elements of Reusable Object-Oriented Software</a> в печатном виде на Amazon&#8217;е стоит $59.99 ($41.56 при доставке по Штатам), а в версии для Kindle &#8212; $28.39. Конечно же, с DRM, и конечно, байты не пощупать и не понюхать, но &#8212; разница налицо. Правда, видел упоминания в блогах, что некоторые книги &#8212; наоборот, неоправданно дороги (см. также <a href="http://feedproxy.google.com/~r/MaxIschenkoRU/~3/PpxHPW4HFEc/9219288259">заметку Макса Ищенко</a>). Купленные книги в любой момент можно скачать из Амазона. Напомню также, что их можно читать с помощью приложений <a href="http://www.amazon.com/gp/feature.html/ref=kinw_myk_dv_reg_ln?ie=UTF8&#038;docId=1000493771">на куче разных платформ</a> &#8212; Linux&#8217;а там, правда, нет, но не давно <a href="http://itc.ua/news/amazon_vypustila_veb-versiyu_kindle_cloud_reader_optimizirovannuyu_dlya_ipad_55011">была запущена веб-версия</a>.</p>
<p>Устройство видится с компьютера, как обычная флешка, но лучше всё-таки использовать <a href="http://calibre-ebook.com/">Calibre</a> &#8212; хотя бы потому, что форматов Kindle понимает мало, а Calibre их все умеет конвертировать. Также замечу, что гугл может помочь вам найти прошивки с русификацией и поддержкой русской клавиатуры &#8212; сам я их, впрочем, не пробовал.</p>
<p>В целом, если сравнивать с другими читалками, то недостатки Kindle явно менее критичные, чем его преимущества. Бесплатный доступ к интернету практически где угодно &#8212; это настолько killer feature, что даже объяснять как-то неудобно. </p>
<p><a href="http://www.mobile-review.com/articles/2010/amazon-kindle3.shtml">Обзор Mobile Review</a><br />
<a href="http://www.exler.ru/expromt/09-11-2010.htm">Обзор Экслера</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lxj.endofinternet.net/blog/2011/08/kindle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
