<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;A0YAR345fSp7ImA9WhVXEUU.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347</id><updated>2012-04-12T04:05:46.025+04:00</updated><category term="queryset" /><category term="полезно" /><category term="flash" /><category term="Visual Studio" /><category term="lighttpd" /><category term="UNetbootin" /><category term="postgresql" /><category term="SQL" /><category term="сео" /><category term="3d" /><category term="GWT Designer" /><category term="network manager" /><category term="gitextensions" /><category term="компьютеры" /><category term="lenny" /><category term="pip" /><category term="img" /><category term="firebug" /><category term="django-json-rpc" /><category term="национализм" /><category term="интеренет" /><category term="gwt" /><category term="объявление" /><category term="modelforms" /><category term="EGit" /><category term="Restlet Framework" /><category term="validation" /><category term="Web" /><category term="кризис" /><category term="django-maintenancemode" /><category term="проза" /><category term="render" /><category term="git" /><category term="fabric" /><category term="ORM" /><category term="заработок" /><category term="интересное" /><category term="gwt-json-rpc" /><category term="музыкальные жанры и стили" /><category term="django-notification" /><category term="freebsd" /><category term="истеричные женщины" /><category term="scrolling position" /><category term="скачать" /><category term="who lock me" /><category term="Google Chorme" /><category term="виртуализация" /><category term="udftools" /><category term="сайт" /><category term="skydrive" /><category term="South" /><category term="django-messages" /><category term="Eclipse Marketplace" /><category term="mysql" /><category term="java" /><category term="нацизм" /><category term="Google Code" /><category term="traceback" /><category term="field" /><category term="apcupsd" /><category term="D-Link DCS-950G" /><category term="django" /><category term="bash" /><category term="gui" /><category term="оффтопик" /><category term="trac" /><category term="CppCMS" /><category term="Puma.NET" /><category term="dns" /><category term="осень" /><category term="RequestContext" /><category term="etch" /><category term="view" /><category term="Eclipse" /><category term="Phusion Passenger" /><category term="chromebug" /><category term="Nhibernate" /><category term="файловые менеджеры" /><category term="quickstart" /><category term="wicd" /><category term="Бердяев" /><category term="файловая система" /><category term="widget" /><category term="formsets" /><category term="svn" /><category term="subversion" /><category term=".NET" /><category term="Redmine" /><category term="re" /><category term="мысли" /><category term="javascript" /><category term="asus eee pc" /><category term="udf" /><category term=".NET C#" /><category term="Visual Studio 2005" /><category term="классика" /><category term="inclusion_tag" /><category term="musicmans.ru" /><category term="blender" /><category term="csh" /><category term="template" /><category term="Vinagre" /><category term="form" /><category term="C++" /><category term="Context" /><category term="attrs" /><category term="политика" /><category term="ldap" /><category term="amd64" /><category term="PyDev" /><category term="блоги" /><category term="n800" /><category term="python" /><category term="wsgi" /><category term="Framework" /><category term="работа" /><category term="AVerTV" /><category term="debian" /><category term="forms" /><category term="windows" /><category term="rtorrent" /><category term="LockHunter" /><category term="mod_rails" /><category term="гикство" /><category term="squeeze" /><category term="боль" /><category term="#" /><category term="kvm" /><category term="интернет" /><category term="django-registration" /><category term="izmenimsya.ru" /><category term="музыка" /><category term="жизнь" /><category term="linux" /><category term="debug" /><category term="rutorrent" /><category term="Россия" /><category term="очумелые ручки" /><category term="бекап" /><category term="mylyn" /><category term="Google Web Toolkit" /><category term="kubuntu" /><category term="REST" /><category term="patterns" /><category term="formset" /><category term="любовь" /><category term="python-mssql" /><category term="америка" /><category term="modules" /><category term="оборона" /><category term="vnc" /><category term="Google Web Toolkit 2" /><category term="хранилище" /><category term="virtual server" /><category term="tar" /><category term="apache2" /><category term="C#" /><category term="цитаты" /><category term="regex" /><category term="толкования" /><category term="Process Explorer" /><category term="KDiff3" /><category term="file manager" /><category term="SmartGWT" /><category term="django-annoying" /><category term="googlefight.com" /><category term="django-compressor" /><category term="unlock" /><category term="django-publicauth" /><category term="OpenDNS" /><category term="философия" /><category term="софт" /><category term="мир объектов" /><category term="django-piston" /><category term="непонятнки" /><category term="смысл жизни" /><category term="архивирование" /><category term="программирование" /><category term="gitosis" /><category term="modelform" /><category term="ссылки" /><title>Vermus</title><subtitle type="html">Программирование, администрирование, музыка, мысли.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://vermus.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>89</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/blogspot/vermus" /><feedburner:info uri="blogspot/vermus" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CU8ERHw4fCp7ImA9WhRXEk8.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-6707928342314413793</id><published>2011-12-18T19:27:00.000+04:00</published><updated>2011-12-18T19:56:45.234+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-18T19:56:45.234+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="LockHunter" /><category scheme="http://www.blogger.com/atom/ns#" term="who lock me" /><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><category scheme="http://www.blogger.com/atom/ns#" term="Process Explorer" /><category scheme="http://www.blogger.com/atom/ns#" term="unlock" /><category scheme="http://www.blogger.com/atom/ns#" term="файловая система" /><title>Аналог who lock me в windows 7</title><content type="html">Была такая утилита, &lt;a href="http://www.dr-hoiby.com/WhoLockMe/"&gt;who lock me&lt;/a&gt;, которая замечательно работала в windows xp. Утилита позволяет узнать какой процесс блокирует файл или папку в windows.
К сожалению, в windows seven она уже не работает.&lt;br /&gt;
&lt;br /&gt;
Поэтому есть альтернатива:&amp;nbsp;&lt;a href="http://www.emptyloop.com/unlocker/"&gt;Unlocker&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Также на сайте указана табличка с альтернативами, может вам хватит &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx"&gt;Process Explorer&lt;/a&gt; от Sysinternals или &lt;a href="http://lockhunter.com/"&gt;LockHunter&lt;/a&gt; от Crystal Rich.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-6707928342314413793?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/9tknU9Dt9a4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/6707928342314413793/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2011/12/who-lock-me-windows-7.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6707928342314413793?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6707928342314413793?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/9tknU9Dt9a4/who-lock-me-windows-7.html" title="Аналог who lock me в windows 7" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://vermus.blogspot.com/2011/12/who-lock-me-windows-7.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkENQno-fip7ImA9Wx5aEE4.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-3813547086633209457</id><published>2010-11-06T11:00:00.003+03:00</published><updated>2010-11-06T11:04:53.456+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-06T11:04:53.456+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gwt" /><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse" /><category scheme="http://www.blogger.com/atom/ns#" term="GWT Designer" /><category scheme="http://www.blogger.com/atom/ns#" term="django-piston" /><category scheme="http://www.blogger.com/atom/ns#" term="Restlet Framework" /><title>musicmans.ru | Как сделать сайт на Django | GWT</title><content type="html">Посмотрел я на дерево &lt;a rel="nofollow" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TJDBKFFvHpI/AAAAAAAAAIg/g2wCE3DY8BU/s400/musicmans.ru+gernres+directions+styles.png"&gt;жанров&lt;/a&gt; и оно мне не понравилось. Страшное, неудобное. И решил сразу заняться клиентской стороной. Тем более у нас есть отличнейший &lt;a rel="nofollow" href="http://googlewebtoolkit.blogspot.com/2010/09/google-relaunches-instantiations.html"&gt;повод&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;Итак, настроим gwt. Скачиваем &lt;a rel="nofollow" href="http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/heliosr"&gt;eclipse 3.6&lt;/a&gt; для java. &lt;br /&gt;Далле переходим на страницы с &lt;a rel="nofollow" href="http://code.google.com/intl/ru-RU/webtoolkit/download.html"&gt;загрузками GWT&lt;/a&gt;. Ставим &lt;a rel="nofollow" href="http://code.google.com/intl/ru-RU/eclipse/docs/download.html"&gt;gwt плагин&lt;/a&gt; для eclipse.&lt;br /&gt;&lt;br /&gt;Создаем проект File &gt; New &gt; Web Application Project.&lt;br /&gt;&lt;br /&gt;Название: genre&lt;br /&gt;package: ru.musicmans&lt;br /&gt;&lt;br /&gt;Запуск - Debug As &gt; Web Application.&lt;br /&gt;&lt;br /&gt;Переходим по адресу, устанавливаем плагин. Все работает.&lt;br /&gt;&lt;br /&gt;Устанавливаем &lt;a rel="nofollow" href="http://code.google.com/intl/ru-RU/webtoolkit/tools/download-gwtdesigner.html"&gt;GWT Designer&lt;/a&gt;. Читаем &lt;a rel="nofollow" href="http://code.google.com/intl/ru-RU/webtoolkit/tools/gwtdesigner/quick_start.html"&gt;quick start&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Далее можно конвертировать, созданный проект из gwt plugin (gwt plugin мы поставили потому, что в нем все равно находиться сам gwt) в gwt java project, который идет с дизайнером (правой кнопкой на проекте - convert to) или создать новый:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TJh0X5O5Q_I/AAAAAAAAAIw/Y53RUXlV5rM/s1600/gwt+java+project.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 380px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TJh0X5O5Q_I/AAAAAAAAAIw/Y53RUXlV5rM/s400/gwt+java+project.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5519289297191650290" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Сразу создадим модуль ru.musicmans.genre.GenreTree:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TLMPbLSLisI/AAAAAAAAAKI/5ORwW_hlD0o/s1600/new+module.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 266px; height: 47px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TLMPbLSLisI/AAAAAAAAAKI/5ORwW_hlD0o/s400/new+module.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5526778127273659074" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Итак, проект создан. Открываем genre.java и кликаем на вкладку Design.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TLMQEDGgJ8I/AAAAAAAAAKQ/-LsmcE5XeM4/s1600/genre+design.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 287px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TLMQEDGgJ8I/AAAAAAAAAKQ/-LsmcE5XeM4/s400/genre+design.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5526778829451831234" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Что нам надо для организации передачи данных? Мы не можем использовать rpc call gwt, так как у нас на стороне сервера django. Что делать в данном случае? Я рассматривал &lt;a href="http://vermus.blogspot.com/2009/12/gwt-django.html"&gt;данный вопрос&lt;/a&gt; около года назад. Итог такой: возможен вывод данных в темплейте и преобразование их в javascript object, но это не очень оптимальный путь, тем более, что приложению обычно нужны данные в процессе работы (в том числе, обновленные). Поэтому лучшим решением мне предоставляется &lt;a href="http://vermus.blogspot.com/2010/02/smartgwt-django-rest.html"&gt;REST&lt;/a&gt; (с помощью этого подхода не надо проходить новый путь создания интерфейсов сервисов). Я решил не использовать SmartGWT, слишком он навороченный. В чистом GWT нет поддержки REST, поэтому воспользуемся &lt;a href="http://www.restlet.org/downloads/testing"&gt;Restlet Framework&lt;/a&gt;, ну а со стороны django - &lt;a rel="nofollow" href="http://bitbucket.org/jespern/django-piston/wiki/Home"&gt;django-piston&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Скачаем &lt;a rel="nofollow" href="http://www.scribd.com/doc/23792711/Presentation-Restlet"&gt;Restlet Framework&lt;/a&gt;, Edition for Google Web Toolkit. Установим (укажем java build path в свойствах проекта).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Django-Piston&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Теперь перейдем к серверной стороне. django-piston я уже как-то &lt;a href="http://vermus.blogspot.com/2010/02/smartgwt-django-rest.html"&gt;упоминал&lt;/a&gt;. Так вот, устанавливаем:&lt;br /&gt;&lt;br /&gt;&gt;c:\Python26\Scripts\pip.exe install hg+http://bitbucket.org/jespern/django-piston@c4b2d21db51a#egg=piston&lt;br /&gt;&lt;br /&gt;Читаем &lt;a href="http://bitbucket.org/jespern/django-piston/wiki/Documentation"&gt;документацию&lt;/a&gt;. Создадим приложение api, пропишем (r'^api/', include('api.urls')), в главном urls.py. Создадим в приложении файлы urls.py и handlers.py. Остальные файлы, кроме __init__.py можно удалить. &lt;br /&gt;handlers.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from django.core.urlresolvers import reverse&lt;br /&gt;&lt;br /&gt;from piston.handler import BaseHandler#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;from genre.models import GenreDirStyle#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;class GenreHandler(BaseHandler):&lt;br /&gt;    allowed_methods = ('GET', )&lt;br /&gt;    fields = ('name', 'type', 'url' )&lt;br /&gt;    model = GenreDirStyle&lt;br /&gt;&lt;br /&gt;    @classmethod&lt;br /&gt;    def url(self, genre):&lt;br /&gt;        return reverse('genre_genre', args=[genre.id])&lt;br /&gt;&lt;br /&gt;    def read(self, request, genre_id):&lt;br /&gt;        genre = GenreDirStyle.objects.get(id=int(genre_id))&lt;br /&gt;        return genre&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;urls.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from django.conf.urls.defaults import *&lt;br /&gt;&lt;br /&gt;from piston.resource import Resource#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;from api.handlers import GenreHandler#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;genre_resource = Resource(handler=GenreHandler)&lt;br /&gt;&lt;br /&gt;urlpatterns = patterns('',&lt;br /&gt;                        url(r'^genre/(?P&lt;genre_id&gt;[^/]+)/$', genre_resource, name='api_genre_id'), &lt;br /&gt;                        )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Переходим по адресу, например http://localhost:8000/api/genre/3/ :&lt;br /&gt;&lt;pre name="code" class="json"&gt;&lt;br /&gt;{&lt;br /&gt;    "url": "/genre/id/4/", &lt;br /&gt;    "type": 3, &lt;br /&gt;    "name": "Prog-Rock"&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Чтобы открывать application/json в firefox, установите &lt;a rel="nofollow" href="https://addons.mozilla.org/en-US/firefox/addon/8207/"&gt;дополнение&lt;/a&gt;, а еще лучше используйте  &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/9780"&gt;RESTClient для Firefox&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Вернемся к клиентской части.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Добавим widget дерево в gwt приложение tree:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TJr5ha8CEXI/AAAAAAAAAJg/PWuBKy3usog/s1600/gwt+designer+add+tree.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 251px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TJr5ha8CEXI/AAAAAAAAAJg/PWuBKy3usog/s400/gwt+designer+add+tree.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5519998645858079090" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TJskXwPItQI/AAAAAAAAAJw/Olqi5QkNwwE/s1600/gwt+designer+add+tree+item.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 349px; height: 400px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TJskXwPItQI/AAAAAAAAAJw/Olqi5QkNwwE/s400/gwt+designer+add+tree+item.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5520045758776653058" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Запускаем отладку, кстати, сразу рекомендую изменить параметр logLevel в конфигурации отладки.&lt;br /&gt;&lt;br /&gt;С помощью firebug видим, что ответа на запрос по адресу http://localhost:8000/api/genre/3 не увенчались успехом, поэтому вспоминаем про &lt;a href="http://vermus.blogspot.com/2009/12/gwt-django.html"&gt;проблему SOP&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;В процессе поиска решений наткнулся на &lt;a rel="nofollow" href="http://gist.github.com/426829"&gt;django-crossdomainxhr-middleware.py&lt;/a&gt;, позволяющее использовать &lt;a rel="nofollow" href="https://developer.mozilla.org/En/HTTP_access_control"&gt;кроссдоменные запросы&lt;/a&gt; (требуется firefox &gt; 3.5).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TKGOygARoAI/AAAAAAAAAJ4/8Mkxg3Up3jU/s1600/SOP+headers.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 182px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TKGOygARoAI/AAAAAAAAAJ4/8Mkxg3Up3jU/s400/SOP+headers.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5521851616368631810" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Но мы пока его использовать не будем.&lt;br /&gt;&lt;br /&gt;Итак, проблему SOP при разработке решим следующим образом. Отключим Jetty сервер, запускаемый в отладке, а также укажем порт 8000.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TKQjwUZw3FI/AAAAAAAAAKA/PARp1ez8YyQ/s1600/gwt+noserver.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 363px; height: 117px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TKQjwUZw3FI/AAAAAAAAAKA/PARp1ez8YyQ/s400/gwt+noserver.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5522578356080532562" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Сделаем символическую ссылку с директории war проекта на www\media\static\gwt\genre, под windows, например, так:&lt;br /&gt;&lt;br /&gt;www\media\static\gwt&gt;mklink /d genre d:\path\to\gwt\war\&lt;br /&gt;&lt;br /&gt;Далее, можно отлаживать приложение по адресу примерно такому (предварительно запустив веб-сервер отладки django) http://localhost:8000/media/static/gwt/genre/GenreTree.html?gwt.codesvr=127.0.0.1:9997 адресу. Параметр gwt.codesvr обязателен при отладке gwt приложения.&lt;br /&gt;&lt;br /&gt;А еще лучше создать темплейт django, подключив к нему приложение gwt примерно следующим образом. Создаем div с id='genreTreeEntryPointId' в темплейте, а в gwt root panel определяем следующим образом: &lt;br /&gt;RootPanel rootPanel = RootPanel.get("genreTreeEntryPointId");&lt;br /&gt;&lt;br /&gt;Теперь мы можем отлаживать gwt приложение прямо в "окружении" django-проекта, например, по адресу такому http://localhost:8000/genre/tree/?gwt.codesvr=127.0.0.1:9997&amp;genre_id=2 .&lt;br /&gt;&lt;br /&gt;Серверный и клиентский код выкладывать слишком много, остановимся на нюансах:&lt;br /&gt;&lt;br /&gt;- Выбор конкретного жанра в дереве (например при нажатии на меню жанров сверху). В этом случае загрузка списка для создания дерева вложенного множества осуществляется следующим образом (нам надо загрузить ветку, каждый элемент которой должен загрузить соседей):&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;        treeqs = GenreDirStyle.objects.raw("""SELECT t2.*&lt;br /&gt;                    FROM genre_genredirstyle AS t1&lt;br /&gt;                    LEFT JOIN genre_genredirstyle AS t2&lt;br /&gt;                    ON t2.lft BETWEEN t1.lft AND t1.rgt&lt;br /&gt;                    WHERE t1.lft &lt; %s AND t1.rgt &gt; %s AND t1.tree_id = 1 AND t2.depth-1 = t1.depth AND t2.tree_id = %s&lt;br /&gt;                    ORDER BY t2.lft;""", (genre.lft, genre.rgt, genre.tree_id))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Построение дерева решил возложить на плечи клиентов.&lt;br /&gt;- При загрузке приложения выводим div с изображением, который заменяется самим приложением, после его загрузки, а также выполнения всех ajax запросов.&lt;br /&gt;- Настройки приложения выводим как JSON объект в javascript, получая значения которого в gwt приложении:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;private final Dictionary paramsDict = Dictionary.getDictionary("gwtGenreParameters");&lt;br /&gt;String paramsDict.get("API_GENRES_MAIN_URL");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;- Автоматический разбор JSON ответа. Используем данное &lt;a href="http://code.google.com/p/piriti/wiki/RestletExtension"&gt;приложение&lt;/a&gt;.&lt;br /&gt;Оно зависит от: google-gin и totoe (погуглите и подключайте в проект).&lt;br /&gt;- Для обозначения состояния элемента дерева используем своей объект класса (сокращенный вид):&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt; private class GenreTreeItemData&lt;br /&gt; {&lt;br /&gt;  private int id;&lt;br /&gt;  private Boolean alreadyLoaded  = false;&lt;br /&gt;  private String description;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;используя функцию setUserObject(Object).&lt;br /&gt;- При создании проекта, в настройках gwt приложения наследуется стиль по умолчанию gwt standart. Так вот, проблема в том, что в нем есть правила css, переопределяющие наши (в том числе body). Решить это можно двумя способами, вот &lt;a rel="nofollow" href="http://groups.google.fr/group/Google-Web-Toolkit/msg/3e630a3059303b19"&gt;первый&lt;/a&gt;, а можно просто удалить нежелательные строки из standard.css файла в директории gwt\standard\ (их там немного и они вначале).&lt;br /&gt;- Для генерации документации по API используем вид from piston.doc import documentation_view.&lt;br /&gt;&lt;br /&gt;После работы все как обычно и результат:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TNUBALNwIgI/AAAAAAAAAKY/bKWHzOGij00/s1600/genres.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 366px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TNUBALNwIgI/AAAAAAAAAKY/bKWHzOGij00/s400/genres.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5536332419443991042" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ps. На стороне сервера попробовал &lt;a href="http://oradir.blogspot.com/2010/08/aptana-3-beta.html"&gt;использовать Aptana 3.0&lt;/a&gt;, там действительно отменная поддержка Django темплейтов в PyDev (но наткнулся на баг, ctrl+space вешает IDE, может только у меня так?).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-3813547086633209457?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/23V9ZnCGD6c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/3813547086633209457/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/11/musicmansru-django-gwt.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3813547086633209457?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3813547086633209457?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/23V9ZnCGD6c/musicmansru-django-gwt.html" title="musicmans.ru | Как сделать сайт на Django | GWT" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_kR0dajk9XAw/TJh0X5O5Q_I/AAAAAAAAAIw/Y53RUXlV5rM/s72-c/gwt+java+project.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/11/musicmansru-django-gwt.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcFQnw8fyp7ImA9Wx5XFkw.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-362813386912085866</id><published>2010-09-15T17:00:00.005+04:00</published><updated>2010-09-16T10:20:13.277+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-16T10:20:13.277+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="музыкальные жанры и стили" /><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Жанры, направления, стили</title><content type="html">Прошу прощения за долгое отсутствие. &lt;br /&gt;Пишем следующее приложение. Я решил, что жанры, направления и стили; инструменты; композиции; исполнители будут у нас отдельными приложениями, потому что планирую, что они обрастут серьезной функциональностью.&lt;br /&gt;&lt;br /&gt;Начнем с жанров, направлений и стилей. Создадим приложение genre, сразу создаем модель жанра.&lt;br /&gt;Чтобы создать модель, нам надо определиться, что такое жанр собственно? К сожалению, в русском интернете, большая путница и мешанина из жанров, стилей и направлений. Нет ни &lt;a rel="nofollow" href="http://books.google.ru/books?id=Y1OoJE0cu-EC&amp;printsec=frontcover"&gt;исследований&lt;/a&gt;, ни достаточно устоявшихся критериев. &lt;a rel="nofollow" href="http://www.csl.sony.fr/downloads/papers/2000/pachet-riao2000.pdf"&gt;Прочитав эту заметку&lt;/a&gt; начал задумываться структуре систематизации жанров и стилей. И главное, неплохо было бы найти уже существующую отлаженную структуру (&lt;a rel="nofollow" href="http://moustaki.org/resources/styles.owl"&gt;Amazon&lt;/a&gt;). После долгих поисков (amg, amazon, mp3.com, discogs) остановился на варианте от &lt;a rel="nofollow" href="http://allmusic.com/cg/amg.dll?p=amg&amp;sql=73:"&gt;amg&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Итак, напишем модель жанра, направления и стиля и заполним их структурой. &lt;br /&gt;&lt;br /&gt;Так как я один, и у меня нет редакторов, то пришлось воспользоваться результатом чужих трудов и спарсить структуру жанров, направлений и стилей с amg. Надеюсь они на меня за это не в обиде.&lt;br /&gt;&lt;br /&gt;Код приводить не буду, расскажу что использовал &lt;a rel="nofollow" href="http://codespeak.net/lxml"&gt;lxml&lt;/a&gt;, а также окружение проекта для записи жанров/стилей.&lt;br /&gt;&lt;br /&gt;После того как данные в базе, сделаем &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/howto/initial-data/"&gt;initial data&lt;/a&gt; для приложения:&lt;br /&gt;&lt;br /&gt;&gt;python manage.py dumpdata genre &gt; apps\genre\fixtures\initial_data.json&lt;br /&gt;&lt;br /&gt;Теперь попробуем вывести дерево жанров/стилей.&lt;br /&gt;&lt;br /&gt;На данном этапе понимаем, что вывод "детей" стилей сулит нам лавинообразные запросы к базе данных (&lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related"&gt;select_related&lt;/a&gt; нам тоже не поможет, он не срабатывает для моделей с полями отношения ForeignKey с null=True ( father = models.ForeignKey('self', verbose_name=_(u'Родитель'), related_name='child_dirs_styles', null=True) ) ), поэтому воспользуемся приложением для &lt;a rel="nofollow" href="http://www.slideshare.net/quipo/trees-in-the-database-advanced-data-structures?type=presentation"&gt;хранения деревьев&lt;/a&gt; в базе данных &lt;a rel="nofollow" href="http://github.com/tabo/django-treebeard/"&gt;django-treebeard&lt;/a&gt; (&lt;a rel="nofollow" href="https://tabo.pe/projects/django-treebeard/docs/tip/"&gt;документация&lt;/a&gt;). &lt;br /&gt;&lt;br /&gt;C:\&gt;c:\Python26\Scripts\pip.exe install git+git://github.com/tabo/django-treebeard@8eb52a4f4274615e86a7572a8bab39b79d718b88&lt;br /&gt;&lt;br /&gt;Добавляем 'treebeard' в INSTALLED_APPS. Если вы используете админку, то настройка немного &lt;a rel="nofollow" href="https://tabo.pe/projects/django-treebeard/docs/tip/intro.html#configuration"&gt;посложней&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" href="https://tabo.pe/projects/django-treebeard/docs/tip/intro.html#basic-usage"&gt;Воспользуемся&lt;/a&gt; моделью хранения деревьев &lt;a rel="nofollow" href="http://www.getinfo.ru/article610.html"&gt;Nested Sets&lt;/a&gt;. Не стоит пугаться последней ссылки, тем то и хорошо приложение treebeard, что за нас уже &lt;a rel="nofollow" href="https://tabo.pe/projects/django-treebeard/docs/tip/ns_tree.html#treebeard.ns_tree.NS_Node"&gt;решен вопрос хранения деревьев&lt;/a&gt; в SQL базе. Нам лишь стоит воспользоваться набором функций.&lt;br /&gt;&lt;br /&gt;Смотрим нашу модель:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- coding:utf-8 -*-&lt;br /&gt;from django.db import models&lt;br /&gt;from django.utils.translation import ugettext_lazy as _&lt;br /&gt;from treebeard.ns_tree import NS_Node #@UnresolvedImport &lt;br /&gt;&lt;br /&gt;GENRE_DIR_STYLE = (&lt;br /&gt;    (0, _('Музыка')),&lt;br /&gt;    (1, _('Жанр')),&lt;br /&gt;    (2, _('Направление')),&lt;br /&gt;    (3, _('Стиль')),&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;class GenreDirStyle(NS_Node):&lt;br /&gt;    name = models.CharField(max_length=1000, verbose_name=_(u'Title'))&lt;br /&gt;    name_ru = models.CharField(max_length=1000, verbose_name=_(u'Название'), blank=True, null=True)&lt;br /&gt;    type = models.IntegerField(choices=GENRE_DIR_STYLE)&lt;br /&gt;    description = models.CharField(max_length=10000, verbose_name=_(u'Описание'), blank=True)&lt;br /&gt;&lt;br /&gt;    class Meta:&lt;br /&gt;        ordering = ["name"]&lt;br /&gt;        verbose_name = _(u'Жанр, направление, стиль')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Пробуем, работает ли миграции South с treebeard:&lt;br /&gt;&lt;br /&gt;./manage.py schemamigration genre --auto&lt;br /&gt;&lt;br /&gt;получаем сообщение следующего &lt;a rel="nofollow" href="http://south.aeracode.org/docs/tutorial/part2.html"&gt;вида&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;? The field 'GenreDirStyle.lft' does not have a default specified, yet is NOT NULL.&lt;br /&gt; ? Since you are adding or removing this field, you MUST specify a default&lt;br /&gt; ? value to use for existing rows. Would you like to:&lt;br /&gt; ?  1. Quit now, and add a default to the field in models.py&lt;br /&gt; ?  2. Specify a one-off value to use for existing columns now&lt;br /&gt; ? Please select a choice:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://bitbucket.org/tabo/django-treebeard/issue/8/support-for-migrating-to-django-treebeard-null-true-in-the-model"&gt;South нас просит указать обязательное значение&lt;/a&gt; по умолчанию. Нажимаем 2, и значение 0.&lt;br /&gt;&lt;br /&gt;Как создать &lt;a rel="nofollow" href="https://tabo.pe/projects/django-treebeard/docs/tip/api.html#treebeard.models.Node"&gt;дерево&lt;/a&gt;? Просто:&lt;br /&gt;1. Создаем корень дерева.&lt;br /&gt;pop_music = GenreDirStyle.add_root(name = "Популярная музыка", type = 0)#оно сразу сохраняется save()&lt;br /&gt;2. Создаем жанр:&lt;br /&gt;pop_music.add_child(name = "Rock", type = 1)&lt;br /&gt;&lt;br /&gt;И здесь сталкиваемся с &lt;a href="http://bitbucket.org/tabo/django-treebeard/issue/29/ns_node-do-not-cache-lft-rgt-on-insert"&gt;проблемой&lt;/a&gt;, в случае парсинга (см. выше) html и создания базы "на лету". Поэтому читаем &lt;a rel="nofollow" href="https://tabo.pe/projects/django-treebeard/docs/1.61/intro.html#basic-usage"&gt;basic-usage&lt;/a&gt; внимательней и переписываем примерно так:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;&gt;&gt;&gt; get = lambda node_id: Category.objects.get(pk=node_id)&lt;br /&gt;&gt;&gt;&gt; root = Category.add_root(name='Computer Hardware')&lt;br /&gt;&gt;&gt;&gt; node = get(root.id).add_child(name='Memory')&lt;br /&gt;&gt;&gt;&gt; get(node.id).add_sibling(name='Hard Drives')&lt;br /&gt;&lt;Category: Category: Hard Drives&gt;&lt;br /&gt;&gt;&gt;&gt; get(node.id).add_sibling(name='SSD')&lt;br /&gt;&lt;Category: Category: SSD&gt;&lt;br /&gt;&gt;&gt;&gt; get(node.id).add_child(name='Desktop Memory')&lt;br /&gt;&lt;Category: Category: Desktop Memory&gt;&lt;br /&gt;&gt;&gt;&gt; get(node.id).add_child(name='Laptop Memory')&lt;br /&gt;&lt;Category: Category: Laptop Memory&gt;&lt;br /&gt;&gt;&gt;&gt; get(node.id).add_child(name='Server Memory')&lt;br /&gt;&lt;Category: Category: Server Memory&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Не забудем обновить json данные, после изменения и миграций моделей.&lt;br /&gt;Для вывода дерева жанров используем функцию &lt;a rel="nofollow" href="https://tabo.pe/projects/django-treebeard/docs/tip/api.html#treebeard.models.Node.get_tree"&gt;get_tree&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;from annoying.decorators import render_to#@UnresolvedImport&lt;br /&gt;from django.shortcuts import get_object_or_404&lt;br /&gt;&lt;br /&gt;from models import GenreDirStyle&lt;br /&gt;&lt;br /&gt;@render_to('genres/genre_tree.html')&lt;br /&gt;def genre_tree(request):&lt;br /&gt;&lt;br /&gt;    pop_genre = GenreDirStyle.objects.get(name="Популярная музыка", type=0)&lt;br /&gt;    classic_genre = GenreDirStyle.objects.get(name="Классическая музыка", type=0)&lt;br /&gt;&lt;br /&gt;    pop_tree = GenreDirStyle.get_tree(pop_genre)&lt;br /&gt;    classic_tree = GenreDirStyle.get_tree(classic_genre)&lt;br /&gt;   &lt;br /&gt;    return {&lt;br /&gt;          'pop_tree': pop_tree,&lt;br /&gt;          'classic_tree': classic_tree&lt;br /&gt;          } &lt;br /&gt;&lt;br /&gt;@render_to('genres/genre_genre.html')&lt;br /&gt;def genre_genre(request, genre_id):&lt;br /&gt;    &lt;br /&gt;    genre = get_object_or_404(GenreDirStyle, id = int(genre_id))&lt;br /&gt;&lt;br /&gt;    return {&lt;br /&gt;            'genre': genre&lt;br /&gt;          } &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;На данном этапе django-toolbar показывал замечательные 5 запросов за 13 мс. А вот общая генерация страницы занимала 6573.00 ms. Это очень долго, хотя при выключенном debug режиме ощутимо быстрее. Все упирается в рендеринг. Проэтому применим кеш в темплейте (на шесть часов, например):&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;{% load cache %}&lt;br /&gt;{% cache 21600 pop_tree_chache %}&lt;br /&gt;{% for node in pop_tree %}&lt;br /&gt;  {% include "genres/genre_node.html" %}&lt;br /&gt;{% endfor %}&lt;br /&gt;{% endcache %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;А также включим на время (позже настроим memcached на сервере) &lt;a href="http://djbook.ru/ch13.html#djangobook.chap13.setup.simple-caching"&gt;кеширование в память&lt;/a&gt;, в settings/common.py:&lt;br /&gt;&lt;br /&gt;CACHE_BACKEND = 'locmem:///'&lt;br /&gt;&lt;br /&gt;Темплейты интуитивно понятны, покажу лишь темплейт жанра, включаемый в цикл вывода дерева. &lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;{% load dj_tags %}&lt;br /&gt;&lt;div style="padding-left:{{ node.get_depth|multiply:20|subtract:20 }}px;"&gt;&lt;br /&gt;&lt;h{{ node.type|add:"1" }}&gt;&lt;br /&gt;&lt;a href="{% url genre_genre node.pk %}"&gt;{% if node.get_depth &gt; 1 %}{{ node.get_type_display }} {% endif %}{{ node.name }}&lt;/a&gt;&lt;br /&gt;&lt;/h{{ node.type|add:"1" }}&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(Ужасно, blogger все сломал, смотрите &lt;a rel="nofollow" href="http://pastebin.com/SWEHzsMX"&gt;здесь&lt;/a&gt;)&lt;br /&gt;Обратите внимание на фильтр multiply и substract. Это не стандартные фильтры django, а &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/howto/custom-template-tags/"&gt;написаные в нашем приложении&lt;/a&gt; dj_tags.&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;from django import template&lt;br /&gt;register=template.Library()&lt;br /&gt;&lt;br /&gt;@register.filter(name='multiply')&lt;br /&gt;def multiply(value, arg):&lt;br /&gt;    return int(value) * int(arg)&lt;br /&gt;&lt;br /&gt;@register.filter(name='subtract')&lt;br /&gt;def subtract(value, arg):&lt;br /&gt;    return int(value) - int(arg)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Итак, мы познакомились с хранением деревьев в базе данных с django, их выводом, затронули кеширование, написали пару темплейт тегов.&lt;br /&gt;&lt;br /&gt;Ну, окончание, как обычно, тесты, мерж, развертывание.&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TJDBKqlx3oI/AAAAAAAAAIo/npXRiq4EZV4/s1600/musicmans.ru+prog-rock+style.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 283px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TJDBKqlx3oI/AAAAAAAAAIo/npXRiq4EZV4/s400/musicmans.ru+prog-rock+style.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5517121932504456834" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TJDBKFFvHpI/AAAAAAAAAIg/g2wCE3DY8BU/s1600/musicmans.ru+gernres+directions+styles.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 286px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TJDBKFFvHpI/AAAAAAAAAIg/g2wCE3DY8BU/s400/musicmans.ru+gernres+directions+styles.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5517121922437947026" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ps. Не забудем &lt;a rel="nofollow" href="http://www.djangoproject.com/weblog/2010/sep/08/security-release/"&gt;обновить&lt;/a&gt; django (&gt;c:\Python26\Scripts\pip.exe install --upgrade Django и прописать в requirements.txt)!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-362813386912085866?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/W4fif_cgNNk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/362813386912085866/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/09/musicmansru-django.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/362813386912085866?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/362813386912085866?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/W4fif_cgNNk/musicmansru-django.html" title="musicmans.ru | Как сделать сайт на Django | Жанры, направления, стили" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_kR0dajk9XAw/TJDBKqlx3oI/AAAAAAAAAIo/npXRiq4EZV4/s72-c/musicmans.ru+prog-rock+style.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/09/musicmansru-django.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkUGQ389eCp7ImA9Wx5SE04.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-2745221456190559682</id><published>2010-08-07T10:24:00.004+04:00</published><updated>2010-08-09T09:03:42.160+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-09T09:03:42.160+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="django-notification" /><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="django-messages" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Пользователи. Личные сообщения. Уведомления</title><content type="html">Пока Вы &lt;a href="http://vermus.blogspot.com/2010/08/musicmansru-django.html"&gt;разбираетесь с тестированием&lt;/a&gt; в django, попутно приступим к личным сообщениям, без них трудно представить современный веб-сайт. &lt;br /&gt;Перед тем как приступить, установим &lt;a rel="nofollow" href="http://github.com/robhudson/django-debug-toolbar"&gt;еще такую вещь&lt;/a&gt; на машину разработчика. &lt;br /&gt;&lt;br /&gt;#pip install git+git://github.com/robhudson/django-debug-toolbar&lt;br /&gt;Смело ставим последнюю версию, ибо если что-то сломается, то на машине разработчика это не критично.&lt;br /&gt;development.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;INSTALLED_APPS += (&lt;br /&gt;                   'django.contrib.admin',#tests&lt;br /&gt;                   'debug_toolbar',&lt;br /&gt;                   )&lt;br /&gt;&lt;br /&gt;MIDDLEWARE_CLASSES += (    &lt;br /&gt;                       'debug_toolbar.middleware.DebugToolbarMiddleware',&lt;br /&gt;                       )&lt;br /&gt;&lt;br /&gt;INTERNAL_IPS = ('127.0.0.1',)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Обновляем localhost:&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TFkEM59WhuI/AAAAAAAAAHg/lbdXQLTmcoQ/s1600/django+toolbar.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 295px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TFkEM59WhuI/AAAAAAAAAHg/lbdXQLTmcoQ/s400/django+toolbar.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501433039572862690" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Поинтересуйтесь содержимым пунктов меню, там много интересного.&lt;br /&gt;&lt;br /&gt;Итак, личные сообщения. Опять &lt;a rel="nofollow" href="http://code.google.com/p/django-messages/"&gt;все написано&lt;/a&gt; за нас. Устанавливаем из транка (кстати, вот консольный &lt;a rel="nofollow" href="http://www.sliksvn.com/en/download/"&gt;svn клиент&lt;/a&gt; для windows):&lt;br /&gt;&lt;br /&gt;&gt;pip.exe install svn+http://django-messages.googlecode.com/svn/trunk/@141#egg=django_messages&lt;br /&gt;&lt;br /&gt;141-я ревизия транка на данный момент, как написано на странице проекта это будущая версия 0.5, совместимая с Django 1.2 (как раз то, что нам нужно). Не забываем requirements.txt (кстати, я подумал, что неплохо было бы сначала добавлять строчку в requirements.txt, а ставить основываясь на файле. Надо только использовать одну директорию с кешом pip, чтобы он постоянно не скачивал дистрибутивы. Ставить установленные приложения по новой он не будет, но мы будем уверены, что не забудем прописать все приложения в requirements.txt и не испытаем проблем на сервере с сайтом).&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://code.google.com/p/django-messages/source/browse/trunk/docs/usage.txt"&gt;Документация&lt;/a&gt; по настройке. Тут кстати возникает путаница, &lt;a rel="nofollow" href="http://code.google.com/p/django-messages/source/browse/trunk/setup.py"&gt;здесь&lt;/a&gt; приложение указано как django_messages, а в документации используется messages, так что используем django_messages.&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://code.google.com/p/django-messages/source/browse/trunk/docs/customizing.txt"&gt;Изменим темплейты&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Скопируем из приложения и изменим под свои нужды.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TFk1a3gNbgI/AAAAAAAAAHw/IJT-PHhl9ho/s1600/django+messages+templates.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 275px; height: 224px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TFk1a3gNbgI/AAAAAAAAAHw/IJT-PHhl9ho/s400/django+messages+templates.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501487155501690370" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;В документации к приложению читаем, что оно использует &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django_31.html"&gt;django-mailer&lt;/a&gt; и &lt;a rel="nofollow" href="http://github.com/jtauber/django-notification/"&gt;django-notification&lt;/a&gt;, если они установлены. Первое у нас есть, ставим второе (хорошее приложение, пригодится в будущем).&lt;br /&gt;&gt;pip.exe install git+git://github.com/jtauber/django-notification@3f023adf0ce2eafcee744904e2c358792f253721@egg=notification&lt;br /&gt;&lt;br /&gt;Пропишем 'django_messages', 'notification' в приложения, настроим url.py. Синхронизируем базу.&lt;br /&gt;&lt;br /&gt;Просмотрим таблицу notification_noticetype, в ней должны находиться оповещения о работе с личными сообщениями. Приложение notification рассмотрим ниже.&lt;br /&gt;&lt;br /&gt;Как только мы видим список чего бы то ни было (список входящих сообщений), сразу рождаются мысли о пагинации. И как не удивительно :-) , django &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/pagination/"&gt;поддерживает ее из&lt;/a&gt; коробки, а чтобы было совсем просто установим приложение &lt;a rel="nofollow" href="http://github.com/ericflo/django-pagination"&gt;django-pagination&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&gt;pip.exe install git+git://github.com/ericflo/django-pagination@47e7ec874cd7dddda5ed13ffb6993a64dced2537&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://github.com/ericflo/django-pagination/blob/master/docs/usage.txt"&gt;Настраиваем&lt;/a&gt;. Добавляем css разметку.&lt;br /&gt;&lt;br /&gt;А также переведем пару строк, так как в приложении нет русской локализации, djutils\locale\ru_RU\LC_MESSAGES\django.po:&lt;br /&gt;msgid "previous"&lt;br /&gt;msgstr "назад"&lt;br /&gt;&lt;br /&gt;msgid "next"&lt;br /&gt;msgstr "вперед"&lt;br /&gt;&lt;br /&gt;Не забудем скомпилировать.&lt;br /&gt;&lt;br /&gt;в темплейтах с сообщениями добавляем (с месторасположением самостоятельно, 2-ка для теста):&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;{% load pagination_tags %}&lt;br /&gt;{% autopaginate message_list 2 %}&lt;br /&gt;{% paginate %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;В итоге получается:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TFpi1sHB1BI/AAAAAAAAAH4/x622Nh5_Ztc/s1600/django+pagination+messages.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 78px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TFpi1sHB1BI/AAAAAAAAAH4/x622Nh5_Ztc/s400/django+pagination+messages.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501818569299710994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Чтобы пользователи не были у нас безликими, давайте организуем им вывод профайлов.&lt;br /&gt;В user/url.conf&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;url(r'^profile/(?P&lt;userprofile_id&gt;\d+)/$', users_views.userprofile, name='users_profile'),&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(блогспот пытается закрыть тег /userprofile_id самостоятельно, конечно это в коде не требуется)&lt;br /&gt;Вид:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;@login_required&lt;br /&gt;@render_to('users/user_profile.html')&lt;br /&gt;def userprofile(request, userprofile_id):&lt;br /&gt; &lt;br /&gt;    request_user = get_object_or_404(User, id=int(userprofile_id), is_active=True)&lt;br /&gt;    &lt;br /&gt;    return {&lt;br /&gt;          'request_user' : request_user,&lt;br /&gt;          }  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Отмечу, что получать надо user, а не наш UserProfile, который еще может быть не создан.&lt;br /&gt;&lt;br /&gt;Соответственно, напишем темплейт.&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TFqqiqfQd4I/AAAAAAAAAIA/t5mKZ5nFAjY/s1600/django+inbox+messages.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 171px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TFqqiqfQd4I/AAAAAAAAAIA/t5mKZ5nFAjY/s400/django+inbox+messages.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501897407284148098" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;django_messages\templates\notification\ переведите вручную, перевод, который идет с приложением не работает (устарел наверное). Скопируем темплейты в users/templates, создадим папку locale в приложении и запустим создание файла перевода:&lt;br /&gt;&gt;python C:\Python26\Lib\site-packages\django\bin\django-admin.py makemessages  -e .html,.txt --locale=ru_RU&lt;br /&gt;в директории users.&lt;br /&gt;После редактирования скомпилируем.&lt;br /&gt;&lt;br /&gt;Темлейт для уведомлений notices.html для самостоятельного написания (можно подсмотреть в &lt;a rel="nofollow" href="http://github.com/pinax/pinax/blob/master/pinax/templates/default/notification/notices.html"&gt;pinax&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Что такое notification? Это приложение для уведомлений. Когда происходит событие в системе, мы имеем возможность создать уведомление для пользователя, с возможностью настройки дополнительных рассылок уведомлений:&lt;br /&gt;&lt;br /&gt;* при логине на сайт.&lt;br /&gt;* по почте (настраивается пользователем).&lt;br /&gt;* по rss&lt;br /&gt;&lt;br /&gt;Подключим контекстный процессор "notification.context_processors.notification".&lt;br /&gt;&lt;br /&gt;Мне не понравилась часть приложения по работе с url. Поэтому  скопировал приложение себе в проект и поправил под свои нужды, получилось примерно следующее:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TFz8UYWCBRI/AAAAAAAAAIQ/m4XgNAQUmSM/s1600/musimans+user+notification.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 212px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TFz8UYWCBRI/AAAAAAAAAIQ/m4XgNAQUmSM/s400/musimans+user+notification.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5502550271802344722" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Пишите &lt;a href="http://musicmans.ru/users/profile/1/"&gt;сообщения&lt;/a&gt;. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-2745221456190559682?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/_RMCmpiVQes" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/2745221456190559682/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/08/musicmansru-django_04.html#comment-form" title="Комментарии: 7" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/2745221456190559682?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/2745221456190559682?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/_RMCmpiVQes/musicmansru-django_04.html" title="musicmans.ru | Как сделать сайт на Django | Пользователи. Личные сообщения. Уведомления" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_kR0dajk9XAw/TFkEM59WhuI/AAAAAAAAAHg/lbdXQLTmcoQ/s72-c/django+toolbar.png" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/08/musicmansru-django_04.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08DRHc5fCp7ImA9Wx5TGEQ.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-1658261962082738604</id><published>2010-08-03T16:50:00.003+04:00</published><updated>2010-08-04T09:31:15.924+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-04T09:31:15.924+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="django-publicauth" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Пользователи. Дополнительная аутентификация</title><content type="html">В &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django_31.html"&gt;прошлый раз&lt;/a&gt; мы остановились на том, что начали создавать приложение users. Давайте создадим openid аутентификацию на сайте.&lt;br /&gt;Я обещал писать бекенды для django-registration, но оказалось &lt;a rel="nofollow" href="http://habrahabr.ru/blogs/django/91568/"&gt;существует&lt;/a&gt; замечательное &lt;a rel="nofollow" href="http://bitbucket.org/offline/django-publicauth/wiki/Home"&gt;приложение&lt;/a&gt; для всевозможных видов аутентификации с последующим созданием пользователя и авторизации.&lt;br /&gt;&lt;br /&gt;* OpenID - yandex.ru, rambler.ru, yahoo.ru, google.com&lt;br /&gt;* OAuth - twitter.com&lt;br /&gt;* OpenAPI - Вконтакте.ру&lt;br /&gt;* FacebookConnect - facebook.com&lt;br /&gt;&lt;br /&gt;Устанавливаем:&lt;br /&gt;c:\Python26\Scripts\pip.exe install hg+http://bitbucket.org/offline/django-p&lt;br /&gt;ublicauth@7371e8f71be1#egg=django-publicauth&lt;br /&gt;&lt;br /&gt;Прописываем в requirements.txt.&lt;br /&gt;Добавляем 'publicauth' в INSTALLED_APPS. Добавляем 'annoying.middlewares.RedirectMiddleware' в MIDDLEWARE_CLASSES. &lt;br /&gt;&lt;br /&gt;Добавим 'publicauth.PublicBackend' в &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/auth/#authentication-backends"&gt;AUTHENTICATION_BACKENDS&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Запускаем syncdb.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;OpenID&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Необходимо установить python-openid (2.2.5).&lt;br /&gt;&lt;br /&gt;Добавим &lt;br /&gt;(r'', include('publicauth.urls')),&lt;br /&gt;в файл /users/urls.py , тем самым переопределив &lt;a rel="nofollow" href="http://bitbucket.org/offline/django-publicauth/src/tip/publicauth/urls.py"&gt;url login и logout приложения&lt;/a&gt; нашими url'ами, и оставив остальные, необходимые для работы приложения. Ознакомимся &lt;a rel="nofollow" href="http://bitbucket.org/offline/django-publicauth/src/7371e8f71be1/publicauth/templates/publicauth/login.html"&gt;с содержимым темплейта login.html&lt;/a&gt;, и добавим следующую форму в наш login.html:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&lt;form action="{% url publicauth-begin 'openid' %}?next={{ request.GET.next }}" method="post" id="openid_login"&gt;&lt;br /&gt;    Openid URL&lt;br /&gt;    &lt;input type="text" name="openid_url" /&gt;&lt;br /&gt;    &lt;p&gt;&lt;input type="submit" value="Continue"/&gt;&lt;/p&gt;&lt;br /&gt;&lt;/form&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Создадим директорию publicauth в users/templates/ , скопируем туда &lt;a rel="nofollow" href="http://bitbucket.org/offline/django-publicauth/src/7371e8f71be1/publicauth/templates/"&gt;содержимое&lt;/a&gt; и поправим под наш сайт.&lt;br /&gt;Также в файле users/forms.py создадим форму ExtraForm (форма для заполнения дополнительных полей после сторонней аутентификации, например имя пользователя).&lt;br /&gt;Подсмотреть можно &lt;a rel="nofollow" href="http://pyplanet.org/static/example.tar.gz"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;PUBLICAUTH_EXTRA_FORM = "users.forms.ExtraForm"&lt;br /&gt;&lt;br /&gt;Пробуем логиниться.&lt;br /&gt;&lt;br /&gt;Приложение требует установленного и настроенного &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/messages/#ref-contrib-messages"&gt;messages framework&lt;/a&gt;, как указано в документации (для самостоятельного рассмотрения) для вывода сообщений. У меня уже оно почти настроено (сообщение при сохранении профиля реализованно как раз через него).&lt;br /&gt;&lt;br /&gt;Сообщения к сожалению не переведены, придется сделать это самим.&lt;br /&gt;&lt;br /&gt;Создадим файл src/djutils/locale/ru_RU/LC_MESSAGES/django.po и переведем &lt;a rel="nofollow" href="http://bitbucket.org/offline/django-publicauth/src/tip/publicauth/lang.py"&gt;файл&lt;/a&gt;.&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;msgid "To complete registration, check your email and activate your account"&lt;br /&gt;msgstr "Для завершения регистрации проверьте e-mail и активируйте учетную запись"&lt;br /&gt;&lt;br /&gt;msgid "We are sorry, but registration is disabled. Come back later"&lt;br /&gt;msgstr "Извините, но регистрация закрыта"&lt;br /&gt;&lt;br /&gt;msgid "Please fill openid url field"&lt;br /&gt;msgstr "Пожалуйста, заполните поле openid"&lt;br /&gt;&lt;br /&gt;msgid "Your authentication provider returned bad response, please try again"&lt;br /&gt;msgstr "Ваш провайдер аутентификации вернул влохой ответ, попробуйте еще раз"&lt;br /&gt;&lt;br /&gt;msgid "You have cancelled OpenID authentication"&lt;br /&gt;msgstr "Вы отменили аутентификацию по OpenID"&lt;br /&gt;&lt;br /&gt;msgid "OpenID authentication failed. Reason: %s"&lt;br /&gt;msgstr "Аутентификация OpenID провалилась. Причина: %s"&lt;br /&gt;&lt;br /&gt;msgid "You have successfully logged out"&lt;br /&gt;msgstr "Вы успешно вышли"&lt;br /&gt;&lt;br /&gt;msgid "Your existing account was merged with new authentication account"&lt;br /&gt;msgstr "Существующая учетная запись была объединена с новой учетной записью"&lt;br /&gt;&lt;br /&gt;msgid "Your account is not activated. Please activate it first."&lt;br /&gt;msgstr "Ваша учетная запись не активирована. Пожалуйста, активируйте ее сначала."&lt;br /&gt;&lt;br /&gt;msgid "You have successfully authenticated"&lt;br /&gt;msgstr "Вы успешно аутентифицированны"&lt;br /&gt;&lt;br /&gt;msgid "Invalid response received from facebook server, please start the authentication process again"&lt;br /&gt;msgstr "Неверный ответ от сервера facebook, пожалуйста запустите процесс аутентификации еще раз"&lt;br /&gt;&lt;br /&gt;msgid "Invalid response received from OpenID server, please start the authentication process again"&lt;br /&gt;msgstr "Неверный ответ от сервера OpenID, пожалуйста запустите процесс аутентификации еще раз"&lt;br /&gt;&lt;br /&gt;msgid "Invalid response received from vkontakte server, please start the authentication process again"&lt;br /&gt;msgstr "Неверный ответ от сервера vkontakte, пожалуйста запустите процесс аутентификации еще раз"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Скомпилируем (запуск в директории src\apps\djutils\)&lt;br /&gt;&gt;python C:\Python26\Lib\site-packages\django\bin\django-admin.py compilemessages.&lt;br /&gt;&lt;br /&gt;Добавим 'django.middleware.locale.LocaleMiddleware', в MIDDLEWARE_CLASSES.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ВКонтакте&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Делаем все как по ссылке на хабр. Единственно, я не понял для чего нужно VKONTAKTE_API_KEY, работает и без него, да и в бекенде vkontakte.py он не используется.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Facebook&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Делаем по мануалу, ссылка на документацию на &lt;a href="http://developers.facebook.com/docs/guides/web"&gt;facebook&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;В настройки надо добавить&lt;br /&gt;FACEBOOK_PROFILE_MAPPING={ 'name': 'username', }&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;OAuth (Twitter)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Install python-oauth:&lt;br /&gt;&gt;pip.exe install oauth&lt;br /&gt;&lt;br /&gt;Настройки:&lt;br /&gt;&lt;br /&gt;TWITTER_CONSUMER_KEY = "key"&lt;br /&gt;TWITTER_CONSUMER_SECRET = "secret"&lt;br /&gt;TWITTER_REQUEST_TOKEN_URL = "https://twitter.com/oauth/request_token"&lt;br /&gt;TWITTER_ACCESS_TOKEN_URL = "https://twitter.com/oauth/access_token"&lt;br /&gt;TWITTER_AUTHORIZE_URL = "https://twitter.com/oauth/authorize"&lt;br /&gt;TWITTER_API_URL = "http://twitter.com/users/show.json?user_id=%s"&lt;br /&gt;TWITTER_PROFILE_MAPPING = { 'screen_name': 'username', }&lt;br /&gt;&lt;br /&gt;Темплейт:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&lt;a href="{% url publicauth-begin 'twitter' %}?next={{ request.GET.next }}" id="twitter"&gt;&lt;img title="{% trans "Войти с" %} twitter.com" alt="{% trans "Войти с" %} twitter.com" src="{{STATIC_URL}}/i/sign-in-with-twitter-l.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;В итоге, в url auth_login мы должны иметь авторизацию на сайте или аутентификацию в сторонних сайтах в случае неавторизированного пользователя, а также аутентификацию в сторонних сайтах и сопоставления этих профилей с текущим профилем. И, естественно, логин любым из методов для существующего пользователя.&lt;br /&gt;&lt;br /&gt;Как обычно, запускаем тесты, мержим транк, делаем развертывание, и сравниваем &lt;a href="http://musicmans.ru/"&gt;musicmans.ru&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TFff9J4vUHI/AAAAAAAAAHY/v48BopBamOM/s1600/musicams.ru+users+openid+vkontakte+twitter.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 189px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TFff9J4vUHI/AAAAAAAAAHY/v48BopBamOM/s400/musicams.ru+users+openid+vkontakte+twitter.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501111711575265394" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ps. По поводу тестов. Да все уже написано:&lt;br /&gt;&lt;a rel="nofollow" href="http://djangotesting.com/"&gt;http://djangotesting.com/&lt;/a&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://habrahabr.ru/blogs/django/91471/"&gt;http://habrahabr.ru/blogs/django/91471/&lt;/a&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://pyobject.ru/blog/2009/09/13/django-external-test-tools/"&gt;http://pyobject.ru/blog/2009/09/13/django-external-test-tools/&lt;/a&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://night-fairy-tales.com/2009/10/django-eclipse.html"&gt;http://night-fairy-tales.com/2009/10/django-eclipse.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Разбираемся, пишем (На данный момент нами написано лишь редактирование профиля в приложении users, вот для него и можно написать тесты).&lt;br /&gt;&lt;br /&gt;pps. Насткнулся на баг в &lt;a rel="nofollow" href="http://bitbucket.org/ubernostrum/django-registration/issue/93/tests-fail-when-language-is-set-to-es"&gt;тестах&lt;/a&gt; django-registration.&lt;br /&gt;&lt;a rel="nofollow" href="http://www.thebitguru.com/blog/view/246-Using%20custom%20settings%20in%20django%20tests"&gt;Вот&lt;/a&gt; решение (developmet.py)&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;TEST = False&lt;br /&gt;manage_command = filter(lambda x: x.find('manage.py') != -1, sys.argv)&lt;br /&gt;if len(manage_command) != 0:&lt;br /&gt;    command = sys.argv.index(manage_command[0]) + 1&lt;br /&gt;    if command &lt; len(sys.argv):&lt;br /&gt;        TEST = sys.argv[command] == "test"&lt;br /&gt;&lt;br /&gt;if TEST:&lt;br /&gt;    LANGUAGE_CODE = 'en-us'&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-1658261962082738604?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/7pmKDzVB2w0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/1658261962082738604/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/08/musicmansru-django.html#comment-form" title="Комментарии: 7" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1658261962082738604?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1658261962082738604?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/7pmKDzVB2w0/musicmansru-django.html" title="musicmans.ru | Как сделать сайт на Django | Пользователи. Дополнительная аутентификация" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_kR0dajk9XAw/TFff9J4vUHI/AAAAAAAAAHY/v48BopBamOM/s72-c/musicams.ru+users+openid+vkontakte+twitter.png" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/08/musicmansru-django.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcERnsyfSp7ImA9Wx5SEkk.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-6349922584850893423</id><published>2010-07-31T13:20:00.011+04:00</published><updated>2010-08-08T09:06:47.595+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-08T09:06:47.595+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="django-registration" /><category scheme="http://www.blogger.com/atom/ns#" term="django-annoying" /><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="django-compressor" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Пользователи</title><content type="html">Наконец-то мы добрались до самого сладкого. Начнем с приложения users. Мы помним, что в django, как и в python надо писать приложения, а не проекты, чтобы соблюдать принцип &lt;a rel="nofollow" href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;. Это основное приложение, которое требуется почти в каждом проекте. Несмотря на то, что в django уже есть &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/auth/"&gt;django.contrib.auth&lt;/a&gt;, класс models.User содержит только минимальный набор полей. Расширение полей этого класса существует в &lt;a rel="nofollow" href="http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/"&gt;следующих&lt;/a&gt; &lt;a rel="nofollow" href="http://bazaar.launchpad.net/~isagalaev/scipio/trunk/annotate/head:/models.py#L33"&gt;вариантах&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://vermus.blogspot.com/2010/06/musicmansru-django-eclipse.html"&gt;Создадим и переключимся в ветку&lt;/a&gt; users.&lt;br /&gt;Перед тем, как создавать приложение, создадим темплейт для сайта - base.html и поместим его в директорию /src/templates. Для удобства редактирования темплейтов рекомендую &lt;a rel="nofollow" href="http://eclipse.kacprzak.org/updates"&gt;Django Editor - plugin for Eclipse&lt;/a&gt;.&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://eclipse.kacprzak.org/image/code.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 281px;" src="http://eclipse.kacprzak.org/image/code.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;В нем прописаны некоторые темплейты для тегов django (вызываются по ctrl+space). Для редактирования html, просто открываем файл в html редакторе aptana.&lt;br /&gt;&lt;br /&gt;Для изменения &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/contrib/sites"&gt;названия сайта&lt;/a&gt; (он по умолчанию создается &lt;a rel="nofollow" href="http://code.djangoproject.com/browser/django/trunk/django/contrib/sites/management.py#L9"&gt;при первом&lt;/a&gt; syncdb) создадим файл в корне src - install.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- mode: python; coding: utf-8; -*-&lt;br /&gt;from django.core.management import setup_environ&lt;br /&gt;try:&lt;br /&gt;    import settings.development as settings&lt;br /&gt;except ImportError:&lt;br /&gt;    import settings.production as settings&lt;br /&gt;setup_environ(settings)&lt;br /&gt;&lt;br /&gt;from django.contrib.sites.models import Site&lt;br /&gt;s = Site.objects.get(pk=1)&lt;br /&gt;s.domain = "musicmans.ru"&lt;br /&gt;s.name = "Меломаны"&lt;br /&gt;s.save()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;и запустим выполнение (правой кнопкой на файле - Run As - Python Run). Пропишем SITE_ID=1 в настройках.&lt;br /&gt;&lt;br /&gt;Для того, чтобы переменные настроек 'STATIC_URL', 'DEBUG' (и другие в будущем), а также имя и домен сайта были доступны в шаблонах (я их использую в base.html) &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/templates/api/#writing-your-own-context-processors"&gt;напишем свои контекстные процессоры&lt;/a&gt; для темплейтов. Для этого создадим пакет питона (new - pydev package) в /src/, назовем, например, apps.djutils. В нем мы будем собирать все дополнительную функциональность проекта, которая может пригодиться и в будущем. &lt;br /&gt;&lt;br /&gt;Создадим модуль питона (new - pydev module) в этом пакете под названием context_processors, следующим содержимым:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from django.contrib.sites.models import Site, RequestSite&lt;br /&gt;&lt;br /&gt;def current_site(request):&lt;br /&gt;    try:&lt;br /&gt;        current_site = Site.objects.get_current()&lt;br /&gt;    except Site.DoesNotExist:&lt;br /&gt;        current_site = RequestSite(request)&lt;br /&gt;    &lt;br /&gt;    return {&lt;br /&gt;            'SITE_NAME': current_site.name,&lt;br /&gt;            'SITE_DOMAIN': current_site.domain,&lt;br /&gt;            }&lt;br /&gt;    &lt;br /&gt;def settings_processor(*settings_list):&lt;br /&gt;    def _processor(request):&lt;br /&gt;        from django.conf import settings&lt;br /&gt;        settings_dict = {}&lt;br /&gt;        for setting_name in settings_list:&lt;br /&gt;            settings_dict[setting_name] = getattr(settings, setting_name)&lt;br /&gt;        return settings_dict&lt;br /&gt;    return _processor&lt;br /&gt;&lt;br /&gt;dj_settings = settings_processor(&lt;br /&gt;   'STATIC_URL', 'DEBUG'&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(Не пугайтесь, Site.objects.get_current() &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/contrib/sites/#caching-the-current-site-object"&gt;кешируется&lt;/a&gt;)&lt;br /&gt;В /settings/common.py &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors"&gt;добавим&lt;/a&gt;:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;TEMPLATE_CONTEXT_PROCESSORS = (&lt;br /&gt;                               "django.contrib.auth.context_processors.auth",&lt;br /&gt;                               "django.core.context_processors.debug",&lt;br /&gt;                               "django.core.context_processors.i18n",&lt;br /&gt;                               "django.core.context_processors.media",&lt;br /&gt;                               "django.contrib.messages.context_processors.messages",&lt;br /&gt;                               "djutils.context_processors.dj_settings",&lt;br /&gt;                               "djutils.context_processors.current_site",&lt;br /&gt;                               )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Теперь в любом темплейте, использующим &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/templates/api/#id1"&gt;RequestContext&lt;/a&gt;, мы получаем значение вышеуказанных переменных.&lt;br /&gt;&lt;br /&gt;Вернемся к шаблону base.html. Код шаблона приводить не буду из-за размеров. Его примерное содержание можно подсмотреть &lt;a rel="nofollow" href="http://github.com/pinax/pinax/blob/master/pinax/templates/default/base.html"&gt;здесь&lt;/a&gt;. К нему простенький css. Для того, чтобы css отдавался как статика при разработке на встроенном веб-сервере django, пропишем в urls.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from django.conf import settings&lt;br /&gt;if settings.DEBUG:&lt;br /&gt;    urlpatterns += patterns('',&lt;br /&gt;        (r'^media/(?P&lt;path&gt;.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),&lt;br /&gt;    )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;а в общие настройки пропишем ADMIN_MEDIA_PREFIX="admin", ибо media по умолчанию занята ADMIN_MEDIA_PREFIX и в случае, если мы ее не переопределим, наша статика работать не будет.&lt;br /&gt;&lt;br /&gt;Для сжатия css, а также для перезагрузки закешированного браузером css файла в случае его обновления установим приложение &lt;a rel="nofollow" href="http://github.com/mintchaos/django_compressor"&gt;django-compressor&lt;/a&gt;:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;$ sudo pip install BeautifulSoup&lt;br /&gt;$ sudo apt-get install git-core&lt;br /&gt;$ sudo pip install git+git://github.com/mintchaos/django_compressor@9b6966260398ff2dbdd11275e083e028e73c7af8#egg=django_compressor&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(Чтобы установить последнюю версию из репозитория удалите @9b6966260398ff2dbdd11275e083e028e73c7af8 , на данный момент это как раз последний коммит.)&lt;br /&gt;Добавим в requirements.txt &lt;br /&gt;BeautifulSoup==3.1.0.1&lt;br /&gt;git+git://github.com/mintchaos/django_compressor@9b6966260398ff2dbdd11275e083e028e73c7af8#egg=django_compressor&lt;br /&gt;и в приложения в settings - compressor.&lt;br /&gt;Добавим в настройки:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;COMPRESS = True&lt;br /&gt;COMPRESS_URL = STATIC_URL&lt;br /&gt;COMPRESS_ROOT = STATIC_ROOT&lt;br /&gt;COMPRESS_CSS_FILTERS = [&lt;br /&gt;     'compressor.filters.cssmin.CSSMinFilter'&lt;br /&gt;]&lt;br /&gt;COMPRESS_JS_FILTERS = [&lt;br /&gt;     'compressor.filters.jsmin.JSMinFilter'&lt;br /&gt;]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В случае отсутствия переменной COMPRESS в настройках проекта - приложением используется переменная DEBUG, поэтому, если вы хотите отключить сжатие на время разработки, просто закомментируйте COMPRESS.&lt;br /&gt;Используем встроенные в приложения фильтры. Также можно использовать &lt;a rel="nofollow" href="http://github.com/mintchaos/django_compressor/tree/master/compressor/filters/"&gt;фильтры&lt;/a&gt; от &lt;a rel="nofollow" href="http://developer.yahoo.com/yui/compressor/"&gt;yahoo&lt;/a&gt; или &lt;a rel="nofollow" href="http://code.google.com/intl/ru/closure/"&gt;google&lt;/a&gt;.&lt;br /&gt;После того как приложение создаст каталог CACHE, добавим его в svn:ignore.&lt;br /&gt;Теперь попробуем использовать этот шаблон. Для начала отключим MAINTENANCE_MODE. &lt;br /&gt;Исправим файл url.py &lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from django.conf.urls.defaults import *&lt;br /&gt;from views import home_page&lt;br /&gt;&lt;br /&gt;urlpatterns = patterns('',&lt;br /&gt;            url(r'^$', home_page, name="home"),&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Создадим файл /src/view.py для проекта:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- mode: python; coding: utf-8; -*-&lt;br /&gt;from annoying.decorators import render_to&lt;br /&gt;&lt;br /&gt;@render_to('homepage.html')&lt;br /&gt;def home_page(request):&lt;br /&gt;   &lt;br /&gt;    return {}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В этом виде используется декоратор функции @render_to. Он поставляется с приложением &lt;a rel="nofollow" href="http://bitbucket.org/offline/django-annoying"&gt;django-annoying&lt;/a&gt;. Установим, добавим в requirements.txt, &lt;a rel="nofollow" href="http://bitbucket.org/offline/django-annoying/wiki/Home"&gt;просмотрим список &lt;/a&gt;возможностей (AutoOneToOne field кстати нам пригодится в приложении users).&lt;br /&gt;homepage.html пока содержит следующие вещи:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;{% extends "base.html" %}&lt;br /&gt;{% load i18n %}&lt;br /&gt;{% block title %}{% trans "Главная страница" %}{% endblock %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;При создании темплейтов сразу закладываем возможность &lt;a href="http://docs.djangoproject.com/en/dev/topics/i18n/internationalization/#id1"&gt;будущей интернационализации&lt;/a&gt;.&lt;br /&gt;Для работы с html кодом используем firebug и &lt;a rel="nofollow" href="http://users.skynet.be/mgueury/mozilla/"&gt;HTML VALIDATOR&lt;/a&gt;. Также я использовал тег {% &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/1.2/ref/templates/builtins/#spaceless"&gt;spaceless &lt;/a&gt;%} в base.html, чтобы сжать выдаваемый html.&lt;br /&gt;&lt;br /&gt;Итак, вернемся к users. Приложение users будет хранить дополнительные поля профилей, и использовать сторонние приложения для регистрации и авторизации по open id. Создадим приложение:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TE6otZIEAXI/AAAAAAAAAGo/Qw-VoN_6T58/s1600/django+create+application.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 106px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TE6otZIEAXI/AAAAAAAAAGo/Qw-VoN_6T58/s400/django+create+application.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498517692858237298" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Переместим его в apps и создадим модель, например, такую:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- coding:utf-8 -*-&lt;br /&gt;from django.db import models&lt;br /&gt;from django.contrib.auth.models import User&lt;br /&gt;&lt;br /&gt;from django.utils.translation import ugettext_lazy as _&lt;br /&gt;&lt;br /&gt;from annoying.fields import AutoOneToOneField#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;GENDER_CHOICES = (&lt;br /&gt;    ('M', 'Мужской'),&lt;br /&gt;    ('F', 'Женский'),&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;class UserProfile(models.Model):&lt;br /&gt;    user = AutoOneToOneField(User, related_name='user_profile', primary_key=True)&lt;br /&gt;    date_birth = models.DateField(verbose_name=_(u'Дата Рождения'), blank=True, null=True)&lt;br /&gt;    gender = models.CharField(verbose_name=_(u'Пол'), max_length=1, choices=GENDER_CHOICES, blank=True, null=True)&lt;br /&gt;    URL = models.URLField(max_length=150, verbose_name=_(u'Ваш сайт'), blank=True, null=True, verify_exists=False)&lt;br /&gt;    ICQ = models.CharField(max_length=30, verbose_name=u'ICQ', blank=True, null=True)&lt;br /&gt;    skype = models.CharField(max_length=100, verbose_name=u'skype', blank=True, null=True)&lt;br /&gt;    jabber = models.CharField(max_length=100, verbose_name=u'jabber', blank=True, null=True)&lt;br /&gt;    mobile = models.CharField(max_length=100, verbose_name=_(u'Мобильный телефон'), blank=True, null=True)&lt;br /&gt;    about = models.TextField(verbose_name=_(u'О себе'), help_text=_(u'Несколько слов о себе.'), blank=True, null=True)&lt;br /&gt;    &lt;br /&gt;    count_login = models.IntegerField(default=0)&lt;br /&gt;    &lt;br /&gt;    last_activity_ip = models.IPAddressField(null=True)&lt;br /&gt;    last_activity_date = models.DateTimeField(null=True)&lt;br /&gt;    &lt;br /&gt;    class Meta:&lt;br /&gt;        verbose_name = _(u'Профиль пользователя')&lt;br /&gt;        verbose_name_plural = _(u'Профили пользователей')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Добавим в installed apps 'users'. Создаем первоначальную миграцию для приложения users (вызов custom command manage.py, см. изображение выше):&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TE_UrGJhxtI/AAAAAAAAAG4/8Jr3pvEmrrI/s1600/schemamigration+users+initial.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 203px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TE_UrGJhxtI/AAAAAAAAAG4/8Jr3pvEmrrI/s400/schemamigration+users+initial.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498847506892703442" /&gt;&lt;/a&gt;&lt;br /&gt;Можно ознакомиться с содержимым users/migrations. Для создания таблицы, вместо syncdb запускаем migrate users. После изменения модели запускаем schemamigration users --auto и снова migrate users для изменения базы.&lt;br /&gt;&lt;br /&gt;Итак, профили у нас есть, приступим к регистрации.&lt;br /&gt;$pip install hg+http://bitbucket.org/ubernostrum/django-registration@d36a38202ee3#egg=django-registration&lt;br /&gt;обновляем hg+http://bitbucket.org/ubernostrum/django-registration@d36a38202ee3#egg=django-registration в requirements.txt.&lt;br /&gt;Читаем &lt;a rel="nofollow" href="http://docs.b-list.org/django-registration/0.8/"&gt;документацию&lt;/a&gt; (&lt;a rel="nofollow"  href="http://bitbucket.org/ubernostrum/django-registration/src/tip/docs/quickstart.rst"&gt;быстрый старт&lt;/a&gt;).&lt;br /&gt;Добавляем registration в приложения. Добавляем в настройки ACCOUNT_ACTIVATION_DAYS = 3.&lt;br /&gt;&lt;br /&gt;Необходимые темплейты для приложения:&lt;br /&gt;**registration/registration_form.html**&lt;br /&gt;**registration/registration_complete.html**&lt;br /&gt;**registration/activate.html**&lt;br /&gt;**registration/activation_complete.html**&lt;br /&gt;**registration/activation_email_subject.txt**&lt;br /&gt;**registration/activation_email.txt**&lt;br /&gt;Вот &lt;a rel="nofollow" href="http://habrahabr.ru/blogs/django/74165/"&gt;здесь&lt;/a&gt; можно посмотреть пример темплейта (на другое смотреть не надо, сам механизм работы приложения существенно изменился). Создадим их в директории users/templates/users/ .&lt;br /&gt;Хотел перенести все темплейты туда, не вышло, темплейт e-mail'а, отсылаемого при регистрации прописан &lt;a rel="nofollow" href="http://bitbucket.org/ubernostrum/django-registration/src/d36a38202ee3/registration/models.py#cl-248"&gt;жестко&lt;/a&gt;, а также жестко они прописаны в тестах django-registration (структура ниже). &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TFLlfAubHXI/AAAAAAAAAHI/E9hqg651F9Y/s1600/users+template+struture.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 214px; height: 400px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TFLlfAubHXI/AAAAAAAAAHI/E9hqg651F9Y/s400/users+template+struture.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5499710415906217330" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Теперь добавим в urls.py сайта&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;urlpatterns = patterns('',&lt;br /&gt;            url(r'^$', home_page, name="home"),&lt;br /&gt;            (r'^users/auth/', include('registration.backends.default.urls')),&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Пробуем зайти по адресу http://localhost:8000/users/auth/register/ .&lt;br /&gt;&lt;br /&gt;Идея такова: для простой регистрации и регистрации по openid создадим свои &lt;a rel="nofollow" href="http://docs.b-list.org/django-registration/0.8/backend-api.html"&gt;backend'ы&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Для начала напишем backend для простой регистрации. Наследуем класс &lt;a rel="nofollow" href="http://bitbucket.org/ubernostrum/django-registration/src/d36a38202ee3/registration/backends/default/"&gt;дефолтного бекенда&lt;/a&gt; в наше приложение users, копируем urls.py и правим маски url'ов в файле urls.py сайта и бекенда.&lt;br /&gt;__init__.py бекенда:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from registration.backends.default import DefaultBackend#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;from users.forms import DJRegistrationForm#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;class DjBackend(DefaultBackend):&lt;br /&gt;&lt;br /&gt;    def get_form_class(self, request):&lt;br /&gt;        """&lt;br /&gt;        Return the default form class used for user registration.&lt;br /&gt;        """&lt;br /&gt;        return DJRegistrationForm&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Код формы регистрации см. ниже.&lt;br /&gt;&lt;br /&gt;Все работает но не все устраивает. Для начала мне не нравится длина input. Переопределим аттрибуты виджета, переопределением форм. Создадим файл forms.py в приложении users (код ниже). &lt;br /&gt;&lt;br /&gt;Далее. Так как приложение использует отправку почты по smtp нам на данном этапе неплохо было бы его отслеживать. Можно запустить тестовый smtp сервер python (python -m smtpd -n -c DebuggingServer localhost:1025), но я предлагаю пойти другим путем.&lt;br /&gt;Существует замечательное приложение django-mailer, которое собирает почту в базе, а отправляет по крону. Это нам гарантирует доставку почты, а также избавляет от ошибок при отсутствии доступа к smtp серверу. Его мы добавим в общие настройки.&lt;br /&gt;Итак, устанавливаем.&lt;br /&gt;$pip install git+git://github.com/jtauber/django-mailer@eb236b23a597753a0662290bc3b2666882515791#eggs=django-mailer&lt;br /&gt;requirements.txt не забываем.&lt;br /&gt;А теперь используем новую возможность django-1.2 - &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/email/#e-mail-backends"&gt;EMAIL_BACKENDS&lt;/a&gt;. Пропишем в настройках:&lt;br /&gt;EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'&lt;br /&gt;Добавляем в INSTALLED_APPS, синхронизируем базу.&lt;br /&gt;&lt;br /&gt;Пробуем регистрироваться и ищем сериализованный объект сообщения в базе.&lt;br /&gt;Если все работает, добавляем &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/email/#console-backend"&gt;EMAIL_BACKEND&lt;/a&gt; = 'django.core.mail.backends.console.EmailBackend' в settings/development.py и наблюдаем тело письма в консоли eclipse.&lt;br /&gt;&lt;br /&gt;Не забудем поменять &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/settings/#std:setting-LOGIN_URL"&gt;LOGIN_URL&lt;/a&gt; и &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#login-redirect-url"&gt;LOGIN_REDIRECT_URL&lt;/a&gt;:&lt;br /&gt;LOGIN_URL = "/users/auth/login/"&lt;br /&gt;LOGIN_REDIRECT_URL = "/"&lt;br /&gt;&lt;br /&gt;Перейдем к редактированию профиля.&lt;br /&gt;&lt;br /&gt;Для редактирования профиля нам потребуется &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets"&gt;inline formsets&lt;/a&gt;.&lt;br /&gt;views.py для users с одной фукнцией для самостоятельного написания:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;@login_required&lt;br /&gt;@render_to('users/edit_profile.html')&lt;br /&gt;def edit_profile(request):&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;urls.py также самый обычный, самостоятельно.&lt;br /&gt;Так как рендериг формсета и других форм по умолчанию нас не устраивает, создадим два подключаемых темплейта в директории djutils/templates/forms_render:&lt;br /&gt;formset_table.html&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;{{ formset.management_form }}&lt;br /&gt;{% for form in formset.forms %}&lt;br /&gt;{% include "forms_render/form_table.html" %}&lt;br /&gt;{% endfor %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;form_table.html&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;table&lt;br /&gt;{% for field in form %}&lt;br /&gt;    {% if not field.is_hidden %}&lt;br /&gt;    &lt;tr&gt;&lt;td&gt;&lt;span class="arial-bold-90"&gt;{{ field.label_tag }}&lt;/span&gt;&lt;/td&gt;&lt;td&gt;{{ field }}{{ field.errors }}&lt;br /&gt;    &lt;div class="hint"&gt;{{ field.help_text }}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;    {% else %}&lt;br /&gt;    &lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;{{ field }}&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;    {% endif %}    &lt;br /&gt;{% endfor %}&lt;br /&gt;/table&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;table пришлось оставить без кавычек, иначе blogspot выводит нечто непонятное.&lt;br /&gt;Соответственно,&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;   {% include "forms_render/formset_table.html" %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;в темплейте users/edit_profile.html .&lt;br /&gt;&lt;br /&gt;forms.py для приложения users получился такой:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;from django.forms import ModelForm&lt;br /&gt;from django import forms&lt;br /&gt;&lt;br /&gt;from django.utils.translation import ugettext_lazy as _&lt;br /&gt;&lt;br /&gt;from django.contrib.auth import forms as auth_forms&lt;br /&gt;from django.contrib.auth.models import User&lt;br /&gt;&lt;br /&gt;from users.models import UserProfile#@UnresolvedImport&lt;br /&gt;&lt;br /&gt;from registration.forms import RegistrationFormUniqueEmail#@UnresolvedImport&lt;br /&gt;  &lt;br /&gt;class DJRegistrationForm(RegistrationFormUniqueEmail):&lt;br /&gt;    def __init__(self, *args, **kwargs):&lt;br /&gt;        super(DJRegistrationForm, self).__init__(*args, **kwargs)&lt;br /&gt;        self.fields['username'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['email'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['password1'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['password2'].widget.attrs["size"] = 65&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class AuthForm(auth_forms.AuthenticationForm):&lt;br /&gt;    def __init__(self, *args, **kwargs):&lt;br /&gt;        super(AuthForm, self).__init__(*args, **kwargs)&lt;br /&gt;        self.fields['username'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['password'].widget.attrs["size"] = 65&lt;br /&gt;&lt;br /&gt;class PassResetForm(auth_forms.PasswordResetForm):&lt;br /&gt;    def __init__(self, *args, **kwargs):&lt;br /&gt;        super(PassResetForm, self).__init__(*args, **kwargs)&lt;br /&gt;        self.fields['email'].widget.attrs["size"] = 65&lt;br /&gt;&lt;br /&gt;class EditProfileForm(ModelForm):&lt;br /&gt;    &lt;br /&gt;    date_birth = forms.DateField(('%d.%m.%Y',), label=_('Дата рождения'), required=False,  &lt;br /&gt;        widget = forms.DateInput(format='%d.%m.%Y', attrs={&lt;br /&gt;            'class':'input',&lt;br /&gt;            'size':'65'&lt;br /&gt;        })&lt;br /&gt;    )&lt;br /&gt;    &lt;br /&gt;    def __init__(self, *args, **kwargs):&lt;br /&gt;        super(EditProfileForm, self).__init__(*args, **kwargs)&lt;br /&gt;        self.fields['ICQ'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['URL'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['jabber'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['mobile'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['skype'].widget.attrs["size"] = 65&lt;br /&gt;        self.fields['about'].widget.attrs["cols"] = 49&lt;br /&gt;        self.fields['about'].widget.attrs["rows"] = 8&lt;br /&gt;    &lt;br /&gt;    class Meta:&lt;br /&gt;        model = UserProfile&lt;br /&gt;        fields = ['gender', 'date_birth', 'ICQ', 'URL', 'jabber', 'mobile', 'skype', 'about' ]&lt;br /&gt;        &lt;br /&gt;        #не работает http://code.djangoproject.com/ticket/13095&lt;br /&gt;        #widgets = {&lt;br /&gt;        #            'date_birth': forms.DateInput(format="%d.%m.%Y"), &lt;br /&gt;        #           }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Для проверки выключаем бекенд вывода писем в консоль, добавляем конфигурацию smtp сервера:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;EMAIL_HOST='smtp.server.ru'&lt;br /&gt;EMAIL_HOST_USER='musicmans.ru'&lt;br /&gt;EMAIL_HOST_PASSWORD='password'&lt;br /&gt;DEFAULT_FROM_EMAIL='musicmans.ru@server.ru'&lt;br /&gt;SERVER_EMAIL='musicmans.ru@server.ru'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Регистрируемся, выполняем команду django-mailer - manage.py send_mail. Проверяем почту.&lt;br /&gt;&lt;br /&gt;Создадим crontab для сервера в develop и можно сразу прописать на сервере (отправка почты (раз в пять минут), повторная отправка (раз в двадцать минут), удаление неактивных пользователей (раз в сутки); будем добавлять вручную, ибо не так часто требуется):&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;*/5 *   * * *   vermus  (/usr/bin/python /srv/musicmans/root/src/manage.py send_mail &gt;&gt; /srv/musicmans/logs/cron_mail.log 2&gt;&amp;1)&lt;br /&gt;0,20,40 *       * * *   vermus  (/usr/bin/python /srv/musicmans/root/src/manage.py retry_deferred &gt;&gt; /srv/musicmans/logs/cron_mail_deferred.log 2&gt;&amp;1)&lt;br /&gt;0   0   * * *   vermus  (/usr/bin/python /srv/musicmans/root/src/manage.py cleanupregistration  &gt;&gt; /srv/musicmans/logs/cleanupregistration .log 2&gt;&amp;1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Запускаем тесты, и если все ок, &lt;a rel="nofollow" href="http://www.eclipse.org/forums/index.php?t=msg&amp;&amp;th=160976&amp;goto=546822#msg_546822"&gt;переключаемся в trunk&lt;/a&gt; и &lt;a rel="nofollow" href="http://habrahabr.ru/blogs/development_tools/57591/"&gt;мержим&lt;/a&gt; ветку &lt;a rel="nofollow"href="http://www.eclipse.org/subversive/documentation/teamSupport/merge_dialog.php"&gt;users&lt;/a&gt;, закрываем все задачи в redmine и &lt;br /&gt;$fab production deploy&lt;br /&gt;(можно в него дописать запуск install.py)&lt;br /&gt;&lt;br /&gt;Кстати в &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django_19.html"&gt;fabfile.py&lt;/a&gt; закрались ошибочки, &lt;br /&gt;if "y" == prompt('Install the necessary applications (y/n)?', default="n"):&lt;br /&gt;      install_requirements();&lt;br /&gt;надо выполнять после svn update, а svn update для production.py не будет обновлять maintenance_mode, так как для svn файл уже обновлен,  также рестартовать необходимо и uwsgi, смотрим обновленный &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django_19.html"&gt;fabfile.py&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Ну и как обычно, результат смотрим &lt;a href="http://musicmans.ru/"&gt;http://musicmans.ru/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TFUJgChoOeI/AAAAAAAAAHQ/JlCYKRm4PJo/s1600/musimans+user+registation.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 183px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TFUJgChoOeI/AAAAAAAAAHQ/JlCYKRm4PJo/s400/musimans+user+registation.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5500312965941311970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Аутентификация через OpenId и написание тестов для нашего приложения в следующей статье.&lt;br /&gt;&lt;br /&gt;ps. Как сделать подсвечивающиеся меню расскажу отдельным постом, если кто заинтересуется.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-6349922584850893423?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/pZqXdssK834" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/6349922584850893423/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/07/musicmansru-django_31.html#comment-form" title="Комментарии: 6" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6349922584850893423?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6349922584850893423?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/pZqXdssK834/musicmansru-django_31.html" title="musicmans.ru | Как сделать сайт на Django | Пользователи" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_kR0dajk9XAw/TE6otZIEAXI/AAAAAAAAAGo/Qw-VoN_6T58/s72-c/django+create+application.png" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/07/musicmansru-django_31.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIBQnY6fSp7ImA9Wx5SGEg.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-4502361316041127619</id><published>2010-07-19T19:50:00.017+04:00</published><updated>2010-08-15T10:09:13.815+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-15T10:09:13.815+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="pip" /><category scheme="http://www.blogger.com/atom/ns#" term="django-maintenancemode" /><category scheme="http://www.blogger.com/atom/ns#" term="fabric" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Развертывание</title><content type="html">Я &lt;a href="http://vermus.blogspot.com/2010/06/musicmansru-django.html"&gt;обещал&lt;/a&gt; выкладывать все этапы работы на &lt;a href="http://musicmans.ru"&gt;http://musicmans.ru&lt;/a&gt;, поэтому настала пора вывесить табличку "Сайт в разработке" :), заодно наладив работу развертывания. &lt;br /&gt;&lt;br /&gt;Итак, задачи: создать приложение по вводу сайта в режим обслуживания, настроить сервер, автоматизировать процесс развертки на сервер с помощью fabric.&lt;br /&gt;&lt;br /&gt;Вспомним о том, что у нас есть &lt;a href="http://vermus.blogspot.com/2010/06/redmine-eclipse-mylyn.html"&gt;redmine и mylyn&lt;/a&gt;, создадим данные задачи (не забываем создать категории задач в настройках проекта в redmine). &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;django-maintenancemode&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Для ввода сайта в режим обслуживания есть целое &lt;a rel="nofollow" href="http://code.google.com/p/django-maintenancemode/"&gt;приложение&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Устанавливаем:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;C:\&gt;c:\Python26\Scripts\pip.exe install django-maintenancemode&lt;br /&gt;Downloading/unpacking django-maintenancemode&lt;br /&gt;  Downloading django-maintenancemode-0.9.2.tar.gz&lt;br /&gt;  Running setup.py egg_info for package django-maintenancemode&lt;br /&gt;Installing collected packages: django-maintenancemode&lt;br /&gt;  Running setup.py install for django-maintenancemode&lt;br /&gt;Successfully installed django-maintenancemode&lt;br /&gt;Cleaning up...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Прописываем в requirements.txt:&lt;br /&gt;&lt;br /&gt;django-maintenancemode==0.9.2&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://pypi.python.org/pypi/django-maintenancemode/"&gt;Настраиваем&lt;/a&gt;. В MIDDLEWARE_CLASSES &lt;a rel="nofollow" href="http://code.google.com/p/django-maintenancemode/source/browse/trunk/testproject/settings.py"&gt;добавляем&lt;/a&gt; "maintenancemode.middleware.MaintenanceModeMiddleware". &lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TDKy3nyh5XI/AAAAAAAAAF4/OaHsrBbOyR8/s1600/middleware+settings.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 245px; height: 289px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TDKy3nyh5XI/AAAAAAAAAF4/OaHsrBbOyR8/s400/middleware+settings.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5490647564361524594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;В templates создаем файл &lt;a rel="nofollow" href="http://code.google.com/p/django-maintenancemode/source/browse/trunk/testproject/templates/503.html"&gt;503.html&lt;/a&gt; со статическим содержимым того, что будет выводиться в период обслуживания сайта.&lt;br /&gt;&lt;br /&gt;Функции приложения:&lt;br /&gt;* MAINTENANCE_MODE - включает\выключает режим обслуживания, по умолчанию: False.&lt;br /&gt;* Страница 503 не отображается залогиненым админам и клиентам с ip адресами, входящими в &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#internal-ips"&gt;INTERNAL_IPS&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Итак, пропишем MAINTENANCE_MODE = True, в development.py и в production.py (в development.py закомментируем вскоре).&lt;br /&gt;&lt;br /&gt;Запускаем pydev сервер, отладку, переходим на страницу и видим следующее:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TDK457bcqUI/AAAAAAAAAGI/QobQ3rjfphM/s1600/temp+unav.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 361px; height: 208px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TDK457bcqUI/AAAAAAAAAGI/QobQ3rjfphM/s400/temp+unav.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5490654201062926658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Немного поправим 503.html по своему желанию.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Настройка сервера&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Устанавливаем и настраиваем &lt;a rel="nofollow" href="http://www.meppum.com/2009/jan/17/installing-django-ubuntu-intrepid/"&gt;фаерволл&lt;/a&gt;:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;$ sudo aptitude install ufw&lt;br /&gt;$ sudo ufw enable&lt;br /&gt;$ sudo ufw logging on&lt;br /&gt;$ sudo ufw allow 80/tcp&lt;br /&gt;$ sudo ufw allow SSH_port&lt;br /&gt;$ sudo ufw default deny&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Настройку веб сервера выбрал &lt;a rel="nofollow" href="http://blog.zacharyvoase.com/2010/03/05/django-uwsgi-nginx/"&gt;такую&lt;/a&gt; (nginx + uwsgi). Тем более, nginx, начиная &lt;a rel="nofollow" href="http://sysoev.ru/nginx/changes.html"&gt;с версии 0.8.40&lt;/a&gt; поддерживает uwsgi из коробки.&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;# apt-get install gcc libssl-dev libpcre++-dev make&lt;br /&gt;# wget http://sysoev.ru/nginx/nginx-0.8.44.tar.gz&lt;br /&gt;# tar -xzvf nginx-0.8.44.tar.gz&lt;br /&gt;# cd nginx-0.8.44/&lt;br /&gt;# ./configure --conf-path=/etc/nginx/nginx.conf \&lt;br /&gt;  --prefix=/usr \&lt;br /&gt;  --error-log-path=/var/log/nginx/error.log \&lt;br /&gt;  --pid-path=/var/run/nginx.pid \&lt;br /&gt;  --lock-path=/var/lock/nginx.lock \&lt;br /&gt;  --http-log-path=/var/log/nginx/access.log \&lt;br /&gt;  --with-http_dav_module \&lt;br /&gt;  --http-client-body-temp-path=/var/lib/nginx/body \&lt;br /&gt;  --with-http_ssl_module \&lt;br /&gt;  --http-proxy-temp-path=/var/lib/nginx/proxy \&lt;br /&gt;  --with-http_stub_status_module \&lt;br /&gt;  --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \&lt;br /&gt;  --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \&lt;br /&gt;  --http-scgi-temp-path=/var/lib/nginx/scgi \&lt;br /&gt;  --with-debug \&lt;br /&gt;  --with-http_flv_module &lt;br /&gt;# make&lt;br /&gt;# make install&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Скрипт запуска /etc/init.d/nginx я взял из &lt;a rel="nofollow" href="http://packages.debian.org/lenny/nginx"&gt;стандартного пакета debian&lt;/a&gt; (устанавливать его не нужно, ибо можно перетереть новые конфиги старыми. В принципе, не страшно, так как мы будем их писать заново, но например mime.types могут отличаться).&lt;br /&gt;&lt;br /&gt;Создаем рабочую директорию для сайта, например /srv/musicmans&lt;br /&gt;Структура:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;/srv/musicmans &lt;br /&gt;|  backups&lt;br /&gt;--|  src&lt;br /&gt;--|  db&lt;br /&gt;|  logs&lt;br /&gt;|  root&lt;br /&gt;--|  src&lt;br /&gt;--|  www&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;На машине разработчика создаем файл в src wsgi.py (основой файл запуска проекта для веб-сервера):&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;import os&lt;br /&gt;import sys&lt;br /&gt;import locale&lt;br /&gt;import django.core.handlers.wsgi&lt;br /&gt;&lt;br /&gt;DIR=(os.path.abspath(__file__))&lt;br /&gt;sys.path.append(DIR)&lt;br /&gt;os.environ['DJANGO_SETTINGS_MODULE'] = 'settings.production'&lt;br /&gt;&lt;br /&gt;def force_utf8_hack():&lt;br /&gt;  reload(sys)&lt;br /&gt;  sys.setdefaultencoding('utf-8')&lt;br /&gt;  for attr in dir(locale):&lt;br /&gt;    if attr[0:3] != 'LC_':&lt;br /&gt;      continue&lt;br /&gt;    aref = getattr(locale, attr)&lt;br /&gt;    locale.setlocale(aref, '')&lt;br /&gt;    (lang, enc) = locale.getlocale(aref)&lt;br /&gt;    if lang != None:&lt;br /&gt;      try:&lt;br /&gt;        locale.setlocale(aref, (lang, 'UTF-8'))&lt;br /&gt;      except:&lt;br /&gt;        os.environ[attr] = lang + '.UTF-8'&lt;br /&gt;&lt;br /&gt;force_utf8_hack()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;application = django.core.handlers.wsgi.WSGIHandler()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Перед тем, как настраивать сервер, запустим &lt;a rel="nofollow" href="http://docs.djangoproject.com/en/dev/topics/testing/"&gt;тестирование&lt;/a&gt; проекта.&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TD7MYl0S-pI/AAAAAAAAAGY/VVkXKQCKNmg/s1600/django+custom+command.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 110px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TD7MYl0S-pI/AAAAAAAAAGY/VVkXKQCKNmg/s400/django+custom+command.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5494053318278970002" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Введем команду test и получим &lt;a rel="nofollow" href="http://code.djangoproject.com/ticket/7756"&gt;ошибку&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Добавим в development.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;INSTALLED_APPS += (&lt;br /&gt;                   'django.contrib.admin',&lt;br /&gt;                   )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;А также в manage.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;    if settings.DEBUG and command == "test":&lt;br /&gt;        settings.MAINTENANCE_MODE = False&lt;br /&gt;    &lt;br /&gt;    execute_manager(settings)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ибо нам тесты в режиме обслуживания не нужны, да и не отрабатывают они, у меня вышла ошибка отсутствия темплейта 503.html и куча других.&lt;br /&gt;&lt;br /&gt;Сделаем коммит.&lt;br /&gt;&lt;br /&gt;Вернемся к серверу с сайтом. Сделаем предварительную настройку: &lt;br /&gt;&lt;br /&gt;1. Перейдем в директорию /srv/musicmans/ и заберем транк в root:&lt;br /&gt;&lt;br /&gt;$export SVN_SSH="ssh -l loginname"&lt;br /&gt;&lt;br /&gt;или сделаем пару ключа (она нам все рано пригодиться при использовании fabric). На сервере с сайтом:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;$ ssh-keygen -t dsa&lt;br /&gt;$ cat ~/.ssh/id_dsa.pub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;копируем вывод, добавляем на сервер с кодом в ~/.ssh/authorized_keys2 на сервер (если файла нет, то touch ~/.ssh/authorized_keys2 &amp;&amp; chmod 600 ~/.ssh/authorized_keys2 ). Пробуем логиниться без пароля.&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;$svn checkout --depth=empty svn+ssh://codesrv/repos/musicmans/trunk/backend root&lt;br /&gt;$cd root/&lt;br /&gt;$svn update --set-depth=infinity www&lt;br /&gt;$svn update --set-depth=infinity src&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Так как нам нужны только две директории src и www, то &lt;a rel="nofollow" href="http://svnbook.red-bean.com/en/1.5/svn.advanced.sparsedirs.html"&gt;делаем пустой&lt;/a&gt; checkout, после чего обновляем две директории с бесконечной вложенностью. После этого svn update будет нам обновлять только директории www и src.&lt;br /&gt;&lt;br /&gt;Устанавливаем необходимые приложения для сайта:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;vermus@musicmans:~$ cd /srv/musicmans/root/src&lt;br /&gt;vermus@musicmans:~$ sudo pip install -r requirements.txt --download-cache /usr/src/pipcache/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Установка postgresql:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;# apt-get install postgresql python-psycopg2&lt;br /&gt;# su postgres&lt;br /&gt;$ createuser musicmans --no-superuser --no-createdb --no-createrole --login --pwprompt --encrypted&lt;br /&gt;$ createdb --owner=musicmans --encoding=utf-8 musicmans&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;База создана, пробуем синхронизировать django с базой данных (мы &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django.html"&gt;это делали уже&lt;/a&gt; на машине разработчика, но так как у нас база на сайте будет жить своей жизнью, а девелоперская своей, то сделаем это еще раз, т.е. миграцию данных выполнять не будем):&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;vermus@musicmans:~$ cd /srv/musicmans/root/src/&lt;br /&gt;vermus@musicmans:/srv/musicmans/root/src$ python manage.py syncdb&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Итак, все в порядке. Осталось настроить веб-сервер. Конфигурацию мы уже выбрали.&lt;br /&gt;&lt;br /&gt;Установим uwsgi сервер:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;$ cd /usr/src/&lt;br /&gt;$ sudo pip install http://projects.unbit.it/downloads/uwsgi-latest.tar.gz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Настроим &lt;a rel="nofollow" href="http://library.linode.com/lemp-guides/debian-5-lenny/#deploy_python_code_with_wsgi_and_uwsgi"&gt;скрипт init.d&lt;/a&gt; для &lt;a rel="nofollow" href="http://projects.unbit.it/uwsgi/wiki/Doc"&gt;запуска&lt;/a&gt; через &lt;a rel="nofollow" href="http://www.westphahl.net/blog/2010/4/8/running-django-nginx-and-uwsgi/"&gt;файловый сокет&lt;/a&gt; сервера uwsgi с проектом (/etc/init.d/uwsgi):&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;# cat uwsgi&lt;br /&gt;### BEGIN INIT INFO&lt;br /&gt;# Provides:          uwsgi&lt;br /&gt;# Required-Start:    $all&lt;br /&gt;# Required-Stop:     $all&lt;br /&gt;# Default-Start:     2 3 4 5&lt;br /&gt;# Default-Stop:      0 1 6&lt;br /&gt;# Short-Description: starts the uwsgi app server&lt;br /&gt;# Description:       starts uwsgi app server using start-stop-daemon&lt;br /&gt;### END INIT INFO&lt;br /&gt;&lt;br /&gt;PATH=/sbin:/bin:/usr/sbin:/usr/bin&lt;br /&gt;DAEMON=/usr/bin/uwsgi&lt;br /&gt;&lt;br /&gt;OWNER=uwsgirun&lt;br /&gt;&lt;br /&gt;NAME=uwsgi&lt;br /&gt;DESC=uwsgi&lt;br /&gt;&lt;br /&gt;test -x $DAEMON || exit 0&lt;br /&gt;&lt;br /&gt;# Include uwsgi defaults if available&lt;br /&gt;if [ -f /etc/uwsgi ] ; then&lt;br /&gt;        . /etc/uwsgi&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;set -e&lt;br /&gt;&lt;br /&gt;DAEMON_OPTS="--socket /var/lib/nginx/uwsgi/musicmans.sock --chmod-socket -d /srv/musicmans/logs/uwsgi.log --pythonpath $PYTHONPATH --module $MODULE"&lt;br /&gt;&lt;br /&gt;case "$1" in&lt;br /&gt;  start)&lt;br /&gt;        echo -n "Starting $DESC: "&lt;br /&gt;        start-stop-daemon --start --chuid $OWNER:$OWNER --user $OWNER \&lt;br /&gt;                --exec $DAEMON -- $DAEMON_OPTS&lt;br /&gt;        echo "$NAME."&lt;br /&gt;        ;;&lt;br /&gt;  stop)&lt;br /&gt;        echo -n "Stopping $DESC: "&lt;br /&gt;        start-stop-daemon --signal 3 --user $OWNER --quiet --retry 2 --stop \&lt;br /&gt;                --exec $DAEMON&lt;br /&gt;        echo "$NAME."&lt;br /&gt;        ;;&lt;br /&gt;  reload)&lt;br /&gt;        killall -1 $DAEMON&lt;br /&gt;        ;;&lt;br /&gt;  force-reload)&lt;br /&gt;        killall -15 $DAEMON&lt;br /&gt;       ;;&lt;br /&gt;  restart)&lt;br /&gt;        echo -n "Restarting $DESC: "&lt;br /&gt;        start-stop-daemon --signal 3 --user $OWNER --quiet --retry 2 --stop \&lt;br /&gt;                --exec $DAEMON&lt;br /&gt;        sleep 1&lt;br /&gt;        start-stop-daemon --user $OWNER --start --quiet --chuid $OWNER:$OWNER \&lt;br /&gt;               --exec $DAEMON -- $DAEMON_OPTS&lt;br /&gt;        echo "$NAME."&lt;br /&gt;        ;;&lt;br /&gt;  status)&lt;br /&gt;        killall -10 $DAEMON&lt;br /&gt;        ;;&lt;br /&gt;      *)&lt;br /&gt;            N=/etc/init.d/$NAME&lt;br /&gt;            echo "Usage: $N {start|stop|restart|reload|force-reload|status}" &gt;&amp;2&lt;br /&gt;            exit 1&lt;br /&gt;            ;;&lt;br /&gt;    esac&lt;br /&gt;    exit 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Не забываем создать пользователя uwsgirun, под которым будет запускаться uwsgi. Параметр chmod-socket устанавливает права 666 на сокет, если Вас это не устраивает смотрите документацию. Если uwsgi после запуска ругается на права, проверьте права на директорию с сокетом, на директорию с логами.&lt;br /&gt;Создадим файл конфигурации /etc/uwsgi :&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;PYTHONPATH=/srv/musicmans/root/src&lt;br /&gt;MODULE=wsgi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Обратите внимание, что мы указываем имя модуля python, а не имя файла.&lt;br /&gt;Устанавливаем chmod 755 для скрипта /etc/init.d/uwsgi , загружаем при старте системы:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;root@musicmans:/var/lib/nginx# chown -R uwsgirun uwsgi&lt;br /&gt;root@musicmans:/etc/init.d# chmod 755 uwsgi&lt;br /&gt;root@musicmans:/etc/init.d# update-rc.d -f uwsgi defaults&lt;br /&gt;root@musicmans:/etc/init.d# /etc/init.d/uwsgi start&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Конфиги nginx: nginx.conf, стандартный из пакета debian. Конфиг сайта:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;root@musicmans:/etc/nginx/sites-available# cat musicmans&lt;br /&gt;#serving Django.&lt;br /&gt;upstream django {&lt;br /&gt;    ip_hash;&lt;br /&gt;    server unix:/var/lib/nginx/uwsgi/musicmans.sock;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;server {&lt;br /&gt;     listen      80;&lt;br /&gt;     server_name musicmans.ru;&lt;br /&gt;     charset     utf-8;&lt;br /&gt;     error_log   /srv/musicmans/logs/nginx_error.log   info;&lt;br /&gt;     access_log /srv/musicmans/logs/nginx_access.log;&lt;br /&gt;&lt;br /&gt;     # Django admin media.&lt;br /&gt;     #location /media/admin/ {&lt;br /&gt;     #               alias lib/python2.6/site-packages/django/contrib/admin/media/;&lt;br /&gt;     #               }&lt;br /&gt;&lt;br /&gt;     # Your project's static media.&lt;br /&gt;     location /media/ {&lt;br /&gt;                  alias /srv/musicmans/root/www/media/;&lt;br /&gt;                         }&lt;br /&gt;&lt;br /&gt;     # Finally, send all non-media requests to the Django server.&lt;br /&gt;     location / {&lt;br /&gt;                 uwsgi_pass  django;&lt;br /&gt;                 include     uwsgi_params;&lt;br /&gt;                 }&lt;br /&gt;&lt;br /&gt;     location ~ /.svn/ {&lt;br /&gt;           deny all;&lt;br /&gt;               }&lt;br /&gt;&lt;br /&gt;       }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Включаем сайт&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;# ln -s /etc/nginx/sites-available/musicmans /etc/nginx/sites-enabled/musicmans&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Перезапускаем /etc/init.d/uwsgi restart и /etc/init.d/nginx restart.&lt;br /&gt;&lt;br /&gt;Заходим &lt;a href="http://musicmans.ru/"&gt;http://musicmans.ru/&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TEGelozzGnI/AAAAAAAAAGg/pzoHVxex1Rs/s1600/musicmans+main.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 146px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TEGelozzGnI/AAAAAAAAAGg/pzoHVxex1Rs/s400/musicmans+main.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5494847389815741042" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Процесс развертывания кода и структуры базы данных на сервер с помощью fabric&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Установим на машину разработчика &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django.html"&gt;pip&lt;/a&gt; и &lt;a href="http://djangoizm.blogspot.com/2009/05/fabric.html"&gt;fabric&lt;/a&gt;.&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#pip install fabric&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Создаем fab файл с командами fabric в корне проекта &lt;a rel="nofollow" href="http://djangotricks.blogspot.com/2010/05/deployment-using-fabric.html"&gt;для установки необходимых приложений из requirements.txt&lt;/a&gt;, обновления кода, &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django.html"&gt;миграции базы данных&lt;/a&gt; и перезапуска Nginx:&lt;br /&gt;&lt;br /&gt;    * Включить режим обслуживания сайта (см. выше).&lt;br /&gt;    * Сделать резервную копию базы данных.&lt;br /&gt;    * Сделать резервную копию кода (src) сайта.&lt;br /&gt;    * Обновить код с репозитория subversion.&lt;br /&gt;    * Запустить миграцию базы данных (South).&lt;br /&gt;    * Выключить режим обслуживания сайта.&lt;br /&gt;&lt;br /&gt;У нас fabric 0.9.1, а в 1.0 &lt;a rel="nofollow" href="http://docs.fabfile.org/1.0a/api/contrib/django.html"&gt;обещают поддержку django&lt;/a&gt;. Ну а пока ее нет создаем fabfile.py в корне проекта следующего содержания (перевод windows консоли для понимания удаленного UTF8 в случае ошибок - шрифт cmd окна Lucida Console (или любой другой true type), далее команда chcp 65001).&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- mode: python; coding: utf-8; -*-&lt;br /&gt;import sys&lt;br /&gt;from fabric.api import env, run, prompt, local, get, cd, sudo, require&lt;br /&gt;from fabric.state import output&lt;br /&gt;from fabric.contrib.files import uncomment&lt;br /&gt;import datetime&lt;br /&gt;&lt;br /&gt;now = datetime.datetime.now()&lt;br /&gt;&lt;br /&gt;def production():&lt;br /&gt;    #здесь данные об удаленном сервере с сайтом&lt;br /&gt;    env.environment = "production"&lt;br /&gt;    &lt;br /&gt;    env.hosts = ['codesrv']&lt;br /&gt;    env.user = 'vermus'&lt;br /&gt;    env.path = '/srv/musicmans/root'&lt;br /&gt;    env.root_path = '/srv/musicmans'&lt;br /&gt;    &lt;br /&gt;    env.db_name = 'musicmans'&lt;br /&gt;    env.db_user = 'musicmans'&lt;br /&gt;    &lt;br /&gt;def deploy():&lt;br /&gt;    """&lt;br /&gt;    In the current version fabfile no initial database creation and configure the virtual server host.&lt;br /&gt;    """ &lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    &lt;br /&gt;    if env.environment == 'production':&lt;br /&gt;        if "y" != prompt('Are you sure you want to update the production site (test &amp; check in trunk release code!)? (y/[n])?', default="n"):&lt;br /&gt;            return&lt;br /&gt;    &lt;br /&gt;    if "y" == prompt('Set MAINTENANCE_MODE (y/n)?', default="y"):&lt;br /&gt;        maintenance_mode() &lt;br /&gt;    &lt;br /&gt;    if "y" == prompt('Create database backup? (y/n)?', default="y"):&lt;br /&gt;        backup_db()&lt;br /&gt;        &lt;br /&gt;    if "y" == prompt('Create source code backup? (y/n)?', default="y"):&lt;br /&gt;        backup_src()&lt;br /&gt;&lt;br /&gt;    update_from_svn()&lt;br /&gt;&lt;br /&gt;    if "y" == prompt('Install the necessary applications (y/n)?', default="n"):&lt;br /&gt;        install_requirements();&lt;br /&gt;&lt;br /&gt;    migrate_database()&lt;br /&gt;&lt;br /&gt;    maintenance_mode(set=False)    &lt;br /&gt;&lt;br /&gt;    restart_webserver()        &lt;br /&gt;&lt;br /&gt;def install_requirements():&lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    print(" * install the necessary applications...")&lt;br /&gt;    &lt;br /&gt;    requirements_file = env.path+'/src/requirements.txt'&lt;br /&gt;    &lt;br /&gt;    args = ['install',&lt;br /&gt;                '-r', requirements_file,&lt;br /&gt;                '--download-cache', '/usr/src/pipcache/'&lt;br /&gt;                ]&lt;br /&gt;    &lt;br /&gt;    sudo('pip %s' % ' '.join(args))&lt;br /&gt;&lt;br /&gt;def maintenance_mode(set=True):&lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    print(" * change production.py and restart nginx...")&lt;br /&gt;    if set:&lt;br /&gt;        uncomment(env.path+'/src/settings/production.py', 'MAINTENANCE_MODE = True')&lt;br /&gt;    else:&lt;br /&gt;        comment(env.path+'/src/settings/production.py', 'MAINTENANCE_MODE = True')&lt;br /&gt;        &lt;br /&gt;    restart_webserver() &lt;br /&gt;&lt;br /&gt;def backup_db():&lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    print(" * create database dump...")&lt;br /&gt;    &lt;br /&gt;    db_name = env.db_name&lt;br /&gt;    db_user = env.db_user&lt;br /&gt;&lt;br /&gt;    backup_file = "backup_%d_%d_%d_%d_%d.sqlgzip" % (now.day, now.month, now.year, now.hour, now.minute)&lt;br /&gt;    backup_dir = env.root_path+'/backups/db'&lt;br /&gt;    with cd(backup_dir):&lt;br /&gt;        run("echo dbpassword | pg_dump -W -U %s -F c %s &gt; %s" % (db_user, db_name, backup_file))&lt;br /&gt;&lt;br /&gt;def backup_src():&lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    print(" * create source code backup...")&lt;br /&gt;    backup_dir = env.root_path+'/backups/src'&lt;br /&gt;    backup_file = "backup_%d_%d_%d_%d_%d.tar.gz" % (now.day, now.month, now.year, now.hour, now.minute)&lt;br /&gt;    src_dir = env.path+'/src'&lt;br /&gt;    &lt;br /&gt;    run("mkdir -p %s" % backup_dir+'/all')&lt;br /&gt;    run("cp -f -R %s %s" % (src_dir, backup_dir+'/all'))&lt;br /&gt;    run("cp -f -R %s %s" % (env.path+'/www/static', backup_dir+'/all'))&lt;br /&gt;    &lt;br /&gt;    with cd(backup_dir):&lt;br /&gt;        run ('tar -zcf %s %s' % (backup_file, backup_dir+'/all'))&lt;br /&gt;        run ('rm -f -R %s' % (backup_dir+'/all'))&lt;br /&gt;        &lt;br /&gt;       &lt;br /&gt;def update_from_svn():&lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    with cd(env.path):&lt;br /&gt;        run('svn update') #svn checkout сделаем вручную первый раз&lt;br /&gt;&lt;br /&gt;def migrate_database():&lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    with cd(env.path+'/src'):&lt;br /&gt;        run('python manage.py migrate --no-initial-data')&lt;br /&gt;        run('python manage.py syncdb')&lt;br /&gt;        &lt;br /&gt;def restart_webserver():&lt;br /&gt;    require('environment', provided_by=[production])#дописать по желанию dev и stage&lt;br /&gt;    print(" * restart nginx")&lt;br /&gt;    sudo('/etc/init.d/uwsgi restart', pty=True)&lt;br /&gt;    sudo('/etc/init.d/nginx force-reload', pty=True)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Схема такая:&lt;br /&gt;Запуск $fab production deploy с машины разработчика - логин по ssh на сервер с сайтом (fabric выполняет автоматически при выполнении run, sudo и др., используя данные из env), далее выполняются необходимые действия, в том числе svn+ssh с сервера с кодом с транка.&lt;br /&gt;&lt;br /&gt;Добавим в /etc/postgresql/8.4/main/pg_hba.conf следующую строчку:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;# "local" is for Unix domain socket connections only&lt;br /&gt;local   musicmans   musicmans                         md5&lt;br /&gt;local   all         all                               ident&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Обратите внимание, что строчку добавляем перед ident. Она позволит нам соединяться пользователю без логина с указанием пароля при беакпе базы.&lt;br /&gt;&lt;br /&gt;Статья получилась объемной, старался быстрее закончить с технической стороной и перейти наконец к созиданию. :)&lt;br /&gt;&lt;br /&gt;Не забываем про задачи (перспектива planning), указываем время, потраченное на задачи и закрываем их.&lt;br /&gt;&lt;br /&gt;ps. Если вы хотите сразу обеспечить полноценную защиту сайта от различных атак, которая требует более тонкой настройки системы, то следует обратиться к профессионалам или почитать соответствующую литературу. Нам пока некому завидовать, поэтому оставляем все как есть на данном этапе.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-4502361316041127619?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/UCbKDOFLcYY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/4502361316041127619/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/07/musicmansru-django_19.html#comment-form" title="Комментарии: 10" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/4502361316041127619?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/4502361316041127619?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/UCbKDOFLcYY/musicmansru-django_19.html" title="musicmans.ru | Как сделать сайт на Django | Развертывание" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_kR0dajk9XAw/TDKy3nyh5XI/AAAAAAAAAF4/OaHsrBbOyR8/s72-c/middleware+settings.png" height="72" width="72" /><thr:total>10</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/07/musicmansru-django_19.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8GQ3k_eCp7ImA9WxFaEE0.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-3065820426273792977</id><published>2010-07-04T08:37:00.010+04:00</published><updated>2010-07-13T11:57:02.740+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-13T11:57:02.740+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="pip" /><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="South" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Миграция моделей и данных</title><content type="html">В &lt;a href="http://vermus.blogspot.com/2010/07/musicmansru-django-c.html"&gt;прошлый раз&lt;/a&gt; мы остановились на том, что запустили django на машине разработчика. Использование virtualenv оставим на самостоятельное рассмотрение. А вот &lt;a rel="nofollow" href="http://pypi.python.org/pypi/pip"&gt;pip&lt;/a&gt; мы все-таки установим. Тем более, что у нас в проекте уже лежит пустой файл requirements.txt - файл формата pip, со списком приложений, необходимых для нашего проекта. Кстати, пора в него уже прописать:&lt;br /&gt;Django==1.2.1&lt;br /&gt;South==0.7.2&lt;br /&gt;&lt;br /&gt;Далее, качаем pip, разархивируем, устанавливаем:&lt;br /&gt;#apt-get install python-setuptools&lt;br /&gt;pip-0.7.2#python setup.py install&lt;br /&gt;&lt;br /&gt;Теперь перейдем к теме. &lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.proft.com.ua/2010/06/14/migracii-modelej-i-dannyh-v-django/"&gt;South привносит в django&lt;/a&gt; возможность миграции структуры и данных модели. На практике это означает, что если мы что-то поменяли в модели (добавили/удалили поле) то south сам увидит изменения и создаст инструкции для внесения изменений в БД, которые останется только применить на всех экземплярах приложения.&lt;br /&gt;&lt;br /&gt;Основные особенности, которые отмечают разработчики:&lt;br /&gt;&lt;br /&gt;    * отслеживание изменений в модели и создание миграций&lt;br /&gt;    * независимость от движков БД (заявлена поддержка 5 разных типов БД)&lt;br /&gt;    * создание миграций только для выбранного приложения (application)&lt;br /&gt;    * сообщение о возможных конфликтах при комите миграций от других разработчиков&lt;br /&gt;&lt;br /&gt;Далее:&lt;br /&gt;&lt;br /&gt;Устанавливаем South&lt;br /&gt;&gt;C:\Python26\Scripts\pip.exe install South&lt;br /&gt;или&lt;br /&gt;#pip install South&lt;br /&gt;&lt;br /&gt;Вы уже наверное заметили, что у меня две машины разработчика. Одна под windows, другая Kubuntu, поэтому в будущем буду выводить консольные команды той системы, в которой работаю, поскольку они аналогичны.&lt;br /&gt;&lt;br /&gt;Добавляем &lt;a rel="nofollow" href="http://vostryakov.ru/blog/19-south-07-i-django-12/"&gt;south в INSTALLED_APPS&lt;/a&gt; и делаем syncdb (пробуем из gui).&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TDGH3EPG6gI/AAAAAAAAAFw/eXs4GClLhFs/s1600/pyde+syncdb.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 367px; height: 400px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TDGH3EPG6gI/AAAAAAAAAFw/eXs4GClLhFs/s400/pyde+syncdb.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5490318800841009666" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Так как по умолчанию в INSTALLED_APPS прописано несколько стандартных приложений, в том числе django.contrib.auth, поэтому в консоли выполним создание суперпользователя сайта (первый запуск syncdb).&lt;br /&gt;&lt;br /&gt;South применяется отдельно для каждого приложения в проекте, а так как у нас нет приложений, то пока настройку South можно считать законченной (подробнее по вышеприведенным ссылкам или в &lt;a rel="nofollow" href="http://south.aeracode.org/docs/"&gt;документации&lt;/a&gt;). Плюс, мы еще вернемся к обсуждению South позже.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-3065820426273792977?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/vPEXdrBwc2U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/3065820426273792977/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/07/musicmansru-django.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3065820426273792977?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3065820426273792977?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/vPEXdrBwc2U/musicmansru-django.html" title="musicmans.ru | Как сделать сайт на Django | Миграция моделей и данных" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_kR0dajk9XAw/TDGH3EPG6gI/AAAAAAAAAFw/eXs4GClLhFs/s72-c/pyde+syncdb.png" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/07/musicmansru-django.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkECR3s_cSp7ImA9WxFaFk8.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-6866336204970340606</id><published>2010-07-03T14:40:00.004+04:00</published><updated>2010-07-20T14:11:06.549+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-20T14:11:06.549+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Cтруктура проекта</title><content type="html">Структура проекта может быть по вашему вкусу. Тут нет каких-то жестких правил, хотя правила в создании django приложений (не проекта) все-таки есть. Итак, приведем в пример структуру pinax сайта проекта:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;apps&lt;br /&gt;deploy&lt;br /&gt;media&lt;br /&gt;site_media&lt;br /&gt;templates&lt;br /&gt;tests&lt;br /&gt;&lt;br /&gt;__init__.py&lt;br /&gt;context_processors.py&lt;br /&gt;manage.py&lt;br /&gt;urls.py&lt;br /&gt;settings.py&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Мы подсмотрим у &lt;a rel="nofollow" href="http://blog.zacharyvoase.com/2010/02/03/django-project-conventions/"&gt;профессионалов&lt;/a&gt;, virtualenv мы использовать не будем, поэтому немного подсократим:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PROJECT_ROOT/&lt;br /&gt;|-- apps/         # Django приложения сайта&lt;br /&gt;|-- etcs/         # Разные конфигурационные файлы&lt;br /&gt;|-- settings/     # Настройки&lt;br /&gt;|-- templates/    # темплейты для всего сайта&lt;br /&gt;|-- __init__.py   # Инициализация пакета python&lt;br /&gt;|-- requirements.txt #файл &lt;a href="http://pip.openplans.org/#requirements-files"&gt;формата pip&lt;/a&gt; со списком приложений для сайта&lt;br /&gt;|-- manage.py     # файл запуска\управления проекта&lt;br /&gt;`-- urls.py       # Главный URLconf&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Разделение setting для разработки и развертывания одна из постоянно обсуждаемых вещей в django мире. Начнем с того, что разделим общие настройки и настройки развертывания плюс добавим несколько своих директорий. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;|-- PROJECT_ROOT/&lt;br /&gt;|   |-- apps/&lt;br /&gt;|   |-- etc/&lt;br /&gt;|   |-- settings/&lt;br /&gt;|   |   |-- __init__.py&lt;br /&gt;|   |   |-- common.py&lt;br /&gt;|   |   |-- development.py&lt;br /&gt;|   |   `-- production.py&lt;br /&gt;|   |-- templates/&lt;br /&gt;|   |-- __init__.py&lt;br /&gt;|   |-- manage.py&lt;br /&gt;|   `-- urls.py&lt;br /&gt;|-- www/&lt;br /&gt;|   |-- media/&lt;br /&gt;|   |   |-- static/&lt;br /&gt;|-- develop/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Статику (изображения, css, яваскрипт), которую веб сервер будет отдавать напрямую, мы вынесем за пределы PROJECT_ROOT.&lt;br /&gt;&lt;br /&gt;Общие настройки определим в модуле settings.common. В development и production будут находиться специфичные настройки, а также они будут импортровать common, а manage.py будет загружать development.py, а в случае его отсутствия (svn:ignore) -  production.py.&lt;br /&gt;&lt;br /&gt;У меня получилась следующая структура проекта:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8NKKcm3AI/AAAAAAAAAFQ/fF7zon12IXc/s1600/eclipse6.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 237px; height: 400px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8NKKcm3AI/AAAAAAAAAFQ/fF7zon12IXc/s400/eclipse6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5489620939042118658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Обратите внимание на директорию develop, там будут лежать вещи, которые относятся к разработке, например отправим туда файл конфигурации development.py, вдруг кому-нибудь придется развертывать проект для разработки, тогда он легко сможет взять файл оттуда. Также обратите внимание на файл develompent.py в самом проекте, декоратор иконок subversive говорит нам о том, что он находится в svn:ignore.&lt;br /&gt;&lt;br /&gt;Итак, переместим settings.py в common.py.&lt;br /&gt;&lt;br /&gt;Подредактируем файлы примерно следующим образом (вывожу только то, что изменилось):&lt;br /&gt;&lt;br /&gt;common.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- mode: python; coding: utf-8; -*-&lt;br /&gt;'''&lt;br /&gt;Created on 02.07.2010&lt;br /&gt;'''&lt;br /&gt;import os&lt;br /&gt;import sys&lt;br /&gt;&lt;br /&gt;ADMINS = (&lt;br /&gt;    ('Vermus', 'admin@musicmans.ru'),&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;MANAGERS = ADMINS&lt;br /&gt;&lt;br /&gt;TIME_ZONE = 'Europe/Moscow'&lt;br /&gt;&lt;br /&gt;LANGUAGE_CODE = 'ru-RU'&lt;br /&gt;&lt;br /&gt;#Определяем корень проекта&lt;br /&gt;PROJECT_ROOT = os.path.normpath(os.path.dirname(os.path.dirname(__file__)))&lt;br /&gt;&lt;br /&gt;#Добавляем apps в системную переменную path&lt;br /&gt;sys.path.insert(0, os.path.join(PROJECT_ROOT, 'apps'))&lt;br /&gt;&lt;br /&gt;#URL к медиа файлам&lt;br /&gt;MEDIA_URL = '/media/'&lt;br /&gt;#путь в системе к медиа файлам&lt;br /&gt;MEDIA_ROOT = os.path.join(os.path.dirname(PROJECT_ROOT), os.path.join('www', MEDIA_URL.strip('/') ))&lt;br /&gt;&lt;br /&gt;#URL к статическим файлам&lt;br /&gt;STATIC_URL = MEDIA_URL + 'static/'&lt;br /&gt;#пусть в системе к статическим файлам&lt;br /&gt;STATIC_ROOT = os.path.join(MEDIA_ROOT, 'static') &lt;br /&gt;&lt;br /&gt;ROOT_URLCONF = 'urls'&lt;br /&gt;&lt;br /&gt;#Админка нам не нужна, убираем&lt;br /&gt;#ADMIN_MEDIA_PREFIX = '/media/'&lt;br /&gt;&lt;br /&gt;TEMPLATE_DIRS = (&lt;br /&gt;    os.path.join(PROJECT_ROOT, 'templates'),&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;development.py (svn:ignore):&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- mode: python; coding: utf-8; -*-&lt;br /&gt;&lt;br /&gt;from common import *&lt;br /&gt;&lt;br /&gt;DEBUG = True&lt;br /&gt;TEMPLATE_DEBUG = DEBUG&lt;br /&gt;&lt;br /&gt;DATABASES = {&lt;br /&gt;    'default': {&lt;br /&gt;        'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.&lt;br /&gt;        'NAME': 'musicmans', # Or path to database file if using sqlite3.&lt;br /&gt;        'USER': 'user', # Not used with sqlite3.&lt;br /&gt;        'PASSWORD': 'pass', # Not used with sqlite3.&lt;br /&gt;        'HOST': 'localhost', # Set to empty string for localhost. Not used with sqlite3.&lt;br /&gt;        'PORT': '', # Set to empty string for default. Not used with sqlite3.&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;production.py:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# -*- mode: python; coding: utf-8; -*-&lt;br /&gt;&lt;br /&gt;from common import *&lt;br /&gt;&lt;br /&gt;DEBUG = False&lt;br /&gt;&lt;br /&gt;DATABASES = {&lt;br /&gt;    'default': {&lt;br /&gt;        'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.&lt;br /&gt;        'NAME': 'musicmans', # Or path to database file if using sqlite3.&lt;br /&gt;        'USER': 'user', # Not used with sqlite3.&lt;br /&gt;        'PASSWORD': 'pass', # Not used with sqlite3.&lt;br /&gt;        'HOST': 'localhost', # Set to empty string for localhost. Not used with sqlite3.&lt;br /&gt;        'PORT': '', # Set to empty string for default. Not used with sqlite3.&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Итак, единственное, что пока отличает production от develompent, переменная DEBUG и настройки базы данных (для нашего случая можно установить один пароль, и они тоже не будут отличаться, можно переместить в common.py). Остальное мы постарались сделать независимым от платформы и месторасположения проекта в системе.&lt;br /&gt;&lt;br /&gt;Отключим в eclipse  Window -&gt; Preferences =&gt; Pydev -&gt; Editor -&gt; Code Analysis unused wild import, иначе будем получать много предупреждений от Code Analysis (в связи с нашими import *).&lt;br /&gt;&lt;br /&gt;Отредактируем manage.py (в его начале мы разбираемся с settings проекта, потом настраиваем eclipse для &lt;a href="http://vermus.blogspot.com/2009/10/django-eclipse-c-pydev.html"&gt;отладки django проекта&lt;/a&gt;):&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;#!/usr/bin/env python&lt;br /&gt;import sys&lt;br /&gt;&lt;br /&gt;from django.core.management import execute_manager&lt;br /&gt;&lt;br /&gt;try:&lt;br /&gt;    import settings.development as settings&lt;br /&gt;except ImportError:&lt;br /&gt;    try:&lt;br /&gt;        import settings.production as settings&lt;br /&gt;    except:&lt;br /&gt;        sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)&lt;br /&gt;        sys.exit(1)&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    if len(sys.argv) &gt; 1:&lt;br /&gt;        command = sys.argv[1]&lt;br /&gt;    if settings.DEBUG and (command == "runserver" or command == "testserver"):&lt;br /&gt;        # Make pydev debugger works for auto reload.&lt;br /&gt;        try:&lt;br /&gt;            import pydevd&lt;br /&gt;        except ImportError:&lt;br /&gt;            sys.stderr.write("Error: " +&lt;br /&gt;                "You must add org.python.pydev.debug.pysrc to your PYTHONPATH.")&lt;br /&gt;            sys.exit(1)&lt;br /&gt;   &lt;br /&gt;        from django.utils import autoreload&lt;br /&gt;        m = autoreload.main&lt;br /&gt;        def main(main_func, args=None, kwargs=None):&lt;br /&gt;            import os&lt;br /&gt;            if os.environ.get("RUN_MAIN") == "true":&lt;br /&gt;                def pydevdDecorator(func):&lt;br /&gt;                    def wrap(*args, **kws):&lt;br /&gt;                        pydevd.settrace(suspend=False)&lt;br /&gt;                        return func(*args, **kws)&lt;br /&gt;                    return wrap&lt;br /&gt;                main_func = pydevdDecorator(main_func)&lt;br /&gt;    &lt;br /&gt;            return m(main_func, args, kwargs)&lt;br /&gt;    &lt;br /&gt;        autoreload.main = main&lt;br /&gt;    &lt;br /&gt;    execute_manager(settings)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Выполняем все действия по вышеприведенной ссылке, чтобы запустить отладку проекта. Кстати, в новом pydev появился &lt;a rel="nofollow" href="http://pydev.org/manual_adv_django.html"&gt;конфигуратор отладчика для django&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8QVvWEVUI/AAAAAAAAAFg/v4_U30Y9P_0/s1600/eclipse8.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 241px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8QVvWEVUI/AAAAAAAAAFg/v4_U30Y9P_0/s400/eclipse8.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5489624436460246338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Но он многого не дает, как я понял, единственное --no-reload прописан по умолчанию и в аргументах можно просто указать runserver.&lt;br /&gt;&lt;br /&gt;Итак, запускаем отладку:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8QH3aHJ1I/AAAAAAAAAFY/Nb4pBrrCBB8/s1600/eclipse7.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 294px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8QH3aHJ1I/AAAAAAAAAFY/Nb4pBrrCBB8/s400/eclipse7.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5489624198106523474" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;И заходим браузером по адресу http://127.0.0.1:8000/, и видим классику:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8TZydnjaI/AAAAAAAAAFo/OAG8sVlIMck/s1600/eclipse9.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 199px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8TZydnjaI/AAAAAAAAAFo/OAG8sVlIMck/s400/eclipse9.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5489627804551581090" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-6866336204970340606?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/1_15rZNWx-w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/6866336204970340606/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/07/musicmansru-django-c.html#comment-form" title="Комментарии: 7" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6866336204970340606?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6866336204970340606?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/1_15rZNWx-w/musicmansru-django-c.html" title="musicmans.ru | Как сделать сайт на Django | Cтруктура проекта" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_kR0dajk9XAw/TC8NKKcm3AI/AAAAAAAAAFQ/fF7zon12IXc/s72-c/eclipse6.png" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/07/musicmansru-django-c.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEUDRX4yeip7ImA9WxFbEE0.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-6489058185919031501</id><published>2010-06-30T08:30:00.024+04:00</published><updated>2010-07-01T21:44:34.092+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-01T21:44:34.092+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse" /><category scheme="http://www.blogger.com/atom/ns#" term="subversion" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse Marketplace" /><title>musicmans.ru | Как сделать сайт на Django | Настраиваем Eclipse</title><content type="html">&lt;span style="font-weight:bold;"&gt;Подготовка Eclipse&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1. Качаем &lt;a rel="nofollow" href="http://www.eclipse.org/downloads/"&gt;Eclipse&lt;/a&gt; на машину разработчика (windows, linux).&lt;br /&gt;&lt;br /&gt;2. В Eclipse - Help-&gt;Install New Software, выбираем из выпадающего списка Helios - http://download.eclipse.org/releases/helios, выбираем:&lt;br /&gt;General Purpose Tools -  Marketplace Client 1.0.0.v20100611-0430&lt;br /&gt;Это новый удобный клиент репозитория приложений для Eclipse. Следует учесть, что в этом репозитории находятся и платные приложения, так что, проверяйте информацию нажатием на кнопочку "i". Почитать &lt;a rel="nofollow" href="http://samolisov.blogspot.com/2010/06/eclipse-marketplace.html"&gt;обзор&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;3. Заходим в Help-Eclipse Marketplace, устанавливаем pydev (поддержка python).&lt;br /&gt;4. Заходим в Help-Eclipse Marketplace, устанавливаем mylyn.&lt;br /&gt;&lt;br /&gt;Почитать про Mylyn &lt;a rel="nofollow" href="http://www.ibm.com/developerworks/ru/library/j-mylyn1/index.html"&gt;здесь&lt;/a&gt; и &lt;a rel="nofollow" href="http://www.ibm.com/developerworks/ru/library/j-mylyn2/"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;5. &lt;a href="http://vermus.blogspot.com/2010/06/redmine-eclipse-mylyn.html"&gt;Обеспечиваем синхронизацию задач&lt;/a&gt; mylyn в eclipse и redmine.&lt;br /&gt;&lt;br /&gt;6. Заходим в Help-Eclipse Marketplace, устанавливаем Subversive (считайте меня консерватором) (после перезапуска устанавливаем коннектор SVN Kit последней версии).&lt;br /&gt;&lt;br /&gt;7. Устанавливаем Aptana (Aptana Studio 3.0.0 Beta, 2я не установилась на Helios) (редактирование шаблонов html, css, может быть и js).&lt;br /&gt;Update Site: http://download.aptana.com/studio3/plugin/install&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Subversion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Будем использовать через svn+ssh://. Устанавливаем subversion и ssh на сервер для управления кодом.&lt;br /&gt;#apt-get install subversion ssh&lt;br /&gt;&lt;br /&gt;Создаем репозиторий:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;root@codeserver:~# cd /&lt;br /&gt;root@codeserver:/# mkdir repos&lt;br /&gt;root@codeserver:/# cd repos/&lt;br /&gt;root@codeserver:/repos# svnadmin create musicmans&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Добавляем пользователя и устанавливаем владельца:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#useradd -m svnuser&lt;br /&gt;#chown -R svnuser:svnuser /repos/musicmans/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Проверяем, что пользователь может логиниться по ssh.&lt;br /&gt;Далее, в eclise на машине разработчика переходим в перспективу SVN Repository Exploring, в Repository Location, и добавляем New-Repository Location.&lt;br /&gt;&lt;br /&gt;Указываем URL: svn+ssh://codeserver/repos/musicmans/, указываем логин, пароль, включаем Enable Struсture Detection, ну и смотрим другие параметры.&lt;br /&gt;&lt;br /&gt;После успешного подключения создаем структуру репозитория в New -&gt; Project Structure, Monolithic layout.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Создание проекта&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Далее, нам надо создать каркас django проекта и выложить его в trunk.&lt;br /&gt;&lt;br /&gt;Как установить python (2.6), django (1.2), рассказывать не буду, мануалов масса.&lt;br /&gt;&lt;br /&gt;В новом pydev появилась замечательная вещь, как создание Django проекта из мастера создания проектов. &lt;br /&gt;&lt;br /&gt;Переходим в перспективу Pydev, в контекстном меню package explorer указываем New - Project:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TCsQmNDO5XI/AAAAAAAAAEA/XkjFax26TLE/s1600/new+project.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 393px; height: 400px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TCsQmNDO5XI/AAAAAAAAAEA/XkjFax26TLE/s400/new+project.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5488498819405374834" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Далее, введем название проекта, директорию, а также необходимо настроить интерпретатор python, нажав на кнопку autoconfig:&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TCsb6LKSFCI/AAAAAAAAAEI/DoqDpyrBoPY/s1600/python.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 327px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TCsb6LKSFCI/AAAAAAAAAEI/DoqDpyrBoPY/s400/python.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5488511257123361826" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Выбираем версию django. Выбираем Database Engine. От обещанного sqlite3 я &lt;a rel="nofollow" href="http://seanhayes.name/2010/01/09/cross-database-incompatibility/"&gt;отказался&lt;/a&gt;, решив использовать postgresql как в разработке, так и на рабочем сервере. Считаем что, postgresql установлен на машине разработчика (мануалов масса на любую систему).&lt;br /&gt;&lt;br /&gt;После окончания работы мастера наблюдаем примерно такую картину:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TCsxCsvBfGI/AAAAAAAAAEY/9Prozt4aU20/s1600/project+tree.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 210px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TCsxCsvBfGI/AAAAAAAAAEY/9Prozt4aU20/s400/project+tree.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5488534493318970466" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Еще в pydev появилось вот такое меню для проекта:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TCsx5nWVhbI/AAAAAAAAAEg/GHNDm2-H6rQ/s1600/django+menu.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 381px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TCsx5nWVhbI/AAAAAAAAAEg/GHNDm2-H6rQ/s400/django+menu.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5488535436766053810" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Поменяем кодировку проекта на UTF-8, если необходимо (пункт меню Properties).&lt;br /&gt;&lt;br /&gt;Далее, расшарим проект в репозиторий.&lt;br /&gt;&lt;br /&gt;Subversive замечательно справляется со структурой trunk, branches, tags.&lt;br /&gt;&lt;br /&gt;Сделаем так, что у нас проект будет находится в trunk под именем backend, то есть trunk/backend, ведь в будущем у нас может появиться и другие проекты, которые надо будет разместить в транке, например проект gwt.&lt;br /&gt;&lt;br /&gt;В контекстном меню проекта указываем -&gt; Team - Share Project, далее несколько окон по умолчанию, в окне указания расположения проекта: &lt;br /&gt;use specified name: backend&lt;br /&gt;Нажимаем далее, в комментарии видим что-то похожее:&lt;br /&gt;Share project "musicmans" into "svn+ssh://codeserver/repos/musicmans"&lt;br /&gt;И коммитим изменения, в окне с коммитом необходимо добавить *.setting, *.pydevproject и *.project в svn:ignore (контекстное меню в списке ресурсов).&lt;br /&gt;Перейдем в SVN Repository Exploring, в Repository Location нажмем на кнопку обновить:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_kR0dajk9XAw/TCxB_LvlXBI/AAAAAAAAAE4/g87tzBfmzrY/s1600/musicmans+repos.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 176px;" src="http://4.bp.blogspot.com/_kR0dajk9XAw/TCxB_LvlXBI/AAAAAAAAAE4/g87tzBfmzrY/s400/musicmans+repos.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5488834599597726738" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Предварительную настройку директорий и проекта выполним в транке, а конкретные задачи создав ветку и переключившись в нее. Смотрите, как это просто делается в subversive:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TCxDEOVvlkI/AAAAAAAAAFA/4UtHdovqHig/s1600/team+branch.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 386px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TCxDEOVvlkI/AAAAAAAAAFA/4UtHdovqHig/s400/team+branch.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5488835785705625154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TCxDhUwHkNI/AAAAAAAAAFI/cxUParDIKHc/s1600/create+branch.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 321px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TCxDhUwHkNI/AAAAAAAAAFI/cxUParDIKHc/s400/create+branch.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5488836285643067602" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Создаем и сразу переключаемся в ветку. Пока делать этого не будем. На этом настройка Eclipse пока закончена.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-6489058185919031501?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/9wBmiQr5BuQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/6489058185919031501/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/06/musicmansru-django-eclipse.html#comment-form" title="Комментарии: 4" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6489058185919031501?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6489058185919031501?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/9wBmiQr5BuQ/musicmansru-django-eclipse.html" title="musicmans.ru | Как сделать сайт на Django | Настраиваем Eclipse" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_kR0dajk9XAw/TCsQmNDO5XI/AAAAAAAAAEA/XkjFax26TLE/s72-c/new+project.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/06/musicmansru-django-eclipse.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUMSXw4fip7ImA9WxFUGE8.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-3532205591034185138</id><published>2010-06-29T15:34:00.004+04:00</published><updated>2010-06-29T20:18:08.236+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T20:18:08.236+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse" /><category scheme="http://www.blogger.com/atom/ns#" term="subversion" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Схема работы</title><content type="html">Ну что же. Инструментарий у нас уже &lt;a href="http://vermus.blogspot.com/2010/06/musicmansru-django.html"&gt;готов&lt;/a&gt;. Вникаем в общую схему работы.&lt;br /&gt;&lt;br /&gt;Схема такая: &lt;br /&gt;1. Разрабатываем локально, используя &lt;a href="http://vermus.blogspot.com/2009/10/django-eclipse-c-pydev.html"&gt;отладку Django в Eclipse&lt;/a&gt; (наверное будем использовать SQLite при разработке, чтобы было проще, плюс файл базы можно будет хранить в svn, для одного разработчика, я думаю, это нормально).&lt;br /&gt;2. Subversion. &lt;a rel="nofollow" href="http://svnbook.red-bean.com/nightly/ru/svn.branchmerge.html"&gt;Общепринятая&lt;/a&gt; структура svn проекта:&lt;br /&gt;&lt;br /&gt;branches&lt;br /&gt;tags&lt;br /&gt;trunk&lt;br /&gt;&lt;br /&gt;Как их сделать расскажу позже. Сейчас остановимся на теории. &lt;br /&gt;Итак, trunk - рабочая копия проекта, trunk должен &lt;span style="font-weight:bold;"&gt;работать&lt;/span&gt;, не забываем про это.&lt;br /&gt;Если trunk должен работать, то как коммитить недоделанные задачи? Для этого есть branches - ветки. Когда перед нами встает задача по модернизации или исправлению ошибок, создаем ветку (копию) из проекта trunk в branches.&lt;br /&gt;После внесения изменений (и соответственных между ними коммитов/апдейтов ветки), выполняем слияние ветки с trunk.&lt;br /&gt;tags - метки, это копии проекта в которые нельзя коммитить, обычно там хранятся копии релизов.&lt;br /&gt;&lt;br /&gt;Если ничего не понятно, читаем &lt;a rel="nofollow" href="http://svnbook.red-bean.com/nightly/ru/index.html"&gt;книгу&lt;/a&gt; или ждем продолжения (рассмотрим вопрос, как это делается в Eclipse Subversive).&lt;br /&gt;&lt;br /&gt;3. Используя &lt;a rel="nofollow" href="http://fabfile.org"&gt;Fabric&lt;/a&gt;, мы будем из tag/current с сервера с Subversion выкладывать релиз на сервер с сайтом, а также обновлять базу (с помощью &lt;a rel="nofollow" href="http://south.aeracode.org/"&gt;South&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-3532205591034185138?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/Tr37c--0xeQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/3532205591034185138/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/06/musicmansru-django_29.html#comment-form" title="Комментарии: 5" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3532205591034185138?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3532205591034185138?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/Tr37c--0xeQ/musicmansru-django_29.html" title="musicmans.ru | Как сделать сайт на Django | Схема работы" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>5</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/06/musicmansru-django_29.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUBSXw7fCp7ImA9WxFUGE8.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-1361378562670741881</id><published>2010-06-29T10:22:00.013+04:00</published><updated>2010-06-29T20:17:38.204+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T20:17:38.204+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gwt" /><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="сайт" /><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse" /><category scheme="http://www.blogger.com/atom/ns#" term="subversion" /><category scheme="http://www.blogger.com/atom/ns#" term="Redmine" /><category scheme="http://www.blogger.com/atom/ns#" term="musicmans.ru" /><title>musicmans.ru | Как сделать сайт на Django | Начало</title><content type="html">Подумал я тут на досуге и решил сделать &lt;a href="http://musicmans.ru"&gt;сайт для меломанов&lt;/a&gt;, так как сам являюсь таким же. И не просто сделать, а рассказать об этапах работы, акцентируя внимание на не очевидных вещах. Это не профессиональное руководство, а скорее создание нормального сайта для любителей (то есть не брать обычный движок и неумело приспосабливать его к желаниям, а желание воплощать в реализацию). &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Технологии.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Серверная сторона - django. Конечно будем использовать сторонние django приложения, и не будем писать тесты, тестировать будут пользователи. :) Элементарные вещи о django рассматриваться не будут, для этого есть &lt;a href="http://djbook.ru/" rel="nofollow"&gt;django book&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Клиентская сторона - наверное &lt;a rel="nofollow" href="http://code.google.com/intl/ru/webtoolkit/"&gt;gwt&lt;/a&gt;. Пока не определился, но думаю внедрим.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Инструменты и техническая сторона&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Техническая сторона - &lt;a rel="nofollow" href="http://www.google.ru/search?hl=ru&amp;newwindow=1&amp;client=firefox&amp;hs=0YW&amp;rls=org.mozilla%3Aru%3Aofficial&amp;q=%D0%B4%D0%BE%D0%BC%D0%B5%D0%BD+vds&amp;aq=f&amp;aqi=&amp;aql=&amp;oq=&amp;gs_rfai="&gt;покупайте&lt;/a&gt; &lt;a rel="nofollow" href="http://ru.wikipedia.org/wiki/%D0%94%D0%BE%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5_%D0%B8%D0%BC%D1%8F"&gt;домен&lt;/a&gt; и &lt;a rel="nofollow" href="http://ru.wikipedia.org/wiki/VPS"&gt;vds&lt;/a&gt;. VDS можете купить два или сделать &lt;a href="http://vermus.blogspot.com/2009/02/blog-post.html"&gt;собственный сервер, например, дома&lt;/a&gt;, один для сайта, другой для хранения и управления кодом.&lt;br /&gt;Собственно, предыдущие посты как раз были подготовкой к работе.&lt;br /&gt;Система контроля версий - &lt;a href="http://vermus.blogspot.com/2009/02/subversion-debian-lenny.html"&gt;Subversion&lt;/a&gt;.&lt;br /&gt;Управление проектом и баг-трекер - &lt;a href="http://vermus.blogspot.com/2010/06/redmine-lighttpd-debian-squeeze.html"&gt;Redmine&lt;/a&gt;.&lt;br /&gt;Среда разработки &lt;a href="http://vermus.blogspot.com/2010/06/redmine-eclipse-mylyn.html"&gt;Eclipse&lt;/a&gt; (кстати вышел &lt;a rel="nofollow" href="http://www.eclipse.org/helios/"&gt;Helios 3.6&lt;/a&gt;) с pydev, &lt;a rel="nofollow" href="http://www.eclipse.org/subversive/"&gt;Subversive&lt;/a&gt; + расширения по вкусу плюс второй экземпляр Eclipse для gwt.&lt;br /&gt;&lt;br /&gt;Ну вот вроде бы и все. Если не все, допишу позже.&lt;br /&gt;Сроки. Ориентируюсь на полгода до более-менее приличного сайта (потому что есть еще основная работа, к сожалению :) ). &lt;br /&gt;Все этапы разработки сразу будут выкладываться на сайт &lt;a href="http://musicmans.ru"&gt;http://musicmans.ru&lt;/a&gt; . &lt;br /&gt;&lt;br /&gt;Следить за постами о разработке можно по тегу &lt;a rel="nofollow" href="http://vermus.blogspot.com/search/label/musicmans.ru"&gt;musicmans.ru&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-1361378562670741881?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/DfUDFQOIbRE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/1361378562670741881/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/06/musicmansru-django.html#comment-form" title="Комментарии: 8" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1361378562670741881?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1361378562670741881?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/DfUDFQOIbRE/musicmansru-django.html" title="musicmans.ru | Как сделать сайт на Django | Начало" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>8</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/06/musicmansru-django.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEYFSHc4eCp7ImA9WxFUF0Q.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-1460770289167600110</id><published>2010-06-27T17:13:00.012+04:00</published><updated>2010-06-29T10:15:19.930+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T10:15:19.930+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse" /><category scheme="http://www.blogger.com/atom/ns#" term="Redmine" /><category scheme="http://www.blogger.com/atom/ns#" term="mylyn" /><title>Дружим redmine и eclipse: mylyn</title><content type="html">Продолжим &lt;a href="http://vermus.blogspot.com/2010/06/redmine-lighttpd-debian-squeeze.html"&gt;заниматься redmine'ом&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Mylyn («майлин») — подсистема Eclipse управления заданиями.&lt;br /&gt;&lt;br /&gt;Mylyn динамически подстраивает интерфейс Eclipse, оставляет только те элементы в дереве ресурсов, которые соответствуют текущей задаче. Mylyn логически продолжает такие «диалоги», как «Go Into», «Open Associated Perspective?»&lt;br /&gt;&lt;br /&gt;Ставим в эклипс поддежку redmine для mylyn:&lt;br /&gt;&lt;br /&gt;Eclipse redmine mylyn update site: http://redmin-mylyncon.sourceforge.net/update-site/nightly/&lt;br /&gt;&lt;br /&gt;Скачиваем плагин для сервера &lt;a rel="nofollow" href="http://sourceforge.net/projects/redmin-mylyncon/files/Redmine%20Plugin%20%28WS-API%29/WS-API%20Redmine%200.8%20and%200.9/"&gt;отсюда&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Закачиваем плагин в директорию &lt;REDMINE_INSTALLATION_DIR&gt;/vendor/plugins/ .&lt;br /&gt;&lt;br /&gt;Перезапускам lighttpd:&lt;br /&gt;# /etc/init.d/lighttpd restart&lt;br /&gt;&lt;br /&gt;Заходим по адресу https://srv/redmine/mylyn/version , должны увидеть нечто такое:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&lt;version&gt;&lt;br /&gt;&lt;plugin major="2" tiny="4" minor="6"&gt;2.6.4.stable&lt;/plugin&gt;&lt;br /&gt;&lt;redmine&gt;0.9.4.stable&lt;/redmine&gt;&lt;br /&gt;&lt;rails&gt;2.3.5&lt;/rails&gt;&lt;br /&gt;&lt;/version&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;а также сюда https://srv/redmine/admin/plugins&lt;br /&gt;&lt;br /&gt;Другие варианты установки &lt;a rel="nofollow" href="http://sourceforge.net/apps/mediawiki/redmin-mylyncon/index.php?title=Installation#Redmine-Installation_via_download"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Настройка клиента:&lt;br /&gt;&lt;br /&gt;Далее, открываем перспективу planning в eclipse.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TCdSOJS9MjI/AAAAAAAAADU/p5ybFxMAXGI/s1600/eclipse1.png" rel="nofollow"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 160px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TCdSOJS9MjI/AAAAAAAAADU/p5ybFxMAXGI/s400/eclipse1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5487445073941312050" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Добавляем репозиторий:&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_kR0dajk9XAw/TCdTP56p-WI/AAAAAAAAADc/-2atbipS1Qk/s1600/eclipse2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 180px;" src="http://2.bp.blogspot.com/_kR0dajk9XAw/TCdTP56p-WI/AAAAAAAAADc/-2atbipS1Qk/s400/eclipse2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5487446203684223330" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TCdTea74eHI/AAAAAAAAADk/hS1X4pEWhbo/s1600/eclipse3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 251px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TCdTea74eHI/AAAAAAAAADk/hS1X4pEWhbo/s400/eclipse3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5487446453065906290" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Настраиваем примерно следующим образом и нажимаем validate settings (что приятно не возникло проблем с http auth и https):&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_kR0dajk9XAw/TCdUqs9P0GI/AAAAAAAAADs/GsqoUCEypn4/s1600/eclipse4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 332px;" src="http://3.bp.blogspot.com/_kR0dajk9XAw/TCdUqs9P0GI/AAAAAAAAADs/GsqoUCEypn4/s400/eclipse4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5487447763573526626" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Далее создаем запрос задач (спросит автоматом после установки соединения) из redmine которые мы хотим видеть.&lt;br /&gt;&lt;br /&gt;Далее пробуем создать задачу в eclipse: правой кнопкой на task list-&gt;New-&gt;Task...&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_kR0dajk9XAw/TCdWeZqGV_I/AAAAAAAAAD0/Yb-YFWoNSJE/s1600/eclipse5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 314px; height: 400px;" src="http://1.bp.blogspot.com/_kR0dajk9XAw/TCdWeZqGV_I/AAAAAAAAAD0/Yb-YFWoNSJE/s400/eclipse5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5487449751257765874" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-1460770289167600110?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/j9ytX1PGvvo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/1460770289167600110/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/06/redmine-eclipse-mylyn.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1460770289167600110?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1460770289167600110?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/j9ytX1PGvvo/redmine-eclipse-mylyn.html" title="Дружим redmine и eclipse: mylyn" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_kR0dajk9XAw/TCdSOJS9MjI/AAAAAAAAADU/p5ybFxMAXGI/s72-c/eclipse1.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/06/redmine-eclipse-mylyn.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEYDSHsyeCp7ImA9WxFUF0Q.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-1819618943293345947</id><published>2010-06-25T15:51:00.011+04:00</published><updated>2010-06-29T10:16:19.590+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T10:16:19.590+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mysql" /><category scheme="http://www.blogger.com/atom/ns#" term="debian" /><category scheme="http://www.blogger.com/atom/ns#" term="lighttpd" /><category scheme="http://www.blogger.com/atom/ns#" term="Redmine" /><title>Установка redmine на lighttpd в debian squeeze</title><content type="html">Начинаем как &lt;a href="http://blog.josefsson.org/2008/10/17/redmine-on-debian-lenny-using-lighttpd/"&gt;здесь&lt;/a&gt;:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#apt-get install mysql-server rails lighttpd&lt;br /&gt;#apt-get install librmagick-ruby&lt;br /&gt;#apt-get install libmysql-ruby &lt;br /&gt;#apt-get install subversion git-core &lt;br /&gt;#apt-get install libfcgi-ruby1.8 &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Создаем пользователя и базу:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;# mysql -p&lt;br /&gt;Enter password: &lt;br /&gt;Welcome to the MySQL monitor.  Commands end with ; or \g.&lt;br /&gt;Your MySQL connection id is 63&lt;br /&gt;Server version: 5.1.47-1 (Debian)&lt;br /&gt;&lt;br /&gt;Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.&lt;br /&gt;This software comes with ABSOLUTELY NO WARRANTY. This is free software,&lt;br /&gt;and you are welcome to modify and redistribute it under the GPL v2 license&lt;br /&gt;&lt;br /&gt;Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.&lt;br /&gt;&lt;br /&gt;mysql&gt; create database redmine character set utf8;&lt;br /&gt;Query OK, 1 row affected (0.08 sec)&lt;br /&gt;&lt;br /&gt;mysql&gt; create user 'redmine'@'localhost' identified by 'password';&lt;br /&gt;Query OK, 0 rows affected (0.47 sec)&lt;br /&gt;&lt;br /&gt;mysql&gt; grant all privileges on redmine.* to 'redmine'@'localhost';&lt;br /&gt;Query OK, 0 rows affected (0.01 sec)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Добавляем системного пользователя redmine:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#adduser --disabled-password redmine&lt;br /&gt;#su redmine&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Качаем релиз wget'ом &lt;a rel="nofollow" href="http://www.redmine.org/wiki/redmine/Download"&gt;redmine&lt;/a&gt; (я работал с версией redmine-0.9.4.tar.gz).&lt;br /&gt;&lt;br /&gt;Распаковываем содержимое в ~/redmine (tar zxf)&lt;br /&gt;&lt;br /&gt;Создаем файл redmine*/config/database.yml (можно скопировать из примера):&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;production:&lt;br /&gt;  adapter: mysql&lt;br /&gt;  database: redmine&lt;br /&gt;  host: localhost&lt;br /&gt;  username: rbuser&lt;br /&gt;  password: password&lt;br /&gt;  encoding: utf8&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Генерируем секретный ключ сессий.&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;~/redmine$ rake generate_session_store&lt;br /&gt;(in /home/redmine/redmine)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Заполняем базу данных.&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;~/redmine$ RAILS_ENV=production rake db:migrate&lt;br /&gt;(in /home/redmine/redmine)&lt;br /&gt;rake aborted!&lt;br /&gt;Could not find RubyGem rack (~&gt; 1.0.1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Я получил такую ошибку. &lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#gem install rack -v 1.0.1&lt;br /&gt;Successfully installed rack-1.0.1&lt;br /&gt;1 gem installed&lt;br /&gt;Installing ri documentation for rack-1.0.1...&lt;br /&gt;Installing RDoc documentation for rack-1.0.1...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Запускаем повторно из под пользователя redmine:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#su redmine&lt;br /&gt;redmine@srv:/root$ cd ~/redmine/&lt;br /&gt;redmine@srv:~/redmine$ RAILS_ENV=production rake db:migrate&lt;br /&gt;(in /home/redmine/redmine)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Должно все успешно заполниться.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Настройка lighttpd.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Создаем файл /etc/lighttpd/conf-available/20-redmine.conf :&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;/etc/lighttpd/conf-available# cat 20-redmine.conf &lt;br /&gt;server.modules   += ( "mod_fastcgi" )&lt;br /&gt;&lt;br /&gt;$HTTP["url"] =~ "^/redmine/" {&lt;br /&gt;  alias.url = ("/redmine" =&gt; "/home/redmine/redmine/public")&lt;br /&gt;      server.document-root = "/home/redmine/redmine/public/" &lt;br /&gt;      server.error-handler-404 = "/redmine/dispatch.fcgi" &lt;br /&gt;      index-file.names         = ("dispatch.fcgi")&lt;br /&gt;      fastcgi.server           += (&lt;br /&gt;           ".fcgi" =&gt; (&lt;br /&gt;           "localhost" =&gt; (&lt;br /&gt;           "min-procs"       =&gt; 1,&lt;br /&gt;           "max-procs"       =&gt; 2,&lt;br /&gt;           "check-local"    =&gt; "disable",&lt;br /&gt;           "socket"          =&gt; "/home/redmine/redmine/tmp/sockets/redmine_fcgi.socket"  + var.PID,&lt;br /&gt;           "bin-path"        =&gt; "/home/redmine/redmine/public/dispatch.fcgi",&lt;br /&gt;           "bin-environment" =&gt; (&lt;br /&gt;                            "RAILS_ENV"               =&gt; "production",&lt;br /&gt;                            "RAILS_RELATIVE_URL_ROOT" =&gt; "/redmine",&lt;br /&gt;                            "RAILS_ROOT" =&gt; "/home/redmine/redmine"&lt;br /&gt;                               )&lt;br /&gt;                              )&lt;br /&gt;                    )&lt;br /&gt;                  )&lt;br /&gt;                  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Включаем конфигурацию:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#lighttpd-enable-mod redmine &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Создаем файл:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;srv:/home/redmine/redmine/public# cp dispatch.fcgi.example dispatch.fcgi&lt;br /&gt;srv:/home/redmine/redmine/public# chmod +x dispatch.fcgi &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Даем права пользователю www-data (запускает lighttpd)&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;srv:/home/redmine#chown -R www-data:www-data redmine&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Даем права на директории&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;srv:/home/redmine#chmod 755 logs tmp files&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Перезапускаем конфигурации lighttpd:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#/etc/init.d/lighttpd force-reload&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Заходим: http://srv/redmine/&lt;br /&gt;&lt;br /&gt;По умолчанию, логин admin, пароль admin.&lt;br /&gt;&lt;br /&gt;Если что-то пошло не так, смотрим логи:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#tail -F /var/log/lighttpd/access.log /var/log/lighttpd/error.log /home/redmine/redmine/log/production.log&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-1819618943293345947?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/EC47pmzO0Fo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/1819618943293345947/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/06/redmine-lighttpd-debian-squeeze.html#comment-form" title="Комментарии: 7" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1819618943293345947?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1819618943293345947?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/EC47pmzO0Fo/redmine-lighttpd-debian-squeeze.html" title="Установка redmine на lighttpd в debian squeeze" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>7</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/06/redmine-lighttpd-debian-squeeze.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkUDR344cCp7ImA9WxFVGEU.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-5294992856126461419</id><published>2010-06-18T19:46:00.008+04:00</published><updated>2010-06-18T20:57:56.038+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-18T20:57:56.038+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="kvm" /><category scheme="http://www.blogger.com/atom/ns#" term="apcupsd" /><title>Продолжение про kvm</title><content type="html">Продолжим записи про &lt;a href="http://vermus.blogspot.com/2010/06/kvm-debian-squeeze.html"&gt;kvm&lt;/a&gt;.&lt;br /&gt;Чтобы виртуальная система загружалась автоматически при старте основной системы, надо выполнить:&lt;br /&gt;&lt;br /&gt;#virsh autostart name_domain&lt;br /&gt;&lt;br /&gt;Чтобы корректно выключить (эмулируется нажатие на клавишу питания, что для современных дебианов с поддержкой acpi обеспечивает корректное завершение работы) виртуальную машину выполните:&lt;br /&gt;&lt;br /&gt;#virsh shutdown name_domain&lt;br /&gt;&lt;br /&gt;Ну и чтобы запустить, выполните:&lt;br /&gt;&lt;br /&gt;#virsh start name_domain&lt;br /&gt;&lt;br /&gt;Теперь про &lt;a href="http://exain.wordpress.com/2009/05/22/auto-shutdown-kvm-virtual-machines-on-system-shutdown/"&gt;выключение/перезагрузку&lt;/a&gt; основной системы и соответственное выключение виртуальных машин (может очень полезным при выключении основной системы с помощью apcupsd (управление питанием в зависимости от состояния бесперебойника APC).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Создаем скрипт /etc/init.d/vmshutdown&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;# Original Author :&lt;br /&gt;#    Joern http://www.linux-kvm.com/content/stop-script-running-vms-using-virsh&lt;br /&gt;#&lt;br /&gt;# Modified by : Vivek Kapoor http://exain.com&lt;br /&gt;# Date: 22 May 2009&lt;br /&gt;&lt;br /&gt;# Parameters you can modify :: START&lt;br /&gt;&lt;br /&gt;TIMEOUT=90&lt;br /&gt;LISTFILE=/root/runvm.lst&lt;br /&gt;LOGFILE=/var/log/kvmshutdown.log&lt;br /&gt;# Parameters you can modify :: STOP&lt;br /&gt;&lt;br /&gt;PS=/bin/ps&lt;br /&gt;SSH=/usr/bin/ssh&lt;br /&gt;GREP=/bin/grep&lt;br /&gt;CUT=/usr/bin/cut&lt;br /&gt;VIRSH=/usr/bin/virsh&lt;br /&gt;TR=/usr/bin/tr&lt;br /&gt;CAT=/bin/cat&lt;br /&gt;DATE=/bin/date&lt;br /&gt;&lt;br /&gt;# Function to shutdown the virtual machine&lt;br /&gt;kvmshutdown () {&lt;br /&gt;COUNT=0&lt;br /&gt;PID=$($PS ax|$GREP $1|$GREP kvm|$CUT -c 1-6)&lt;br /&gt;&lt;br /&gt;echo kvmshutdown \: Shutting down $1 with pid $PID&lt;br /&gt;&lt;br /&gt;#$VIRSH shutdown $1&lt;br /&gt;$($SSH root@$1 halt)&lt;br /&gt;&lt;br /&gt;while [ "$COUNT" -lt "$TIMEOUT" ]&lt;br /&gt;do&lt;br /&gt; $PS --pid $PID&lt;br /&gt; if [ "$?" -eq "1" ]&lt;br /&gt; then&lt;br /&gt; return 0&lt;br /&gt; else&lt;br /&gt; sleep 5&lt;br /&gt; COUNT=$(($COUNT+5))&lt;br /&gt; fi&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;echo kvmshutdown \: Timeout happened. Destroying VM $1&lt;br /&gt;&lt;br /&gt;$VIRSH destroy $1&lt;br /&gt;&lt;br /&gt;return 1&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# The program begins here&lt;br /&gt;&lt;br /&gt;$VIRSH list 2&gt;/dev/null|$GREP running|$TR -s \ |$CUT -f3 -d\  &gt; $LISTFILE&lt;br /&gt;&lt;br /&gt;VMN=`$CAT $LISTFILE`&lt;br /&gt;&lt;br /&gt;for vm in $VMN&lt;br /&gt;do&lt;br /&gt; echo "$vm" is running&lt;br /&gt; kvmshutdown "$vm"&lt;br /&gt; if [ "$?" -eq "0" ]&lt;br /&gt; then&lt;br /&gt; echo VM "$vm" normally shutdown&lt;br /&gt; echo `$DATE +%Y-%m-%d\ %H:%M:%S` VM $vm normally shutdown &gt;&gt; $LOGFILE&lt;br /&gt; else&lt;br /&gt; echo VM "$vm" destroyed !&lt;br /&gt; echo `$DATE +%Y-%m-%d\ %H:%M:%S` VM $vm destroyed &gt;&gt; $LOGFILE&lt;br /&gt; fi;&lt;br /&gt;done&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Далее, права запуска:&lt;br /&gt;#chmod 755 vmshutdown&lt;br /&gt;&lt;br /&gt;Проверяем выключение по ssh (как в скрипте, есть вариант через virsh - пробуйте), не забыв прописать хост в /etc/hosts.&lt;br /&gt;&lt;br /&gt;server:/etc/init.d# ./vmshutdown stop&lt;br /&gt;&lt;br /&gt;У меня спросил разрешения на отпечаток ключа RSA, после пароль рута, и только тогда выключился. Наверное метод через ssh требует беспарольного доверенного соединение, поэтому вернемся к virsh (закомментируем $($SSH root@$1 halt) и раскомментируем $VIRSH shutdown $1).&lt;br /&gt;&lt;br /&gt;Пробуем:&lt;br /&gt;&lt;br /&gt;server:/etc/init.d# ./vmshutdown stop&lt;br /&gt;&lt;br /&gt;У меня все получилось - скрипт выключил все виртуальные машины. Остается внедрить его в rc.d.&lt;br /&gt;&lt;br /&gt;Смотрим на уровень запуска самого kwm на основной системе.&lt;br /&gt;&lt;br /&gt;/etc/rc0.d# ls | grep kvm&lt;br /&gt;K20qemu-kvm&lt;br /&gt;&lt;br /&gt;Посему выставляем 18 уровень для завершения виртуальных машин.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#ln -s /etc/init.d/vmshutdown /etc/rc0.d/K18vmshutdown&lt;br /&gt;#ln -s /etc/init.d/vmshutdown /etc/rc6.d/K18vmshutdown&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Теперь подключаемся по ssh к виртуальным машинам и к основному хосту. На основном выполняем:&lt;br /&gt;#poweroff&lt;br /&gt;или:&lt;br /&gt;#reboot&lt;br /&gt;и наблюдаем за виртуальными машинами.&lt;br /&gt;&lt;br /&gt;Они должны выключиться раньше основной.&lt;br /&gt;&lt;br /&gt;Ну а выключение с помощью apcupsd или позже или не буду описывать, ибо мануалов много, посмотрим.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-5294992856126461419?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/6-Hq00nVKpE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/5294992856126461419/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/06/kvm.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/5294992856126461419?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/5294992856126461419?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/6-Hq00nVKpE/kvm.html" title="Продолжение про kvm" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/06/kvm.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYERXY7eyp7ImA9WxFbFE8.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-6196210143914213824</id><published>2010-06-13T11:23:00.008+04:00</published><updated>2010-07-06T16:41:44.803+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-06T16:41:44.803+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="виртуализация" /><category scheme="http://www.blogger.com/atom/ns#" term="Vinagre" /><category scheme="http://www.blogger.com/atom/ns#" term="squeeze" /><category scheme="http://www.blogger.com/atom/ns#" term="vnc" /><category scheme="http://www.blogger.com/atom/ns#" term="debian" /><category scheme="http://www.blogger.com/atom/ns#" term="kvm" /><title>Установка kvm на Debian Squeeze</title><content type="html">Займемся виртуализацией, с аппаратной поддержкой процессора.&lt;br /&gt;&lt;br /&gt;&lt;a rel="nofollow" href="http://ru.wikipedia.org/wiki/Kernel-based_Virtual_Machine"&gt;KVM&lt;/a&gt; (или Kernel-based Virtual Machine) — это программное решение, обеспечивающее виртуализацию в среде Linux на платформе x86, которая поддерживает аппаратную виртуализацию на базе Intel VT (англ.) (Virtualization Technology) либо AMD SVM (Secure Virtual Machine).&lt;br /&gt;&lt;br /&gt;Добавлю, без аппаратной поддержки процессором она, как я понял, работать не будет.&lt;br /&gt;&lt;br /&gt;Ставим основную систему. Для создания загрузочной флешки используем уже установленный линукс.&lt;br /&gt;У меня будущий серверный процессор AMD, а также потому что squeeze еще unstable, качаем образ &lt;a rel="nofollow" href="http://cdimage.debian.org/cdimage/daily-builds/daily/arch-latest/amd64/iso-cd/"&gt;отсюда&lt;/a&gt;. Я скачал &lt;a rel="nofollow" href="http://cdimage.debian.org/cdimage/daily-builds/daily/arch-latest/amd64/iso-cd/debian-testing-amd64-netinst.iso"&gt;debian-testing-amd64-netinst.iso&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Основную систему будем ставить с флешки. Качаем файл boot.img.gz &lt;a rel="nofollow" href="http://http.us.debian.org/debian/dists/squeeze/main/installer-amd64/current/images/hd-media/boot.img.gz"&gt;отсюда&lt;/a&gt;. Далее распаковываем образ на флешку:&lt;br /&gt; &lt;br /&gt;#zcat boot.img.gz &gt; /dev/sdX&lt;br /&gt;&lt;br /&gt;где /dev/sdX - ваша флешка.&lt;br /&gt;&lt;br /&gt;Далее вытащим и вставим флешку обратно. Скопируем в корень на автоматически примонтированную флешку файл debian-testing-amd64-netinst.iso. Все, отмонтируем флешку, вставляем в наш сервер, загружаемся и устанавливаем систему.&lt;br /&gt;&lt;br /&gt;Создадим две директории - iso для установочных образов, vms для образов виртуальных машин. Копируем с флешки образ в iso.&lt;br /&gt;&lt;br /&gt;Устанавливаем kvm:&lt;br /&gt;&lt;br /&gt;#apt-get install qemu-kvm libvirt-bin virtinst bridge-utils&lt;br /&gt;&lt;br /&gt;bridge-utils - сетевой мост. Настраиваем:&lt;br /&gt;&lt;br /&gt;auto eth0&lt;br /&gt;iface eth0 inet manual&lt;br /&gt;&lt;br /&gt;auto br0&lt;br /&gt;iface br0 inet static&lt;br /&gt;    address 192.168.0.20&lt;br /&gt;    netmask 255.255.255.0&lt;br /&gt;    gateway 192.168.0.1&lt;br /&gt;    bridge_ports eth0&lt;br /&gt;    bridge_fd 9&lt;br /&gt;    bridge_hello 2&lt;br /&gt;    bridge_maxage 12&lt;br /&gt;    bridge_stp off&lt;br /&gt;&lt;br /&gt;Перезагружаемся.&lt;br /&gt;&lt;br /&gt;Проверяем &lt;br /&gt;&lt;br /&gt;#ifconfig&lt;br /&gt;#virsh list&lt;br /&gt;&lt;br /&gt;virsh - это команда управления виртуальными машинами.&lt;br /&gt;&lt;br /&gt;полезно virsh help.&lt;br /&gt;&lt;br /&gt;Теперь настала очередь создать виртуальную машину.&lt;br /&gt;&lt;br /&gt;#virt-install -n mediasrv -r 256 -f /vms/mediasrv.qcow2 -s 300 -c /iso/debian-testing-amd64-netinst.iso --vnc --vncport=5900 --noautoconsole --os-type linux --os-variant=generic26&lt;br /&gt;&lt;br /&gt;-r - оперативная память&lt;br /&gt;-f - файл-образ сервера&lt;br /&gt;-s - размер файл-образа в гигабайтах&lt;br /&gt;-c - cdrom&lt;br /&gt;- остальное понятно.&lt;br /&gt;&lt;br /&gt;как ни странно, но подключиться напрямую к vnc серверу (с другой системы в сети) мне не удалось, по-видимому Vinagre не справляется с авторизацией. Поэтому у меня получилось подключиться через ssh тунель:&lt;br /&gt;&lt;br /&gt;$ ssh -L 5900:localhost:5900 root@server&lt;br /&gt;&lt;br /&gt;После подключения, в Vinagre подключаемся к localhost:5900 и попадаем на новую виртуальную систему, с экраном установки debian.&lt;br /&gt;&lt;br /&gt;Устанавливаем ssh на обе системы и дальше дело техники.&lt;br /&gt;&lt;br /&gt;ps. В планах опробовать паравиртуализацию.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-6196210143914213824?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/pZCsx4f1wqc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/6196210143914213824/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/06/kvm-debian-squeeze.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6196210143914213824?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/6196210143914213824?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/pZCsx4f1wqc/kvm-debian-squeeze.html" title="Установка kvm на Debian Squeeze" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/06/kvm-debian-squeeze.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUMQXk_fip7ImA9WxFUF0Q.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-4782661042794157002</id><published>2010-05-01T21:41:00.013+04:00</published><updated>2010-06-29T10:18:00.746+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T10:18:00.746+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="linux" /><category scheme="http://www.blogger.com/atom/ns#" term="kubuntu" /><category scheme="http://www.blogger.com/atom/ns#" term="AVerTV" /><title>Настройка AVerTV Hybrid+FM PCI A16D в kubuntu 10.4 с передачей звука по PCI</title><content type="html">Настраивал тут недавно AVerTV Hybrid+FM PCI A16D в новой kubuntu 10.4. &lt;br /&gt;&lt;br /&gt;1. Качаем firmware &lt;a rel="nofollow" href="http://ifolder.ru/8091450"&gt;отсюда&lt;/a&gt; и копируем содержимое в /lib/firmware&lt;br /&gt;2. Устанавливаем tvtime (sudo apt-get install tvtime). Указываем настройки TV, обычно secam.&lt;br /&gt;3. Перезагружаемся.&lt;br /&gt;4. Запускаем tvtime-scanner (должны найтись каналы).&lt;br /&gt;6. Создаем файл /etc/modprobe.d/tv.conf с содержимым (загрузка модуля звука):&lt;br /&gt;options saa7134 alsa=2 secam=d&lt;br /&gt;&lt;br /&gt;Номер карты alsa=2 определяем следующей командой:&lt;br /&gt;arecord -l&lt;br /&gt;&lt;br /&gt;ищем нечто подобное SAA7134 PCM.&lt;br /&gt;7. Перезагружаемся.&lt;br /&gt;8. Создаем &lt;a href="http://www.beholder.ru/bb/viewtopic.php?t=8261"&gt;скрипт&lt;/a&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;if [ -f /usr/bin/arecord ]; then&lt;br /&gt;arecord -q -D hw:1,0 -r 32000 -c 2 -f S16_LE -B 18000 | aplay -q - &amp;&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;if [ -f /usr/bin/tvtime ]; then&lt;br /&gt;tvtime -m --mixer=/dev/mixer:pcm&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;pid=`pidof arecord`&lt;br /&gt;&lt;br /&gt;if [ $pid ]; then &lt;br /&gt;kill -9 $pid &lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Запускаем, должно работать. О настройке пульта, дополню позже, ну или можете покопаться &lt;a rel="nofollow" href="http://habrahabr.ru/blogs/ubuntu/75745/"&gt;тут&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;-B 18000 - определяет задержку, чем меньше тем лучше, но ограничивается оборудованием (определяется опытным путем).&lt;br /&gt;&lt;br /&gt;ps. Клавиши tvtime:&lt;br /&gt;h - меню;&lt;br /&gt;f - полный экран;&lt;br /&gt;m - отключение звука;&lt;br /&gt;v - поверх всех окон;&lt;br /&gt;a - переключение 16:9, 4:3;&lt;br /&gt;q - выход;&lt;br /&gt;стрелки - каналы и громкость.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-4782661042794157002?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/b7e2b_nVT_8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/4782661042794157002/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/05/avertv-hybridfm-pci-a16d-kubuntu-101.html#comment-form" title="Комментарии: 11" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/4782661042794157002?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/4782661042794157002?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/b7e2b_nVT_8/avertv-hybridfm-pci-a16d-kubuntu-101.html" title="Настройка AVerTV Hybrid+FM PCI A16D в kubuntu 10.4 с передачей звука по PCI" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>11</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/05/avertv-hybridfm-pci-a16d-kubuntu-101.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUNQHs4fSp7ImA9WxFUF0Q.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-1344176085892317081</id><published>2010-04-16T11:05:00.005+04:00</published><updated>2010-06-29T10:18:11.535+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T10:18:11.535+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="debian" /><category scheme="http://www.blogger.com/atom/ns#" term="virtual server" /><title>Установка debian lenny на Microsoft Virtual Server</title><content type="html">Качаем &lt;a rel="nofollow" href="http://www.debian.org/CD/netinst/#netinst-stable"&gt;netinst&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;При загрузке Tab для редактирования меню.&lt;br /&gt;Удаляем "queit", добавляем "noreplace-paravirt nofxsr".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-1344176085892317081?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/KCIYdlDAOQ8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/1344176085892317081/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/04/debian-lenny-microsoft-virtual-server.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1344176085892317081?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1344176085892317081?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/KCIYdlDAOQ8/debian-lenny-microsoft-virtual-server.html" title="Установка debian lenny на Microsoft Virtual Server" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/04/debian-lenny-microsoft-virtual-server.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEQERXoycCp7ImA9WxFUF0Q.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-2728022065257675900</id><published>2010-03-09T15:11:00.009+03:00</published><updated>2010-06-29T10:18:24.498+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T10:18:24.498+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SmartGWT" /><title>SmartGwt. Проблема с RestDataSource.addData()</title><content type="html">Есть небольшая проблема в релизе smartgwt при вызове addData() при использовании DataSource в DynamicForm. Выглядит примерно следующим образом:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;com.google.gwt.core.client.JavaScriptException: (RangeError): Maximum call stack size exceeded&lt;br /&gt; type: stack_overflow&lt;br /&gt; arguments: &lt;br /&gt; __gwt_ObjectId: 104&lt;br /&gt;    at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:195)&lt;br /&gt;    at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:120)&lt;br /&gt;    at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:507)&lt;br /&gt;    at com.google.gwt.dev.shell.ModuleSpace.invokeNativeVoid(ModuleSpace.java:284)&lt;br /&gt;    at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:107)&lt;br /&gt;    at com.smartgwt.client.data.DataSource.addData(DataSource.java)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В принципе в svn уже поправили, есть и быстрое решение &lt;a rel="nofollow" href="http://code.google.com/p/smartgwt/issues/detail?id=336"&gt;проблемы&lt;/a&gt; - &lt;br /&gt;установить в добавляемой записи record пустой атрибут __ref: &lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;record.setAttribute("__ref", "")&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-2728022065257675900?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/KnzbtBcgVhE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/2728022065257675900/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/03/smartgwt-restdatasourceadddata.html#comment-form" title="Комментарии: 5" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/2728022065257675900?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/2728022065257675900?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/KnzbtBcgVhE/smartgwt-restdatasourceadddata.html" title="SmartGwt. Проблема с RestDataSource.addData()" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>5</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/03/smartgwt-restdatasourceadddata.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQARHgycCp7ImA9WxBVEk4.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-8662542286354157092</id><published>2010-02-15T15:02:00.005+03:00</published><updated>2010-02-15T15:19:05.698+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-15T15:19:05.698+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="цитаты" /><category scheme="http://www.blogger.com/atom/ns#" term="философия" /><title>Цитаты</title><content type="html">Очередной блок цитат. Для отвлечения от программирования.&lt;br /&gt;&lt;br /&gt;Каждый слышит лишь то, что понимает (с) Иоганн Вольфганг Гете&lt;br /&gt;&lt;br /&gt;Кому хватает корки черного хлеба, не понять тех, кому не хватает черной икры к батону с маслом. (с) Геннадий Воловой&lt;br /&gt;&lt;br /&gt;Не огорчаюсь, если люди меня не понимают, - огорчаюсь, если я не понимаю людей. (с) Конфуций&lt;br /&gt;&lt;br /&gt;Непонимание делает из друзей врагов. (с) Лион Фейхтвангер&lt;br /&gt;&lt;br /&gt;Несчастье людей заключается не только в том, что они не понимают счастья - еще они не понимают друг друга. (c) Авессалом Подводный&lt;br /&gt;&lt;br /&gt;Нет ничего мудреного в том, чтобы быть общепонятным, если при этом отказываешься от всякого основательного понимания. (c) Иммануил Кант&lt;br /&gt;&lt;br /&gt;Она не понимала его, как бесхитростные ходики, вероятно, не понимают хронометра. (c) Эмиль Кроткий.&lt;br /&gt;&lt;br /&gt;Понимание - начало согласия. (c) Бенедикт Спиноза&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-8662542286354157092?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/uMwlsOgdFpo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/8662542286354157092/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/02/blog-post_15.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/8662542286354157092?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/8662542286354157092?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/uMwlsOgdFpo/blog-post_15.html" title="Цитаты" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/02/blog-post_15.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEQHQXk6eCp7ImA9WxFUF0Q.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-2445459372479862218</id><published>2010-02-15T08:59:00.005+03:00</published><updated>2010-06-29T10:18:50.710+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-29T10:18:50.710+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="django" /><category scheme="http://www.blogger.com/atom/ns#" term="REST" /><category scheme="http://www.blogger.com/atom/ns#" term="django-piston" /><category scheme="http://www.blogger.com/atom/ns#" term="SmartGWT" /><title>SmartGwt + Django = REST</title><content type="html">Этот пост будет просто рассуждением. Точнее даже, рассказом о личных выводах. Так вот, в прошлом я связывал &lt;a href="http://vermus.blogspot.com/2009/12/gwt-django.html"&gt;django и gwt с помощью json-rpc&lt;/a&gt;. Это полезная вещь в целом, но очень трудоемкая для комплексного подхода к приложениям на SmartGWT, в котором уже есть класс DataSource, и более удобный вариант &lt;a href="http://www.smartclient.com/smartgwt/showcase/#featured_restfulds"&gt;RestDataSource&lt;/a&gt;. В нем уже заложены такие возможности, как передача данных в форматах XML, JSON через HTTP, гибкая поддержка REST идеологии (GET, PUT, POST и DELETE), поддерживает пагинацию, поиск, сортировку и другое. Что это значит? Первое, используя REST - создается некая стандартизация запросов (собственно идеология REST), в отличии от JSON-RPC. Создавая REST &lt;a rel="nofollow" href="http://bitbucket.org/jespern/django-piston/wiki/Home"&gt;поддержку на сервере&lt;/a&gt;, мы одновременно создаем API нашего сайта. Второе, нам не надо для каждого UI элемента прописывать свою функцию получения данных, создается один источник данных REST, который можно использовать &lt;a href="http://www.smartclient.com/smartgwt/showcase/#featured_pattern_reuse"&gt;повторно, также менять источники данных для элементов UI на лету&lt;/a&gt;. Все это очень здорово и практично. &lt;br /&gt;Но json-rpc не стоит выкидывать на свалку. Некоторые вещи в которых не нужен громоздкий DataSource, я думаю, нужно делать с помощью него, например, нажатие на кнопку для голосования или какие-то атомарные операции.&lt;br /&gt;Рассказывать как связать &lt;a rel="nofollow" href="http://bitbucket.org/jespern/django-piston/wiki/Home"&gt;django-piston&lt;/a&gt; и &lt;a rel="nofollow" href="http://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/data/RestDataSource.html"&gt;RestDataSource&lt;/a&gt; не буду. Скажу лишь, что сделать это можно, и довольно просто.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-2445459372479862218?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/rfJ317GOy8Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/2445459372479862218/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/02/smartgwt-django-rest.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/2445459372479862218?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/2445459372479862218?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/rfJ317GOy8Y/smartgwt-django-rest.html" title="SmartGwt + Django = REST" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/02/smartgwt-django-rest.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8DQX4-fCp7ImA9Wx9QE0Q.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-5380870965545070417</id><published>2010-02-03T14:49:00.007+03:00</published><updated>2010-12-26T22:14:30.054+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-26T22:14:30.054+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="Puma.NET" /><title>Распознавание текста в .NET</title><content type="html">Понадобилось тут по работе внедрить систему распознавания текста с изображения. Так как программа совсем небольшая, да и как такого полноценного распознавания (вроде FineReader) не требовалось, а требовалось лишь результат распознавания текста сохранить в базу, как индекс для последующего полнотекстового поиска по изображениям, то я старался найти бесплатное простое решение. И оно нашлось - &lt;a href="http://pumanet.codeplex.com/"&gt;Puma.NET&lt;/a&gt;.&lt;br /&gt;Что такое Puma.NET?&lt;br /&gt;&lt;blockquote&gt;Puma.NET представляет собой оболочку для библиотеки распознавания Cognitive Technologies CuneiFrom, которая позволяет легко включать функций распознавания в любом NET Framework 2.0 (или выше) приложении. API предоставляется через ряд простых классов. Высокая производительность и точность результатов распознавания может быть достигнута с помощью пары строчек кода.&lt;br /&gt;&lt;br /&gt;Возможности распознавания:&lt;br /&gt;1. Технология Omni Font - распознавание практически всех печатных шрифтов&lt;br /&gt;2. Поддержка 27 языков (английский, немецкий, хорватский, польский, датский, португальский, голландский, цифры, чешский, французский, румынский, венгерский, болгарский, словенский, латышском, литовском, эстонском, турецкий, русский, шведский, испанский, итальянский, русский/английский, украинский, сербский)&lt;br /&gt;3. Проверка орфографии и автоматическая корректировка&lt;br /&gt;4. Обнаружение форматирования шрифта (размер, курсив, жирность, подчеркивание)&lt;br /&gt;5. Фрагментация и сохранение структуры документа: параграфы, пробелы, изображения, таблицы и т.д.&lt;br /&gt;6. Улучшенное распознавание искаженных и пестрых изображений, специальные режимы для документов с матричных принтеров и факсов&lt;br /&gt;7. Входные форматы изображений: BMP, GIF, EXIG, JPG, PNG и TIFF&lt;br /&gt;8. Выходные форматы: txt, RTF, HTML&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Что приятно, разработчик наш соотечественник.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://itexpress.com.ua/1c.html" target="_blank"&gt;Удаленное сопровождение 1С Бухгалтерия, 1С Торговля. Регламентированная отчетность Симферополь&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-5380870965545070417?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/Wrvz5Nq2r1k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/5380870965545070417/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/02/net.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/5380870965545070417?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/5380870965545070417?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/Wrvz5Nq2r1k/net.html" title="Распознавание текста в .NET" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/02/net.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMGQns6cCp7ImA9WxBWEkw.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-4177633299031087138</id><published>2010-02-03T10:51:00.007+03:00</published><updated>2010-02-03T19:27:03.518+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-03T19:27:03.518+03:00</app:edited><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>Нацисты и националисты</title><content type="html">Вы не задумывались о том, почему понятия нацизма и национализма жестко скрутили воедино в современной России? Да настолько, что журналисты сами не разбираются в их разнице. Я каждый раз задумываюсь, видя очередную &lt;a href="http://www.lenta.ru/news/2010/02/02/pagans/"&gt;статью в СМИ&lt;/a&gt;, и думаю, что это сделано преднамеренно (какими силами, вопрос третий).&lt;br /&gt;По ссылке мы видим заголовок: "Ответственность за взрыв в Петербурге взяли на себя националисты". Далее читаем текст:&lt;br /&gt;&lt;blockquote&gt;Ответственность за взрыв на Октябрьской железной дороге в Санкт-Петербурге взяла на себя некая группировка "NS-WP Невоград". Об этом пишет "Фонтанка.ру" со ссылкой на сообщения, появившиеся в блогах. Отмечается, что таким образом национал-социалисты решили отметить языческий праздник "Громница".&lt;/blockquote&gt;&lt;br /&gt;Помилуйте, товарищи журналисты, но &lt;a href="http://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB-%D1%81%D0%BE%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%BC"&gt;национал-социализм&lt;/a&gt; - это "нем. Nationalsozialismus, сокращённо нацизм", а далеко не &lt;a href="http://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%BC"&gt;национализм&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;В своей основе национализм проповедует верность и преданность своей нации, политическую независимость и работу на благо собственного народа, объединение национального самосознания для практической защиты условий жизни нации, её территории проживания, экономических ресурсов и духовных ценностей.&lt;/blockquote&gt;&lt;br /&gt;Тем более, что:&lt;br /&gt;&lt;blockquote&gt;В академической среде отождествление национализма с его крайними формами считается ошибочным.&lt;/blockquote&gt;&lt;br /&gt;То есть интеллигенция считает, что националист никогда не подменяет понятие национализма и крайнего национализма (кто придумал этот термин, если есть нацизм и фашизм?). Да, говорят, что политика+крайний национализм=нацизм, но &lt;a href="http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%BB%D0%B8%D1%82%D0%B8%D0%BA%D0%B0"&gt;политика&lt;/a&gt; — "всеохватывающий феномен общественной жизни, пронизывающий все её формы и включающий в себя все формы социальной активности людей, все виды деятельности по их организации и руководству в рамках процессов производства.", так чем же выражение нацисткой позиции отличается от выражения позиции крайнего национализма? Ничем! Кто-то явно смешивает понятия национализма и нацизма, преследуя определенные цели. Задумайтесь об этом, когда будете отслеживать новости в наших СМИ.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-4177633299031087138?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/nM-vTVfvTSI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/4177633299031087138/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/02/blog-post.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/4177633299031087138?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/4177633299031087138?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/nM-vTVfvTSI/blog-post.html" title="Нацисты и националисты" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/02/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcDSHo7eyp7ImA9WxBXF0g.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-3609471654586475501</id><published>2010-01-29T12:40:00.000+03:00</published><updated>2010-01-29T12:41:19.403+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-29T12:41:19.403+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="интересное" /><title>В момент смерти происходит "взрыв мозга"</title><content type="html">&lt;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,29,0" width="408" height="356" id="flvplayer" align="middle"&gt;&lt;param name="allowScriptAccess" value="always" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="movie" value="http://www.vesti.ru/i/flvplayer.swf?vid=246239&amp;autostart=false" /&gt;&lt;param name="quality" value="high" /&gt;&lt;param name="wmode" value="transparent" /&gt;&lt;param name="devicefont" value="true" /&gt;&lt;param name="bgcolor" value="#000000" /&gt;&lt;param name="vid" value="246239" /&gt;&lt;embed src="http://www.vesti.ru/i/flvplayer.swf?vid=246239&amp;autostart=false" quality="high" devicefont="true" bgcolor="#000000" width="408" height="356" name="flvplayer" align="middle" allowScriptAccess="always" allowFullScreen="true" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-3609471654586475501?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/pJLbjoGLe3I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/3609471654586475501/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/01/blog-post_29.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3609471654586475501?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/3609471654586475501?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/pJLbjoGLe3I/blog-post_29.html" title="В момент смерти происходит &quot;взрыв мозга&quot;" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/01/blog-post_29.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcCSXo-eip7ImA9WxBQE0U.&quot;"><id>tag:blogger.com,1999:blog-4797167376881538347.post-1290522562048858112</id><published>2010-01-13T15:56:00.004+03:00</published><updated>2010-01-13T16:07:48.452+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-13T16:07:48.452+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="философия" /><category scheme="http://www.blogger.com/atom/ns#" term="Бердяев" /><title>Цитаты Бердяева под настроение</title><content type="html">1. Нет ничего более злого, чем стремление осуществить во что бы то ни было благо.&lt;br /&gt;2. В институте брака есть бесстыдство обнаружения для общества того, что должно было бы быть скрыто, охранено от посторонних взоров.&lt;br /&gt;3. У женщин есть необыкновенная способность порождать иллюзии, не быть такими, каковы они на самом деле.&lt;br /&gt;4. Любовь всегда нелегальна. Легальная любовь есть любовь умершая. Легальность существует лишь для обыденности, любовь же выходит из обыденности.&lt;br /&gt;5. Творчество - победа над похотью жизни.&lt;br /&gt;6. Творчество - переход небытия в бытие через акт свободы.&lt;br /&gt;7. Ад нужен не для того, чтобы злые получили воздаяние, а для того, чтобы человек не был изнасилован добром и принудительно внедрен в рай.&lt;br /&gt;8. В любви есть деспотизм и рабство. И наиболее деспотична любовь женская, требующая себе всего!&lt;br /&gt;9. Ревность не соединена со свободой человека. В ревности есть инстинкт собственности и господства, но в состоянии унижения. Нужно признавать право любви и отрицать право ревности, перестав ее идеализировать... Ревность есть тирания человека над человеком. Особенно отвратительна женская ревность, превращая женщину в фурию.&lt;br /&gt;&lt;br /&gt;(с) Николай Александрович Бердяев&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4797167376881538347-1290522562048858112?l=vermus.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/vermus/~4/6Pfr64iCP2A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://vermus.blogspot.com/feeds/1290522562048858112/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://vermus.blogspot.com/2010/01/blog-post_13.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1290522562048858112?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4797167376881538347/posts/default/1290522562048858112?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/vermus/~3/6Pfr64iCP2A/blog-post_13.html" title="Цитаты Бердяева под настроение" /><author><name>Vermus</name><uri>http://www.blogger.com/profile/02301362335658588127</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_kR0dajk9XAw/SUEZifiZGYI/AAAAAAAAAAU/5OxQBomLbek/S220/1132344966d63af014.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://vermus.blogspot.com/2010/01/blog-post_13.html</feedburner:origLink></entry></feed>

