<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7292835686865407878</id><updated>2011-08-02T08:09:08.952+07:00</updated><category term='objc'/><category term='First post'/><category term='bzr'/><category term='macosx'/><category term='static'/><category term='Constraints'/><category term='linux fun'/><category term='homebudget'/><category term='website'/><category term='Generics'/><category term='conference'/><category term='django'/><category term='blog'/><category term='RichTextBox'/><category term='Индусы'/><category term='c#'/><category term='cocoa'/><category term='локальная сеть'/><category term='android'/><category term='две сетевухи'/><category term='objectdatasource'/><category term='python'/><category term='asp.net'/><category term='wordwrap'/><category term='launchpad'/><category term='word-breaking'/><category term='ubuntu'/><category term='c++'/><category term='syntax-highlighting'/><category term='.NET'/><category term='svn'/><title type='text'>alexa.cpp</title><subtitle type='html'>Что-то о программировании</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>24</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-8968651204312186992</id><published>2011-02-03T23:22:00.001+06:00</published><updated>2011-02-03T23:25:01.061+06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='objc'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='macosx'/><title type='text'>Minimalistic Cocoa application</title><content type='html'>Some time ago I faced with problem - to write crossplatform application with GUI, but it should not contain any tool-specific resources like MSVC rc files, or xcode resources. It should build GUI in the most native way. Simplify problem: there was the following interface in existing code base, that I have to satisfy.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp;"&gt;struct IWindow
{
  virtual void Create() = 0;
  virtual void DoEvents() = 0;
  virtual void Focus() = 0;
  virtual void Hide() = 0;
  virtual void Close() = 0;
  virtual bool IsClosed() = 0;
};
&lt;/pre&gt;&lt;br /&gt;
And an application should be designed to use the similar pattern (it comes from windows-os application as far you can see):&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp;"&gt;int main()
{
  IWindow* window = OSDependent::GetCurrentWindowSystem()-&amp;gt;GetWindow();
  windiw-&amp;gt;Create();
  window-&amp;gt;Focus();
  while (!window-&amp;gt;IsClose())
  {
    window-&amp;gt;DoEvents();
    ///&amp;lt;! some logic should be placed here
  }
  return 0;
}
#ifdef __OBJC__
class MyWindow : public IWindow
{
public:
  ///!&amp;lt; .. methods of IWindow interface
protected:
  NSWindow* window_;
};
#endif // __OBJC__
&lt;/pre&gt;&lt;br /&gt;
After a lot of reading and number of tries, I made a native Cocoa window without any xcode-specific resource. Ta-da-dam!! :) The main function of this post: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp;"&gt;void MyWindow::Create()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
   
  [NSApplication sharedApplication];
  [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
        
  unsigned int styles = NSResizableWindowMask | 
    NSClosableWindowMask | 
    NSTitledWindowMask;
        
  NSRect rectanle = NSMakeRect(100, 100, 640, 480);
  window_ = [[NSWindow alloc] 
    initWithContentRect:rectangle
    styleMask:styles
    backing:NSBackingStoreBuffered 
    defer:NO];
  [window_ setTitle:@&amp;quot;(none)&amp;quot;];
  [window_ setReleasedWhenClosed:NO];
  [window_ 
    performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) 
    withObject:nil 
    waitUntilDone:YES];
  [NSApp activateIgnoringOtherApps:YES];
  [NSApp finishLaunching];
        
  [pool release];
}
&lt;/pre&gt;&lt;br /&gt;
The most strange code here is setActivationPolicy code line. It allows window to be activated, receive keyboard and mouse event and more. I spent a lot of time to find it (this was my first macosx experience). &lt;br /&gt;
&lt;br /&gt;
Next key of system - a message pump. Usually, it isn't necessary, because of usage [NSApp run]; functionality to make window application work. As far I understand NSApp run includes look-like code. In my case, I have to implement message cycle by myself. Here it is:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp;"&gt;void MyWindow::DoEvents()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
    untilDate:[NSDate distantPast]
    inMode:NSDefaultRunLoopMode
    dequeue:YES];
  if (event) {
    [NSApp sendEvent:event];
    [NSApp updateWindows];
  }
  [pool release];
}
&lt;/pre&gt;&lt;br /&gt;
Other minor implementation, easy to get, but might be useful:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp;"&gt;bool MyWindow::IsClosed() 
{
  return ![window_ isVisible];
}
void MyWindow::Focus()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  [window_ makeKeyAndOrderFront:nil];
  [pool release];
}
void MyWindow::Hide()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  [window_ orderOut:nil];
  [pool release];
}
void MyWindow::Close()
{
  [window_ close];
}
&lt;/pre&gt;&lt;br /&gt;
As for addition, we could add custom NSView to our window, here the defenition of derived view.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp;"&gt;@interface MyView : NSView
{
  MyWindow* window_;
}

- (id)initWithFrame:(NSRect)rectangle;
- (id)initWithMyWindow:(MyWindow*)window;
- (MyWindow*)getWindow;
@end

@implementation MyView

- (id)initWithFrame:(NSRect)rectangle
{
  self = [super initWithFrame:rectangle];
  m_Window = 0;
  return self;
}
- (id)initWithMyWindow:(MyWindow*)window
{
  Math::Rect wr = w-&amp;gt;GetRectangle();
  NSRect r = NSMakeRect(wr.x, wr.y, wr.Width, wr.Height);
  if (self = [self initWithFrame:r])
  {
    window_ = window;
  }
  return self;
}
- (MyWindow*)getWindow
{
  return window_;
}
- (BOOL)acceptsFirstResponder
{
  return YES;
}
- (BOOL)becomeFirstResponder
{
  return YES;
}
- (BOOL)resignFirstResponder
{
  return YES;
}
- (BOOL)canBecomeKeyView
{
  return YES;
}
@end
&lt;/pre&gt;&lt;br /&gt;
Final window create method:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp;"&gt;void MyWindow::Create()
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
        
  [NSApplication sharedApplication];
  [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
        
  unsigned int styles = NSResizableWindowMask | 
    NSClosableWindowMask | 
    NSTitledWindowMask;
        
  NSRect rectanle = NSMakeRect(100, 100, 640, 480);
  window_ = [[NSWindow alloc] 
    initWithContentRect:rectangle
    styleMask:styles
    backing:NSBackingStoreBuffered 
    defer:NO];

  [window_ setTitle:@&amp;quot;(none)&amp;quot;];
        
  [window_ setReleasedWhenClosed:NO];
       
  view_ = [[MyView alloc] initWithMyWindow:this];
  [window_ setContentView:view_];
  [window_ makeFirstResponder:view_];
        
  [view_ setFrame:NSMakeRect(0, 0, rectangle.Width, rectangle.Height)];
       
  [window_ 
    performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) 
    withObject:nil 
    waitUntilDone:YES];
  [NSApp activateIgnoringOtherApps:YES];
  [NSApp finishLaunching];
        
  [pool release];
}
&lt;/pre&gt;&lt;br /&gt;
That's all. Yes, this is not the most minimal cocoa application, but it is created without usage of xcode magic, and includes not-big code :) The most interesting and hard things have been found at &lt;a href="http://cocoawithlove.com/"&gt;cocoa with love&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
P.S. Sorry for my bad english ^^&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-8968651204312186992?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/8968651204312186992/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=8968651204312186992' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/8968651204312186992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/8968651204312186992'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2011/02/minimalistic-cocoa-application.html' title='Minimalistic Cocoa application'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-5253381421900522639</id><published>2010-10-26T02:02:00.000+07:00</published><updated>2010-10-26T02:02:37.793+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Populate fake data in django</title><content type='html'>Задача - нагенерить N рандомных слов, которые хоть чуть-чуть похожи на настоящие слова (то есть подобие слогов). Халява на пайтоне:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: python;"&gt;&gt;&gt;&gt; def gen_rand_strings(num):
...     import random
...     allchars = [chr(i) for i in range(97, 97+26)]
...     vowels = ['a', 'e', 'i', 'o', 'u', 'y']
...     consonants = [i for i in allchars if i not in vowels]
...     ret = []
...     for i in range(0, num):
...         s = ''
...         rr = random.randint(2, 6)
...         for j in range(1, rr):
...             s += random.choice(consonants)
...             s += random.choice(vowels)
...         ret.append(s)
...     return ret
&gt;&gt;&gt; gen_rand_strings(20)
['vydohefy', 'hete', 'comegihu', 'sarabo', 'tyde', 'ro', 'mojuletabu', 'ruridaviqy', 'my', 'tafaluhy', 'teleniwu', 'xixy', 'cyky', 'hypakelo', 'kicuba', 'jekiqovazu', 'botaveci', 'byru', 'dolizojaca']
&lt;/pre&gt;&lt;br /&gt;
Зачем это понадобилось, да вот захотелось набить БД фейковыми данными :) примерно так (по проекту &lt;a href="http://alexadotcpp.blogspot.com/search/label/homebudget"&gt;homebudget&lt;/a&gt;):&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: python;"&gt;&gt;&gt;&gt; from homebudget.purchases import models
&gt;&gt;&gt; models.PurchaseTag.objects.count()
0
&gt;&gt;&gt; new_tags = gen_rand_strings(20)
&gt;&gt;&gt; for tag in new_tags:
...     ntag = models.PurchaseTag.objects.get_or_create(value=tag, norm_value=tag, owner=usernew)
...     ntag[0].save()
&gt;&gt;&gt; models.PurchaseTag.objects.count()
20
&gt;&gt;&gt;
&gt;&gt;&gt; from datetime import date, timedelta
&gt;&gt;&gt; last = date.today()
&gt;&gt;&gt; dt = date(year=2010,month=05,day=1)
&gt;&gt;&gt; oneday = timedelta(days=1)
&gt;&gt;&gt; while (dt &lt; last):
...     rr = random.randint(3, 10)
...     for i in range(0, rr):
...          tag = models.PurchaseTag.objects.get(value=random.choice(new_tags))
...          price = random.randint(1, 100)
...          purch = gen_rand_string(2)[0]
...          p = models.Purchase(name=purch,quantity=1.,price_for_one=price,price_total=price, purchase_date=dt, owner=usernew)
...          p.save()
...          p.tags.add(tag)
...          p.save()
...     dt += oneday
&lt;/pre&gt;
База забита фейковыми покупками за полгода, к каждой покупке привязан один из 20 новых тагов. Теперь можно спокойно тестить выборки для построения статистики. Халява как она есть.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-5253381421900522639?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/5253381421900522639/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=5253381421900522639' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5253381421900522639'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5253381421900522639'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/10/populate-fake-data-in-django.html' title='Populate fake data in django'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-3573896383036315271</id><published>2010-10-18T00:29:00.001+07:00</published><updated>2010-10-18T00:38:08.515+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bzr'/><category scheme='http://www.blogger.com/atom/ns#' term='launchpad'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><title type='text'>хроники django или homebudget part 7</title><content type='html'>&lt;p&gt;После долго молчания, возвращаемся к нашим баранам.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Во первых, что обязательно нужно описать, так это передвижение с google-code на launchpad. Предпосылки:&lt;/p&gt;&lt;ol&gt;&lt;li&gt; во-первых плюсы DVCS системы:&lt;ol&gt;&lt;li&gt; банальные мелочи, у меня есть четыре машины с которых я колбашу код. Естественно, я один, про фитчи думаю один, изменения архаичные, некоторые нужно сразу в главную ветку, другие - только в бранчи. В свн-бранчи все оформлять тупо лень (т.к. о них нужно помнить). По этому колбашу в trunk-е. И синхронизировать 4 машины ой какая проблема - типа вчера уснул и часть не закомитил, а изменения были эпические. Работа встала на других машинах. В распределенной системе - каждая локальная копия по-сути бранч. Работотать на нескольких машинах в разы проще.&lt;/li&gt;
&lt;li&gt; смерджить можно все что угодно, без последствий. В свн-е же для мерджа нужные крепкие нервы и холодные разум.&lt;/li&gt;
&lt;li&gt; можно без проблем откатиться до любой версии. Кто как, но я это люблю.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt; во-вторых плюсы launchpad-а:&lt;ol&gt;&lt;li&gt; bazaar собственно. &lt;/li&gt;
&lt;li&gt; Хостинг многих опен-сурс проектов, которые я использую (хотя бы убунта и терминатор)&lt;/li&gt;
&lt;li&gt; всякие бонусы типа отображения публичного gpg ключа и активности пользователь.&lt;/li&gt;
&lt;li&gt; есть публичная страничка на которой отображены все проекты пользователя. В гугль-коде такую не нашел. Мне это критично.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt; давно хотел провести глобальные изменения архитектуры&lt;ol&gt;&lt;li&gt; есть мой хостинг (пока не рассекреченный), крутится ngnix, заведен джанго-проект. В нем крутится homebudget (как не трудно догадаться). Иногда мне нужно добавлять новые приложения для личных целей (скорее не целей, а идей и экспериментов). Заводить новый процесс под это ой как не хочется, да и не логично. А в старой структуре свн-а был заведен репозиторий прямо на проект, и лежащие внутри приложения. Новые приложения хранятся (или не хранятся вовсе) в разных местах. Ну вот и логичнее всего мне вести репозитории по приложениям, а не по проекту. Так что от репозиторя для проекта было решено отказаться в пользу локального репозиторя. А приложения - в народ.&lt;/li&gt;
&lt;li&gt; Еще один плюс приложений - манипулировать гораздо проще. Развертываешь в отдельной папке такую-то ревизию, ставишь на нее симлинк, а старый затираешь. Круто, да круто. &lt;/li&gt;
&lt;li&gt; Хотя нужно сказать структура проект-приложение не совсем гибкая. Это не удобно, тупо не удобно. Нужно больше ветвления. Типа переиспользуемые под-приложения.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;p&gt;Как это делалось, да просто как можно догадаться.&lt;/p&gt;&lt;ol&gt;&lt;li&gt; изменил все межпроектные линки на использование глобальных "homebudget.purchases" и "homebudget.tagsfield"&lt;/li&gt;
&lt;li&gt; вынес маппинг урлов из проектого urls.py в соответствующие файлы приложений. В проектном urls.py теперь стоит просто include bla-bla-bla. &lt;/li&gt;
&lt;li&gt; разместил статик контент и темплейты по директориям проектов (т.е. было "homebudget/content/purchases/css", стало "homebudget/purchases/media/css" и тд)&lt;/li&gt;
&lt;li&gt; в новый проект добавляется директория типа "static-content", в нее ставятся симлинки на директории "homebudget/purchases/media". MEDIA_ROOT смотрит на директорию "static-content". В итоге: из проекта до его статик файлов можно достучаться по пути "/site-media/purchases/css/" (работает MEDIA_URL, имя проекта, и путь по симлинке).&lt;/li&gt;
&lt;li&gt; темплейтные директории прописываются явно в settings.py в TEMPLATES_DIR (типа "/homebudget/purchases/templates/"). Соответствующий код для взятия темплейтов был подправлен.&lt;/li&gt;
&lt;li&gt; ну и проекты были добавлены в PYTHONPATH, типа "export PYTHONATH=${PYTHONPATH}:/path/to/project" где-то внутри "~/.bashrc"&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;p&gt;Собственно ссылки:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://code.launchpad.net/~alexa-infra/+junk/homebudget"&gt;https://code.launchpad.net/~alexa-infra/+junk/homebudget&lt;/a&gt; - новый репозиторий&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/homebudget-site/"&gt;http://code.google.com/p/homebudget-site/&lt;/a&gt; старый репозиторий (будет апдейтиться по ключевым моментам, но рабочие измениния в нем не найти)&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;p&gt;Посты по теме: &lt;ul&gt;&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/04/django-homebudget-part-1.html"&gt;хроники django или homebudget part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-2.html"&gt;хроники django или homebudget part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-3.html"&gt;хроники django или homebudget part 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html"&gt;хроники django или homebudget part 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-5.html"&gt;хроники django или homebudget part 5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/08/django-homebudget-part-6.html"&gt;хроники django или homebudget part 6&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-3573896383036315271?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/3573896383036315271/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=3573896383036315271' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/3573896383036315271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/3573896383036315271'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/10/django-homebudget-part-7.html' title='хроники django или homebudget part 7'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-5468576809021451833</id><published>2010-08-27T23:16:00.000+07:00</published><updated>2010-08-27T23:16:12.452+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='linux fun'/><title type='text'>FrOSCon 2010</title><content type='html'>&lt;p&gt;Итак, пора уже представить миру мой отчет о том как проходила конференция &lt;a href="http://froscon.org"&gt;FrOSCon 2010&lt;/a&gt; (Free and Open Source Software Conference). Состоялась она 21-22 августа, в Германии, в "городке" Sankt-Augustin, в здании местного IT-технического университета.&lt;/p&gt;&lt;br /&gt;
&lt;blockquote&gt;почему "в городке" - насколько я понял, из объяснений друзей, участников конфы, и карты метро-трамваев - Санкт-Агустин это не совсем город, не совсем деревня, не совсем район города, но официально город. Изначально я приехал в Бонн (население около 300 тыс), но этого города не было в объявлении поезда, так как в 30 км от него находится Кельн где проживает около 3 млн. Бонн состоит из районов - давным давно они были деревнями, потом срослись и сохранили свои названия в названиях районов. Совсем рядом к Бону, ну полностью прилегают, без какой-то границы, еще штук 7-8 аналогичных городов, в числе которых и Санкт-Агустин, и Кельн.&lt;/blockquote&gt;&lt;br /&gt;
&lt;p&gt;Нужно сказать что конференция проходила пятый раз и выросла с маленькой тусовки в 10-20 человек в конференцию, объединяющую более одной тысячи человек. Организаторы, в бывшем студенты сего местного университета в один день сказали что-то типа "we will go build our own conference, with blackjack and hookers", договорились и сделали. Полный опен-сурс, полный кайф. Что у них получилось? Не маленькая локальная тусовка гиков, а действительно международная тусовка гиков. Хотя на конференции сильно чувствуется преобладание немецкого языка (большинство народу все-таки местные), но как водится в it-community "да че этот английский язык?! там все слова взяты из си-плюс-плюса!" - большинство нормально говорят по английски, и могу свободно изъясняться на двух языках. Как говорят, в этом году было предложение провести полностью конференцию (все доклады) на английском языке, но встала проблема, не все докладчики могли полностью свободно изъясняться на английском, такие дела. Но радует (как для человека без знания немецкого) что около 25% докладов были на английском. Этого вполне достаточно. Если есть вопросы - на них докладчик может ответить и на ломаном английском, но без микрофона.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Где это было. В августе в университетах не проводятся занятия, но все равно там находится туча народу, кто-то делает свой проект, кто-то просто тусуется. И понятно почему. Здание универа просто восхитительное, все очень и очень модерновое, все укомплектовано под завязку, в аудиториях очень сильно приятно находиться. Все семинарные комнаты оборудованы компами, за каждым местом студента. Вокруг универа какие-то архитектурные сооружения (типа гигантского магнита). Внутри куча работающих кафе. Пусть наши универы (вроде триждыпроклятого и родного НГУ) поперхнуться слюной. Организаторы полностью снимают это здание на выходные. Фойе и коридоры отводятся под стенды контор, лекционные аудитории - под доклады, семинарские аудитории - под проекты, столовая - под столовую, внутренний двор - под опен-эйр-пати.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Как организовывалось. Как и весь опен-сурс: силами людей и бесплатно. Организационный коммитет - это вышеобозначенные студенты универа и их единомышленники. Все остальное делается силами волонтеров, то есть очень простых людей, которые расставят столы и стенды, протянут провода, разгрузят грузовики с пивом, нальют его в стаканы, пожарят мясо и т.п. Надо сказать что мало таких людей не было. Я и сам занимался там растаскиванием столов и т.д., то есть стал волонтером. Могу сказать - ни капли ни жалею, так как это опен-сурс, это круто.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Как проводилось. Достаточно скромно, вход для обычных людей на 2 дня - 5 евро, для коммерческих контор - 100 евро, для волонтеров - бесплатно. Вот вам и сила опен-сурса. Сколько нужно мозгоебаний и затрат для того что бы попасть на конфу мсфт? во-во. Стендов было много. bsd, debian, ubuntu, gentoo, centos - и все им смежные... gnome, kde, qt... postgresql, firebird, mysql... openoffice... zope, и че-то там еще для python-а... xen и че-то еще для виртуальщиков... куча прессы типа open-source-press где можно было в тридешево купить книжки (только на немецком)... какие-то проекты типа linux для школы, ноутбук каждому, космическая программа германии... был и гугль, и был (мне не известный) tarent... Всего не упомнил, но большинство охватил.&lt;/p&gt;&lt;br /&gt;
&lt;blockquote&gt;Кстати что не понравилось - стенд гугля - натянули на стенде три майки с лого, поставили на стол пару макбуков, посадили за стол 3-4 невменяемых человека, и дали им пару занимательных головоломок - стремно это как-то выглядело, но очереди к их стенду были всегда. А вот tarent зажег, действительно было на что посмотреть, загончик с радиоуправляемыми роботами, у которого постоянно крутились дети, куча-куча маленьких стендов, куча-куча людей, какие-то конкурсы.&lt;/blockquote&gt;&lt;br /&gt;
&lt;p&gt;Каждый час в лекционных были &lt;a href="http://programm.froscon.org/2010/day_2010-08-21.en.html"&gt;доклады&lt;/a&gt;. Об докладах читать ниже. Каждый час одновременно 5 докладов, кроме keynotes раз в день (который для всех). Далее я опишу те доклады на которых мне посчастливилось присутствовать и мое резюме по ним.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Free and Open Source Software in the Developing World&lt;/b&gt; &lt;a href="http://en.wikipedia.org/wiki/Jon_Hall_(programmer)"&gt;Jon "maddog" Hall&lt;/a&gt; это было в качестве keynotes, человек-директор Linux International, такой тихинький свиду дедушка, отжег по полной. Полная ненависть к Windows, много умных вещей про развитие стран третьего мира, про коммерческий и опен-сурсный код. Очень профессиональный доклад. Куча инфы, перемешка шуток, очень хорошие ораторские способности. Основные идеи: странам третьего мира навязывается использование проприетарного софта, в большинстве случаев которому имеются полностью свободные аналоги, все происходит от удалености и незнания, на это тратится уйма денег.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Agile in a Year&lt;/b&gt; Thomas Ferris Nicolaisen - доклад не про опен-сурс, а про технолигии которые доступны всем, про очень удобные методы разработки, про то как одна компания из Бонна внедряла Agile процессы на практике. Замечательный доклад, как мне показалось, очень много практических вещей было освещено, что было сложно что было легко, зачем и почему. Тема конечно же очень общирная, но тут все было представлено на одном конкретном примере. Допустим "... таким образом для выполнения основных фитч нашей программы мы выбрали систему канбан, и использовали ее напротяжении месяца". Говорилось про то, как в данной конторе пришли к идее ежедневных stand-up meetting, про переселения девелоперов, про функции менеджеров, про то что agile-master бесполезный человек и тп. Очень интересно. Многим компаниям хотя бы до таких мыслей расти и расти, а тут люди не боятся и экспериментируют. Не понравилось то что пример очень уж узкий вышел - компания состоит около 10-15 человек. Что понравилось: графическое представление презинтации, сразу же на все изображение выводятся все слайды, логические переходы между ними обозначены стрелочками, когда разговор переходит на какой-то слайд - тогда камера "приближается"/"перемещается" к нужному участку карты. Очень удобно понимать логику всего доклада, да и запоминается намного лучше, что и отокуда.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Inside the Cassandra distributed database&lt;/b&gt; Jonathan Ellis - доклад про не-реляционную БД &lt;a href="http://cassandra.apache.org/" target="_blank"&gt;cassandra&lt;/a&gt;. Я все время знал про существование таких бд, про то как их применять, и зачем они нужны, но у меня никогда не было причины задействовать такую БД. Это используется в facebook, в amazon s3, в twitter, в digg и т.д. Там где очень много данных, где данные однотипные и всегда растут в колличестве. После прослушивания доклада я наконец-то проникся идеей, это круто. Любое возможное представление, любые масштабы. Да сложно проектировать и программировать. Но если рассматривать такой тип БД как средство оптимизации - то есть уже полностью есть модель sql обычной базы, все отлажено, выставлены все нужные индексы и тп., но падает производительность на простых запросах, базу нужно перетаскивать на несколько машин и т.д. - тогда вполне реально перетащить базу на cassandra, переделать все sql запросы - и будет простое чудо. Докладчик рассказывал про внутреннее устройство, как происходит запись и чтение, что такое "ключ" в данной бд, как строится дата-кластер, какие возможности есть. Все очень понятно. Не понравилось то что в зале возникло много откровенно тупых вопросов по устройствам дата-кластеров, возможно это сложно для обычных людей. Мне очень понравилось.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Be lazy, make automation&lt;/b&gt; Fabrizio Manfredi - говорилось про то как построить свой собственный "умный" дом. Идея в основном такая: существуют девайсы которые можно купить (типа розетка с lan/wlan интерфейсом), поставить их в свой дом для освещения и т.п., потом настроить домашний софт на влючение/выключение по рассписанию (по cron-у), или на показания других датчиков (типа датчиков движения). Входишь в комнату - свет включается, выходишь - выключается. Нужно разогреть баню пока едешь в общественном транспорте? - подключаешься к домашнему серверу или отправляешь смс-ку. Кондиционер сам выключается когда открыты окна. Согласитесь, это круто. Докладчик объяснял как это все делается, на каких протоколах базируется система, как лучше строить архитектуру (от базового - провода или wifi, до сложного - распределенной системы), какие опен-сурсные проекты реализуют доступ к протоколам. Да это очень прикольно, но дорого, одна розетка стоит 10-20 евро, так как в ней стоит как минимум один чип. Плюс, вопрос секурити вообще не ставится (тупо твой сосед может выключить тебе свет). В общем я сильно проникся, но пока это мне не по карману, да и строить такое нужно только в собственном жилье.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Antifeatures&lt;/b&gt;, &lt;a href="http://en.wikipedia.org/wiki/Benjamin_Mako_Hill"&gt;Benjamin Mako Hill&lt;/a&gt; - тоже keynotes, тоже очень знаменитая персона (бог debian-а и gnu, как-никак), тоже очень интересно. Что сразу заметилось - очень быстрый и очень понятный английский. Человек тоже замечательный оратор, где нужно вставит конфузную шутку на предмет Windows и тп. Основные идеи: каким образом строится стоимость софта, за что реально приходится платить, какие минусы (не баги) может приносить использование софта, ну и собственно идеи GNU. Один из первых примеров - Windows NT Workstation и Windows NT Server - отличались в цене на 800 баксов, а фактически - одной записью в реестре. За что платится разница? во-во, наводит на мысли.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL Plugins - what are they?&lt;/b&gt; Sergei Golubchik - рассказ про еще один форк mysql, про то как в достаточно большой проект была впилена сисмета плагинов и расширений. Вообще говоря - ничего особенного по части проги я не услышал, все очень привычно. Ничего не скажу как это смотрится со стороны знатоков mysql, к коим я не отношусь. Запомнилось одно - английский от русского человека слышно за версту, это точно.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HiPHoP for PHP&lt;/b&gt; - докладчик - человек из комманды facebook, у них сейчас много опен-сурсных проектов. Что такое php я сам себе слабо представляю, попал на доклад случайно, но он мне понравился, я достаточно хорошо туда попал. Есть какая-то магия в том что делает facebook, во-первых если им не нравится php - они его исправляют и не стесняются, во-вторых - у них очень даже рационализаторский подход. Так как пхп - интерпретируемый язык и на фейсбуке сидят миллионы, то давайте перенесем код пхп на цпп. Все, на. Другими словами - сделаем свой интерпритатор пхп который будет рождать бинарный код. Вот и вся суть проекта. Да, есть проблемы со скоростью работы, удобством и т.п. Но это работает в разы быстрее и стабильнее! Я проникся.&lt;br /&gt;
&lt;blockquote&gt;Интересно заметить когда в зале возникла куча вопросов по поводу скорости, и компиляции докладчик просто свернул презинтацию, открыл консоль и показал что и как делается. Да реально, вся аудитория засекала ~20 секунд. Вот еще одна фишка it-конференций, можно продемострировать все за пару секунд. &lt;br /&gt;
&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GNOME 3&lt;/b&gt; Hendrik Richter - тут мало чего сказать, так как доклад был на немецком, да и решают только картинки. Могу сказать три вещи: это будет скоро, много добавится графических наворотов, они уже думают об GnomeOS.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;1, Qt, Profit!&lt;/b&gt; Daniel Molkentin - честно говоря этот доклад был расчитан на людей которые скажут "ой, а что такой qt?", по-этому особых эмоций у меня не возникло. В большей части было показано как обычное десктоп приложение портируется на symbian, "то что можно его отлаживать" ну и все. Жидко как-то, не уровень qt, это всяко.&lt;/li&gt;
&lt;/OL&gt;&lt;p&gt;Еще был &lt;b&gt;Social Event&lt;/b&gt; - огромная пьянка на открытом воздухе, я там был и мне понравилось. Особо нужно сказать как она проводилась. Есть собственная валюта конференции "Bon" - 1 Bon = 1 euro, пиво и еду можно покупать только за Bon-ы. Маленький стаканчик пива - 1 Bon, Большой стакан - 2 Bon + 1 Bon (fant), Мясо (ааа!) - 3 Bon. Вот там монжно было бухнуть много пива и заобщаться с любым человеком. Были награждения проектов (которые я собственно даже не видел), было файер-шоу, был большой торт... у, там было круто!!&lt;br /&gt;
&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Это все что я смог написать про это событие, многое еще крутится в голове, но изложить будет сложно. Выводы - таких конференций очень не хватает в России, очень не хватает конференций такого уровня (типа тут ставятся вопросы - что делать если у тебя дата-кластер на 1000 машин, а не банальное - как установить линукс новичку), просто не хватает аналогичных по духу событий. А организаторам FrOSCon - отдельное спасибо, все было просто круто!!&lt;br /&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-5468576809021451833?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/5468576809021451833/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=5468576809021451833' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5468576809021451833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5468576809021451833'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/08/froscon-2010.html' title='FrOSCon 2010'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-7492300639445040493</id><published>2010-08-26T19:33:00.000+07:00</published><updated>2010-08-26T19:33:28.548+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='linux fun'/><title type='text'>htc hero is not detected as drive</title><content type='html'>&lt;p&gt;Только что словил гигантский глюк андройда (htc hero), надо сказать первый за полгода работы. В коем веке понадобилось перекачать фотографии с телефона на комп. Втыкаю его в линуксовую машину, и ничего не происходит. Лампочка зарядки светится, но никаким другим образом комп не определяет подключенный к нему девайс. dmesg вообще ничего не сказал. Стандартная процедура: выключить, вытащить батарею, включить - не помогла. Глянул в гугле, народ старным образом колдует и после этого телефон определяется (типа прочистить usb разъем и тп). Ни один из этих вариантов не подошел.&lt;/p&gt;&lt;p&gt;Следующая мысль что все таки глючит ядрышко моего линукса. Благо совершенно случайно рядом оказалась виндовая машина. Но, на ней я опять таки ничего не увидел. Комп отказывался понимать что в usb разъем что-то вообще вставлено, как собственно и телефон. Шла только зарядка.&lt;/p&gt;&lt;p&gt;Что меня спасло: то что на борту стоял 2.1 андройд и было доступно обновление ядра до 3.32.411.2, после его установки, о чудо, телефон корректно стал определяться компом. Может быть кому это поможет&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-7492300639445040493?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/7492300639445040493/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=7492300639445040493' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/7492300639445040493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/7492300639445040493'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/08/htc-hero-is-not-detected-as-drive.html' title='htc hero is not detected as drive'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-2336315435488283537</id><published>2010-08-07T17:03:00.001+07:00</published><updated>2010-08-07T17:17:25.895+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><title type='text'>хроники django или homebudget part 6</title><content type='html'>&lt;p&gt;Давно не писал в хроники, а проект уже ушел далеко, так что сначала догоню версию блога до версии репозитория :) Сегодня будет html-css-javascript, делаем календарик для просмотра месяца.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Итак, у нас есть master page следующего содержания. Из отличий с предыдущим его описанием - добавил блок head, через который дочерние страницы могут добавлять скрипты и стили в html код страницы.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;content/css/main.css&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Homebudget site: {% block title %} manage yourself {% endblock %}&amp;lt;/title&amp;gt;
{% block head %}{% endblock %}
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;div id=&amp;quot;content&amp;quot;&amp;gt;
{% block content %}
{% endblock %}
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Еще есть убогий month view который выводит обычный список день-сумма.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;{% extends &amp;quot;purchases/master.html&amp;quot; %}

{% block title %} monthly view {% endblock %}

{% block content %}
&amp;lt;p&amp;gt;Date: {{view_date}} &amp;lt;/p&amp;gt;
{% if day_price_dict %}
    &amp;lt;ul&amp;gt;
        {% for key,value in day_price_dict.items|dictsort:&amp;quot;0&amp;quot; %}
            &amp;lt;li&amp;gt;
                {{key}} 
                &amp;lt;b&amp;gt;{{value}}&amp;lt;/b&amp;gt; 
                &amp;lt;a href=&amp;#39;/purchases/view/{{key.year}}/{{key.month}}/{{key.day}}/&amp;#39;&amp;gt; Edit &amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
{% else %}
    &amp;lt;p&amp;gt;No purchases yet&amp;lt;/p&amp;gt;
{% endif %}
&amp;lt;p&amp;gt;
    Total: &amp;lt;b&amp;gt;{{month_total}}&amp;lt;/b&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;a href=&amp;#39;/purchases/add/&amp;#39;&amp;gt;Add&amp;lt;/a&amp;gt;
{% endblock %}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Я хочу отобразить календарик. Самое простое решение - тупо таблица, ряды - недели, столбцы - дни недели. Но как сказал знакомый верстальщик "деревня, таблицы - это прошлый век, нужно использовать div-ную верстку". В чем она заключается, как я понял для себя, это выделение всех элементов в блоки, и задание их относительного расположения через стили. В итоге было подсмотрено решение в одном проекте. &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;В темплейте month-view заключаем список день-сумма в div с id-шником dashboard&lt;br /&gt;
&lt;pre class="brush: html;"&gt;&amp;lt;div id=&amp;quot;dashboard&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
...
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Создаем файлик dashboard.css в content/css, добавляем его в monthview темплейт.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;{% block head %}
&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;content/css/dashboard.css&amp;quot; /&amp;gt;
{% endblock %}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь все происходит в dashboard.css, сначала выставим фиксированную ширину для контейнера dashboard и уберем дефолтные стили списка с ul.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;#dashboard {
    width: 900px;
    display: inline;
    position: relative;
    float: left;
}
#dashboard ul {
    list-style: none outside none;
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Элементу списка (можно сказать элементу календаря) выставляем фиксированный размер - высоту и ширину. Размер вычисляем по формуле "ширина dashboard-а" разделить на 7, минус пара пикселей. Логика простая - в неделе 7 дней, по этому в контейнер должно помещаться не более 7 элементов. А плюс-минус пиксели, это нужно для учета бордеров элементов. А так же выставим float: left, этот магический параметр определяет с какой стороны будут располагаться соседние элементы. То есть для left следующий элемент выстроится слева от текущего. Если располагать некоторым абстрактным мышлением то можно понять к чему все это приведет. Правильно, к календарю.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;#dashboard li {
    float: left;
    position: relative;
    width: 120px;
    height: 120px;
    text-align: center;
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Можно посмотреть, да выглядит похоже, но не айс, так как дни не располагаются в соответствии с днями недели. Потому что в возвращаемом списке из вьюхи нет дней соседних месяцов, которые довершали недели. Не проблема добавить ;) Нужно в цикл обхода дат начинать от начала недели (не важно текущего или предыдущего месяца), ну и завершать аналогично. Дополнительно в возвращаемый словарь добавим значение что это за день: день текущего месяца, неактивного месяца, выходной, сегодня.&lt;br /&gt;
&lt;pre class="brush: python;"&gt;def month_view(request, year, month):
    """
    List all day in month, with daily sum
    """
    try:
        view_date = datetime.date(year=int(year), month=int(month), day=1)
    except:
        raise Http404
    from django.db.models import Q
    plist = Purchase.objects.filter(Q(purchase_date__year=view_date.year) &amp; Q(purchase_date__month=view_date.month)).order_by('purchase_date')

    y, m, d = view_date.timetuple()[:3]
    dt_next = datetime.date(year=y+((m+1)/12), month=(m+1)%12, day=d)
    day_price_dict = {}

    first_calend = view_date
    while first_calend.weekday() != 0:
        first_calend -= datetime.timedelta(days=1)
    dt = first_calend
    while dt &lt; view_date:
        day_price_dict[dt] = (Decimal('0.0'), 'prev_month')
        dt += datetime.timedelta(days=1)

    month_total = Decimal('0.0')
    while (dt &lt; dt_next):
        purchases_day = plist.filter(purchase_date__day=dt.day)
        day_total = Decimal('0.0')
        for it in purchases_day:
            day_total += it.price_total
        month_total += day_total
        weekend = dt == datetime.date.today() and 'today' or dt.weekday() &gt;= 5 and 'weekend' or 'day'
        day_price_dict[dt] = (day_total, weekend)
        dt += datetime.timedelta(days=1)

    last_calend = dt
    while last_calend.weekday() != 0:
        day_price_dict[last_calend] = (Decimal('0.0'), 'prev_month')
        last_calend += datetime.timedelta(days=1)

    return render_to_response('purchases/month_view.html', locals())
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь сделаем нормальное отображение дня (ячейки календаря). Для этого сначала выставим классы для элементов. sum для суммы дня, edit для линки перехода на день, date для числа календаря. Для самой ячейки нужно добавить класс дня, полученный в предыдущем пункте.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;&amp;lt;div id=&amp;quot;dashboard&amp;quot;&amp;gt;
    &amp;lt;ul&amp;gt;
        {% for key,value in day_price_dict.items|dictsort:&amp;quot;0&amp;quot; %}
            &amp;lt;li class=&amp;quot;{{value.1}}&amp;quot;&amp;gt;
                &amp;lt;span class=&amp;quot;date&amp;quot;&amp;gt;{{key.day|stringformat:&amp;quot;02i&amp;quot;}}&amp;lt;/span&amp;gt;
                {% ifnotequal value.1 &amp;#39;other_month&amp;#39; %}
                &amp;lt;span class=&amp;quot;sum&amp;quot;&amp;gt;{{value.0}}&amp;lt;/span&amp;gt; 
                {% endifnotequal %}
                &amp;lt;a class=&amp;quot;edit&amp;quot; href=&amp;#39;/purchases/view/{{key.year}}/{{key.month}}/{{key.day}}/&amp;#39;&amp;gt;Edit&amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;Потом обозначим для них стили отображения внутри ячейки. Выставим top-left-right-bottom, для этого понадобится position:absolute.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;#dashboard .date {
    font-size: 60px;
    position: absolute;
    left: 10px;
    top: 10px;
}
#dashboard .sum {
    font-weight: bold;
    bottom: 5px;
    right: 5px;
    position: absolute;
    font-size: 20px;
}
#dashboard .edit {
    position: absolute;
    right: 5px;
    top: 5px;
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь мы займемся дизигнерством. То есть разукрасим календарь. Так как необходимые навыки отсутствуют напрочь, и вообще мы дальтоники, то спиздим. Открыл для себе &lt;a href="http://www.colorcombos.com/"&gt;клевый сайт&lt;/a&gt; на котором можно выбрать комбинацию сочетающихся цветов. Мой выбор &lt;a href="http://www.colorcombos.com/color-scheme-275.html"&gt;такой&lt;/a&gt;. В соотвествии этому выставим цвета для всех элементов.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;#dashboard li {
    background: none repeat scroll 0 0 #D5E1DD;
    border: 1px solid white;
    font-color: white;
    cursor: pointer;
}
#dashboard .prev_month {
    background: none repeat scroll 0 0 #747e80;
}
#dashboard .weekend {
    background: none repeat scroll 0 0 #F7F3E8;
}
#dashboard .today {
    background: none repeat scroll 0 0 #F2583E;
}
#dashboard .hover {
    background: none repeat scroll 0 0 #77BED2;
}
#dashboard .date {
    color: white;
}
#dashboard .sum {
    color: #2B3E42;
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Ну и последние штрихи. При наведении на день курсора будем менять цвет ячейки. Для этого понадобится клиентский javascript. Будем использовать мега рулезную библиотеку &lt;a href="jquery.com"&gt;jQuery&lt;/a&gt;. Есть несколько способов ее подключить.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Самый простой и стандартный. Скачать себе jquery, и добавить в статические ресурсы сайта. Использовать можно следующим образом (добавляем в head):&lt;br /&gt;
&lt;pre class="brush: html;"&gt;&amp;lt;script src=&amp;quot;content/common/js/jquery-1.4.2.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Использовать гугл-апи. Способ матер и говорят для оптимизации кеширования само оно, но для себя я отметил следующий минус - для локальной разработки он тормозит сильно. Да и кол-во запросов увеличивается. В общем я от него отказался в пользу первого. Использовать можно следующим образом (добавляем в head):&lt;br /&gt;
&lt;pre class="brush: html;"&gt;&amp;lt;script src=&amp;quot;http://www.google.com/jsapi&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
    google.load(&amp;quot;jquery&amp;quot;, &amp;quot;1.4.2&amp;quot;);
&amp;lt;/script&amp;gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Что-нить еще, типа подключить с какого-нить хостинга.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Заполучив jQuery можно решить задачу с изменением цвета под курсором.&lt;br /&gt;
&lt;pre class="brush: html;"&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
$(document).ready(function(){
    $(&amp;#39;#dashboard li&amp;#39;).bind(&amp;quot;mouseenter&amp;quot;, function() {
        var pthis = $(this)
        pthis.addClass(&amp;#39;hover&amp;#39;)
    });
    $(&amp;#39;#dashboard li&amp;#39;).bind(&amp;quot;mouseleave&amp;quot;, function() {
        var pthis = $(this)
        pthis.removeClass(&amp;#39;hover&amp;#39;)
    });
});
&amp;lt;/script&amp;gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Посты по теме: &lt;ul&gt;&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/04/django-homebudget-part-1.html"&gt;хроники django или homebudget part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-2.html"&gt;хроники django или homebudget part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-3.html"&gt;хроники django или homebudget part 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html"&gt;хроники django или homebudget part 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-5.html"&gt;хроники django или homebudget part 5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-2336315435488283537?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/2336315435488283537/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=2336315435488283537' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2336315435488283537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2336315435488283537'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/08/django-homebudget-part-6.html' title='хроники django или homebudget part 6'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-2848064815787714367</id><published>2010-07-24T03:43:00.001+07:00</published><updated>2010-07-24T03:45:10.884+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux fun'/><title type='text'>compiz headtracking</title><content type='html'>&lt;p&gt;Моя цитата из твиттера: "собрал эту игрушку, забавно и бесполезно, но кайфа - вагон! хочу большой монитор! &lt;a href="http://www.youtube.com/watch?v=kTNG1GN4VV8"&gt;http://www.youtube.com/watch?v=kTNG1GN4VV8&lt;/a&gt;"&lt;/p&gt;&lt;p&gt;Итак вдохновившись... Теперь о том как она ставилась&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Хочу предупредить: у меня ubuntu 10.04, тачка домашне-девелоперская, много на ней чего стоит, так что кой-чего может и не оказаться в вашей системе. Не стоит бояться, как только проблема - сразу же внимательно читаем ошибку и ищем недостающий пакет. В общем, стоит начать с того что -  обновляем систему. Главная цель - получить compiz-fusion 0.8+&lt;/li&gt;
&lt;li&gt;Очень бодро качаем сам headtracking плагин, и смотрим на то как он не собирается.&lt;br /&gt;
&lt;pre class="brush: python;"&gt;mkdir projects/headtrack
cd projects/headtrack/
git clone --depth 1 git://anongit.compiz-fusion.org/users/klange/headtracking
cd headtracking/
make&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Говорит что-то про bcop, быстро находим в репозитории&lt;br /&gt;
&lt;pre class="brush: python;"&gt;apt-cache search compiz-fusion-bcop
sudo apt-get install compiz-fusion-bcop&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Совершенно аналогичным методом разрешаем остальные зависимости.&lt;br /&gt;
&lt;pre class="brush: python;"&gt;sudo apt-get install compiz-dev libcompizconfig0-dev libtool libtool highgui libhighgui-dev
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Примерно где-то здесь начнется проблема с opencv. &lt;b&gt;ВАЖНО&lt;/b&gt;: не стоит искать в репозитории libcv - хотя она там и есть, но увы не заработает, очень старая сборка, проверено. Это была моя большая ошибка, не повторяйте ее. &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Собираем руками последнюю версию opencv из репозитория:&lt;br /&gt;
&lt;pre class="brush: python;"&gt;mkdir projects/opencv
cd projects/opencv
svn co https://opencvlibrary.svn.sourceforge.net/svnroot/opencvlibrary/tags/latest_tested_snapshot
cd latest_tested_snapshot/opencv
mkdir release
cd release/
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_PYTHON_SUPPORT=ON ..
make
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;После того как &lt;strike&gt;вы сварили и попили чаю&lt;/strike&gt; собралась opencv, устанавливаем ее в систему.&lt;br /&gt;
&lt;pre class="brush: python;"&gt;sudo make install
sudo vim /etc/ld.so.conf.d/opencv.conf
sudo ldconfig -v
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь финально билдим headtracking и устанавливаем его в систему:&lt;br /&gt;
&lt;pre class="brush: python;"&gt;cd projects/headtracking
make clean
make
sudo make install
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь идем в настройки compiz-а, жмем клавишу extraz, там headtracking. Включаем его. Окошки видоизменились. Нажимаем галку - General/Webcam head track. Теперь если веб-камера включилась и изображение поплыло в какую-то сторону, то все правильно сделано. Можно наслаждаться. Если нет и камера не включилась - что-то значит у вас не по фен-шею или чего еще, типа генетики. А скорее всего тупо web-камера не совместима с video4linux, чего и советую проверять :)&lt;/li&gt;
&lt;li&gt;Когда у вас заболит шея (как у меня), можно поинтересоваться как это все работает. Для ленивых хинт:&lt;br /&gt;
&lt;pre class="brush: python;"&gt;cd projects/opencv/latest_tested_snapshot/opencv/samples/c/
chmod +x build_all.sh 
./facedetect
&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-2848064815787714367?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/2848064815787714367/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=2848064815787714367' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2848064815787714367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2848064815787714367'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/07/compiz-headtracking.html' title='compiz headtracking'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-4568367384217021544</id><published>2010-06-27T15:06:00.007+07:00</published><updated>2010-08-07T18:11:14.231+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><title type='text'>хроники django или homebudget part 5</title><content type='html'>&lt;p&gt;За посленее время много чего натворил в проекте, попытаюсь все изложить.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;довел до ума систему тегов&lt;/li&gt;
&lt;li&gt;добавил &lt;a href="http://south.aeracode.org/"&gt;South&lt;/a&gt; к проекту для удобной работы&lt;/li&gt;
&lt;li&gt;переделал логику добавления/редактирования покупок &lt;/li&gt;
&lt;li&gt;сделал &lt;em&gt;красивое&lt;/em&gt; отображение месяца&lt;/li&gt;
&lt;/UL&gt;&lt;p&gt;Так судя по всему, первые два пункта получились достаточно обширными, так что сегодня только про них. HTML и javascript останутся на завтра. &lt;p&gt;&lt;h4&gt;South - удобный процесс миграции БД &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;для начала я обновил себе django до последней дев-сборки (в репозиториях убунты все-то лежит 1.1).  &lt;pre class="brush: python;"&gt;sudo apt-get remove python-django
cd ~/devtools
svn co http://code.djangoproject.com/svn/django/trunk/ django
cd django
python setup.py install&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;стоит проверить что django проекты не потеряли работоспособность (:&lt;/li&gt;
&lt;li&gt;Устанавливаем south (для этого нужен mercurial) &lt;pre class="brush: python;"&gt;cd ~/devtools
hg clone http://bitbucket.org/andrewgodwin/south/
cd south
sudo python setup.py install&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Добавляем south в installed_app (в settings.py) &lt;pre class="brush: python;"&gt;INSTALLED_APPS = (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'south',
)&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;для параноиков - backup/restore существующей бд.  &lt;ul&gt;&lt;li&gt;Backup:  &lt;pre class="brush: python;"&gt;sudo -u databaseuser pg_dump homebudget &amp;gt; dump_db&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Restore: &lt;pre class="brush: python;"&gt;sudo -u postres psql homebudget2 &amp;lt; dump_db&lt;/pre&gt;&lt;/li&gt;
&lt;/UL&gt;&lt;/li&gt;
&lt;li&gt;создаем таблицы для south в проекте (это последний раз когда мы сделали syncdb(: )  &lt;pre class="brush: python;"&gt;./manage.py syncdb&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;теперь если бд пустая (т.е. таблицы для проектов не созданы), добавляем проекты в installed_apps и выполняем  &lt;pre class="brush: python;"&gt;./manage.py schemamigration purchases --initial
./manage.py migrate purchases
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;если данные в ней уже были, то: создаем начальную точку миграции  &lt;pre class="brush: python;"&gt;./manage.py schemamigration purchases --initial
&lt;/pre&gt;исправляем скрипт создания (комментим все строки с созданием таблиц)  &lt;pre class="brush: python;"&gt;purchases/migration/0001_initial.py
&lt;/pre&gt;накатываем "псевдо" изменения  &lt;pre class="brush: python;"&gt;./manage.py migrate purchases
&lt;/pre&gt;теперь закоменченные строки можна убрать - для истории &lt;/li&gt;
&lt;li&gt;основной сценарий работы c South &lt;ul&gt;&lt;li&gt;делаем изменения в модели&lt;/li&gt;
&lt;li&gt;создаем автоматическую точку миграции  &lt;pre class="brush: python;"&gt;./manage.py schemamigration purchases --auto
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;проверяем сгенеренный код в purchases/migration/XXXX_randomname.py&lt;/li&gt;
&lt;li&gt;если что-то не так - правим код в методах backward и forward&lt;/li&gt;
&lt;li&gt;в объекте orm находится замечательная django orm :) &lt;pre class="brush: python;"&gt;allpurchases = orm.Purchase.objects.all()
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;накатывание изменений на БД  &lt;pre class="brush: python;"&gt;./manage.py migrate purchases&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;посмотреть все миграции (звездочками помечены те которые успешно прошли) &lt;pre class="brush: python;"&gt;./manage.py migrate purchases --list&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;если хочется смигрировать только данные &lt;pre class="brush: python;"&gt;./manage.py datamigration purchases my_super_migration_of_data&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;или можно даже вот так &lt;pre class="brush: python;"&gt;./manage.py schemamigration purchases --empty my_super_migration_of_data&lt;/pre&gt;&lt;/li&gt;
&lt;/UL&gt;&lt;/li&gt;
&lt;/OL&gt;&lt;h4&gt;Таги &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;как уже &lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html"&gt;упоминалось ранее&lt;/a&gt; таги были добавлены самопальные. Работают примерно так - в модели Purchase есть поле ManyToMany к модели Tag. Дефолтная реализация ManyToMany откидывается. В форму насильно добавляется новое CharField, переопределяются методы загрузки и сохранения формы, в которых как раз идут операции с полем m2m и CharField. Все делается в ручную. Это не никак не нравилось, так как чтобы переиспользовать эти таги пришлось бы прибегать к copy-paste, что не есть хорошо. В общем решил продолжить поиски компонент:&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/django-tagging/"&gt;django-tagging&lt;/a&gt; - жутко наворочанный пакет, вроде бы есть все что нужно, все очень кастомно. но того как оно сделано я вряд ли скоро пойму. много-много черной магии для выполнения простых операций. много, очень много sql-я (без orm-а). Сразу же был откинут.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/alex/django-taggit"&gt;django-taggit&lt;/a&gt; проект поменьше чем предыдущий. но черная магия опять таки присутствует. в некоторых моментах я тупо не разобрался. &lt;/li&gt;
&lt;li&gt;&lt;a href="http://softwaremaniacs.org/soft/tagsfield/"&gt;tagsfield&lt;/a&gt; от Ивана Сагалаева. Вот оно то что нужно. Идея проста - наследуемся от ManyToManyField и заменяем дефолтный widget на собственный. Кода ну очень мало, все предельно понятно. Выбрал я его. Конечно же понадобились изменения.&lt;/li&gt;
&lt;/UL&gt;&lt;/li&gt;
&lt;li&gt;первым делом копируем к себе tagsfield, добавляем его в installed_apps&lt;/li&gt;
&lt;li&gt;изначально проект работает так: в html записывается список всех тагов и список всех тагов объекта, через javascript все это управляется - юзер может добавлять и убирать теги обекта, может добавлять новые. Когда форма сабмитится список активных тагов сохраняется в поле m2m.&lt;/li&gt;
&lt;li&gt;для моего подхода (&lt;em&gt;de.li.cious style&lt;/em&gt; таги) было сделано следующее&lt;/li&gt;
&lt;li&gt;поле MultipleHiddenInput было заменено на TextInput. Поясняю - при сабмите приходил список значений hidden-input-ов, а мне нужно только одно text-input поле.  &lt;pre class="brush: python;"&gt;class TagsFormField(forms.Field):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text_widget = forms.TextInput&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;собственно загрузка списка тагов (метод render):  &lt;pre class="brush: python;"&gt;'value_tags': " ".join(value_tags)&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;из модели убрал поле created и переименовал модель в BaseTag (зачем - ниже)  &lt;pre class="brush: python;"&gt;class BaseTag(models.Model):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value = models.CharField(max_length=50)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;norm_value = models.CharField(max_length=50, editable=False)&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;сохранение: &lt;pre class="brush: python;"&gt;def save_form_data(self, instance, data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;splitted = data.strip().split(' ')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tags = [self._get_tag(value) for value in splitted]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setattr(instance, self.attname, tags)&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;шаблон виджета: &lt;pre class="brush: html;"&gt;&amp;lt;div id="{{ id }}" class="tagsfield"&amp;gt;
&amp;lt;input type="text" name="{{ name }}" value="{{ value_tags }}" /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;удалил больше не нужные файлы и зачистил js и css файлы. Еще были изменения, но все незначительные, так что лучше их смотреть в &lt;a href="http://code.google.com/p/homebudget-site/source/browse/trunk/"&gt;репозитории&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;теперь об одном ограничении оригинального кода - все таги хранятся в одной таблице. То есть если навешать таги на два класса - таги у них будут общие. Плохо. Для решения использовал еще один уровень абстракции, в приложении Purchases создал новый класс PurchaseTag который является наследником BaseTag. Теперь в бд будет две таблицы - одна для BaseTag из приложения TagField и одна для PurchaseTag из приложения Purchase. Таги для них не пересекаются :) Ну и собственно TagField одинаково хорошо работает для PurchaseTag класса. Тем более в последствии можно кастомизировать PurchaseTag и добавить к нему дополнительные поля. В файле purchases/models.py (старый класс Tag уже затерт)  &lt;pre class="brush: python;"&gt;from homebudget.tagsfield import fields
from homebudget.tagsfield.models import BaseTag
class Purchase(models.Model):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;... 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tags = fields.TagsField('PurchaseTag')

class PurchaseTag(BaseTag):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass

class PurchaseItemForm(ModelForm):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class Meta:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model = Purchase
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exclude = ('purchase_date',)
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Ну и в коде везде где обращались к Tag теперь обращаемся к PurchaseTag, ну и из мелочи - поле name изменилось на value. &lt;/li&gt;
&lt;li&gt;Как проходила миграция данных (замена старых таго на новые). У меня база уже забита. Собственно для этого и прикручивался South (см. выше)&lt;/li&gt;
&lt;li&gt;Сначала была таблица для приложения TagField (чтобы не было никаких проблем)&lt;/li&gt;
&lt;li&gt;Потом было добавлена модель PurchaseTag в проект Purchases, в модель Purchase было добавлено поле ntags = fields.TagsField('PurchaseTag'), старое поле tags = models.ManyToManyField('Tag') не удаляя.&lt;/li&gt;
&lt;li&gt;Первая попытка миграции. Не работает (!!) и South отправляет на страницы &lt;a href="http://south.aeracode.org/wiki/MyFieldsDontWork"&gt;http://south.aeracode.org/wiki/MyFieldsDontWork&lt;/a&gt; &lt;a href="http://south.aeracode.org/docs/customfields.html#extending-introspection"&gt;http://south.aeracode.org/docs/customfields.html#extending-introspection&lt;/a&gt;. Типа дописывайте сами себе правила на собственные поля. Ну ведь использовано поле отнаследованное от m2m, значит и вести оно должно себя также.. Значит спиздим определение ManyToManyField из кода south-а &lt;pre class="brush: python;"&gt;rules = [
(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(TagsField,),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"to": ["rel.to", {}],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"symmetrical": ["rel.symmetrical", {"default": True}],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"related_name": ["rel.related_name", {"default": None}],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"db_table": ["db_table", {"default": None}],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"through": ["rel.through", {"ignore_if_auto_through": True}],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},
),
]
from south.modelsinspector import add_introspection_rules
add_introspection_rules(rules, ["^homebudget\.tagsfield\."])&lt;/pre&gt;Поставим это прямо возле определения TagsField-а.&lt;/li&gt;
&lt;li&gt;Теперь создание точки миграции должно пройти успешно. Теперь у нас есть два поля тагов в модели. Делаем миграцию данных. Для каждого старого тега добавляем новый, и повторяем все соотношения. В общем выглядит это так:  &lt;pre class="brush: python;"&gt;class Migration(DataMigration):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def forwards(self, orm):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plist = orm.Purchase.objects.all()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for p in plist:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_tags = p.tags.all()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in old_tags:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;norm_value = i.name.lower()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tag, created = orm.PurchaseTag.objects.get_or_create(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;norm_value = norm_value,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;defaults = {'value': i.name}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p.ntags.add(tag)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p.tags.remove(i)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p.save()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_tags = orm.Tag.objects.all()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in old_tags:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i.delete()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def backwards(self, orm):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Write your backwards methods here."
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plist = orm.Purchase.objects.all()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for p in plist:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_tags = p.ntags.all()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in old_tags:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tag, created = orm.Tag.objects.get_or_create(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name = i.value,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p.tags.add(tag)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p.ntags.remove(i)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p.save()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_tags = orm.PurchaseTag.objects.all()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in old_tags:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i.delete()&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь удаляем модель Tag (старую) и поле tags. Апдейтим базу через South. &lt;/li&gt;
&lt;li&gt;Все хорошо, только поле называется ntags... Переименовываем поле. Смотрим в скрипт сгенеренный South-ом - не порядок, он пытается удалить нашу таблицу и создать новую. Исправляем его ошибку и накатываем изменения  &lt;pre class="brush: python;"&gt;class Migration(SchemaMigration):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def forwards(self, orm):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db.rename_table('purchases_purchase_ntags', 'purchases_purchase_tags')

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def backwards(self, orm):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db.rename_table('purchases_purchase_tags', 'purchases_purchase_ntags')&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь все окей :)&lt;/li&gt;
&lt;/OL&gt;&lt;p&gt;Посты по теме: &lt;ul&gt;&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/04/django-homebudget-part-1.html"&gt;хроники django или homebudget part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-2.html"&gt;хроники django или homebudget part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-3.html"&gt;хроники django или homebudget part 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html"&gt;хроники django или homebudget part 4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-4568367384217021544?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/4568367384217021544/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=4568367384217021544' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4568367384217021544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4568367384217021544'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-5.html' title='хроники django или homebudget part 5'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-5019129562602016996</id><published>2010-06-14T13:41:00.004+07:00</published><updated>2010-08-07T18:10:52.718+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><title type='text'>хроники django или homebudget part 4</title><content type='html'>Выдалось время и появилось настроение. Чего-то нового сваял :)  &lt;ol&gt;&lt;li&gt;Первой целью стояло добавление тагов. Очень полезная вещь для организации однотипных данных. Посмотрел что предлагает интернет на этот вопрос. Есть проект &lt;a href="http://code.google.com/p/django-tagging/"&gt;django-tagging&lt;/a&gt;, типа все готовое прямо из коробки под джангу. Не понравился по нескольким причинам - я не понял как он работает (какие-то мега-архи приемы используются для управления тагами, типа прямых запросов к бд), куча не нужных мне наворотов, находится он бете и имеет баг лист, да и народ от него не в восторге. Вообще вопрос построения тагов теоретически выглядит довольно простым - так что было решено отказаться и самому написать простенькое решение, для целей самообучения имхо лучше. Когда понадобится облако тагов - я подумаю в сторону перехода на сей &lt;i&gt;стандартный&lt;/i&gt; плагин. Мое требование такое - чтобы было одно поле на форме, в котором записаны все таги итема через пробел, при сохранении это поле split-ится по пробелам, и к итему добавляются/убираются теги. Как узналось в процессе - это &lt;i&gt;de.li.cious style&lt;/i&gt; таги.&lt;/li&gt;
&lt;li&gt;Отправной точкой является модель тага &lt;pre class="brush: python;"&gt;class Tag(models.Model):
    name = models.CharField('keyword, bookmark or term', max_length=100)
&lt;/pre&gt;и отношение many-to-many к модели purchase &lt;pre class="brush: python;"&gt;class Purchase(models.Model):
    ...
    tags = models.ManyToManyField('Tag')
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Обновляем схему базы. У нас появляется новая таблица Тагов, и таблица отношения таг-покупка (по foreign-ключам из тагов и покупок). Нужно заметить что таблица PurchaseItem не меняется, значит все правильно.&lt;/li&gt;
&lt;li&gt;Таким образом получилась следующая штука, которую лучше всего опробовать на shell-е: &lt;pre class="brush: python;"&gt;from homebudget.purchases.models import Purchase, Tag
t = Tag(name='beer')
t.save()
p = Purchase.objects.filter(name=u'туборг')[0]
p.tags.add(t)
p.save()
[i.name for i in p.tags.all()]
[i.name for i in t.purchases_set.all()]
&lt;/pre&gt;Нужно заметить, что пока объект Purchase не материализован (не сохранен) в базе, у него нет поля tags. И наоборот, пока Tag не создан в базе у него нет поля purchases_set. &lt;/li&gt;
&lt;li&gt;Если сейчас оставить все как есть и посмотреть что находится на вьюхах добавления и редактирования, то может стать плохо. Так как я использовал ModelForm для генерации html-формы для Purchase - для всех полей подставилась стандартная реализация. И для поля ManyToManyField она тоже есть, и выглядит прям отвратительно (тупой селект). Минусы - совершенно не подходит под мое представление, и добавить новый тег - нужна отдельная форма. Так что нах стандарты!&lt;/li&gt;
&lt;li&gt;В PurchaseItemForm добавляем новое &lt;i&gt;собственное&lt;/i&gt; поле, и удаляем из представление оригинальное поле tag модели PurchaseItem &lt;pre class="brush: python;"&gt;from django.forms import CharField
class PurchaseItemForm(ModelForm):
    ...
    tags_inner = CharField()
    ...
    class Meta:
        model = Purchase
        exclude = ('tags',)
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;Правильное&lt;/i&gt; поле есть, но оно никак не обрабатывается. Сначала мы его заполним данными. Как мне кажется - лучше всего это сделать через override функции __init__ формы. Сначала вызываем родной __init__ (в котором как мы сделали - уже нет оригинального поля tags), потом заполняем &lt;i&gt;собственное&lt;/i&gt; поле tags_inner тагами, разделенными пробелами. Проверка на null-ы нужна как раз для создания и редактирования (пока объект Purchase не создан, у него нет поля tags). &lt;pre class="brush: python;"&gt;class PurchaseItemForm(ModelForm):
    ...
    def __init__(self, *args, **kwargs):
        super(PurchaseItemForm, self).__init__(*args, **kwargs)
        if kwargs.has_key('instance'):
            inst = kwargs['instance']
            if inst is not None and inst.pk is not None:
                self.fields["tags_inner"].initial = " ".join([i.name for i in inst.tags.all()])
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Теперь сохранение. Я выбрал решение через override функции save (которая вызывается при сохранении данных формы из вьюхи). Сначала - обязательно вызываем базовый метод (чтобы объект Purchase наверняка сохранился в базе), потом - вытаскиваем из cleaned_data значения поля tags_inner. Далее понятные операции - split по пробелам,  получаем все таги буквенно, потом создаем новые, удаляем несуществующие, и сохраняем финально. &lt;pre class="brush: python;"&gt;def save(self, *args, **kwargs):
        inst = super(PurchaseItemForm, self).save(*args, **kwargs)
        tags = self.cleaned_data["tags_inner"].strip().split(' ')
        for tag in tags:
            if len(Tag.objects.filter(name=tag))==0:
                new_tag = Tag(name=tag)
                new_tag.save()
            if len(inst.tags.filter(name=tag))==0:
                inst.tags.add(Tag.objects.get(name=tag))
        for tag in inst.tags.all():
            if tag.name not in tags:
                inst.tags.remove(tag)
        inst.save()
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Ну вроде бы с реализацией все - можно пробовать. Какие минусы сейчас есть: отсутствует валидация, отсутствует зачистка тагов к которым не привязан не один итем,  поле tags теперь обязательное для заполнения, решение деревянное и не гибкое. Запишу в todo :) А пока нужно наполнить тагам базу. &lt;/li&gt;
&lt;li&gt;После того как база заполнена можно сделать вьюху для просмотра статистики по тагам. Во views.py добавляем purchases_by_tag &lt;pre class="brush: python;"&gt;def purchases_by_tag(request, tag_name):
    try:
        tag = Tag.objects.get(name=tag_name)
    except:
        raise Http404
    total = Decimal('0.0')
    for item in tag.purchase_set.all():
        total += item.price_total
    return render_to_response('purchases/tag_view.html', {
        'total': total,
        'tag': tag,
    })
&lt;/pre&gt;Добавляем новый темплейт tag_view.html &lt;pre class="brush: html;"&gt;&amp;lt;p&amp;gt;Tag: {{tag.name}}&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
    {% for item in tag.purchase_set.all %}
        &amp;lt;li&amp;gt;
            {{item.name}}
            &amp;lt;b&amp;gt;{{item.price_total}}&amp;lt;/b&amp;gt;
            {{item.purchase_date}}
            &amp;lt;a href=&amp;#39;/purchases/edit/{{item.pk}}/&amp;#39;&amp;gt;Edit&amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
    {% endfor %}
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;Count: {{tag.purchase_set.count}}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Total: {{total}}&amp;lt;/p&amp;gt;
&lt;/pre&gt;Добавляем новое правило урлов в urls.py: &lt;pre class="brush: python;"&gt;urlpatterns = patterns(
    ...
    (r'^purchases/tags/(?P&lt;tag_name&gt;\w+)/$', 'purchases_by_tag'),
)
&lt;/pre&gt;Теперь по ссылке http://127.0.0.1:8000/purchases/tags/beer/ можно узнать когда, и на какую в сумму мы выпили пива :) &lt;/li&gt;
&lt;li&gt;Можно навести красивости, типа в daily view добавить в каждую строчку таги связанные с покупкой, типа  &lt;pre class="brush: html;"&gt;Tags:
   {% for tag in p.tags.all %}
   &amp;lt;span&amp;gt;{{tag.name}}&amp;lt;/span&amp;gt;
   {% endfor %}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Для реализации тагов мне в один момент понадобился дебаг, использовал для этого модуль logging, очень удобный надо сказать, вот так его заставить работать (output идет в консоль локального сервера, что архи удобно): &lt;pre class="brush: python;"&gt;import logging
logging.basicConfig(
    level = logging.DEBUG,
    format = '%(asctime)s %(levelname)s %(message)s',
)
&lt;/pre&gt;а вот так легко использовать: &lt;pre class="brush: python;"&gt;logging.debug(p.tags)
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Еще по мелочам, из области рефакторинга, наткнулся на мега функцию locals - она возвращает все локальные переменные из области видимости, в виде словаря 'имя переменной'-'значение'. Это позволило значительно сократить код на render_to_response, до: &lt;pre class="brush: python;"&gt;return render_to_response('purchases/month_view.html', {
    'view_date': view_date,
    'dict': day_price,
    'total': month_total,
})
&lt;/pre&gt;после: &lt;pre class="brush: python;"&gt;return render_to_response('purchases/month_view.html', locals())
&lt;/pre&gt;И это в каждой вьюхе, очень удобно. Конечно же пришлось поправить все темплейты и вьюхи на предмет именования переменных, но так все таки намного лучше. &lt;/li&gt;
&lt;li&gt;На последок добавил Master Page - по крайней мере так оно обзывается в ASP.NET. Нужно это для двух вещей - чтобы все страницы имели общую часть - в мастере, и чтобы меньше писать кода. После коротких поисков нашел что это в джанге реализуется через наследование темплейтов &lt;a href="http://docs.djangoproject.com/en/dev/topics/templates/#template-inheritance"&gt;читать тут&lt;/a&gt;. В общем по простому: добавил master.html &lt;pre class="brush: html;"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;
&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;content/css/main.css&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;Homebudget site: {% block title %} manage yourself {% endblock %}&amp;lt;/title&amp;gt;
{% block head %}{% endblock %}
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;lt;div id=&amp;quot;content&amp;quot;&amp;gt;
{% block content %}
{% endblock %}
&amp;lt;/div&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;Теперь он будет у меня &lt;i&gt;основным&lt;/i&gt; шаблоном. Любой другой шаблон может его расширять и дополнять. Все элементы block - можно использовать в дочернем шаблоне. Пока я полностью не разобрался, но то что есть сейчас меня устраивает. Вот например новый шаблон tag_view.html &lt;pre class="brush: html;"&gt;{% extends &amp;quot;purchases/master.html&amp;quot; %}

{% block title %} view purchases by tag {% endblock%}

{% block content %}
&amp;lt;p&amp;gt;Tag: {{tag.name}}&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
    {% for item in tag.purchase_set.all %}
        &amp;lt;li&amp;gt;
            {{item.name}}
            &amp;lt;b&amp;gt;{{item.price_total}}&amp;lt;/b&amp;gt;
            {{item.purchase_date}}
            &amp;lt;a href=&amp;#39;/purchases/edit/{{item.pk}}/&amp;#39;&amp;gt;Edit&amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
    {% endfor %}
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;Count: {{tag.purchase_set.count}}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Total: {{total}}&amp;lt;/p&amp;gt;
{% endblock %}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Кстати говоря, в мастере используется ссылка на css-документ, он должен лежать в папке обозначенной в конфигурации как MEDIA_ROOT, и для этого пути должно быть правило в урл-реврайтере... Для чего оно нужно? чтобы в можно было вынести весь статический контент в отдельный сайт, необремененный обработчиками джанги и тп.  В settings.py: &lt;pre class="brush: python;"&gt;MEDIA_ROOT = '/home/username/projects/homebudget/content/'
...
MEDIA_URL = '/content/'
&lt;/pre&gt;И в urls.py &lt;pre class="brush: python;"&gt;from django.conf import settings
if settings.LOCAL_DEVELOPMENT:
    urlpatterns += patterns("django.views",
        url(r"%s(?P&lt;path&gt;.*)/$" % settings.MEDIA_URL[1:], "static.serve", { "document_root": settings.MEDIA_ROOT, })
    )
&lt;/pre&gt;Переменная LOCAL_DEVELOPMENT заведена на будующее. &lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Посты по теме: &lt;ul&gt;&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/04/django-homebudget-part-1.html"&gt;хроники django или homebudget part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-2.html"&gt;хроники django или homebudget part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-3.html"&gt;хроники django или homebudget part 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-5.html"&gt;хроники django или homebudget part 5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-5019129562602016996?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/5019129562602016996/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=5019129562602016996' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5019129562602016996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5019129562602016996'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html' title='хроники django или homebudget part 4'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-8570095232404493891</id><published>2010-05-16T14:54:00.004+07:00</published><updated>2010-06-27T16:54:57.526+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><title type='text'>хроники django или homebudget part 3</title><content type='html'>&lt;p&gt;Этот мини проект обзавелся public репозиторием на &lt;a href="http://code.google.com/p/homebudget-site/"&gt;google code&lt;/a&gt;. Выбрал я его из-за встроенного баг-трека и вики. По большому счету лучше подошел бы launchpad, но с гуглокодом я уже работал и более-менее там освоился. Из минусов - репозиторий svn, а у меня локально все ведется в bzr. Ну это не беда, &lt;a href="http://alexadotcpp.blogspot.com/2010/01/bzrsvn-friends.html"&gt;пройденный этап&lt;/a&gt; ;) &lt;/p&gt;
&lt;p&gt;Из последних приготовлений кода к первому коммиту - были убраны все пароли и пути из settings.py, убирал примерно следующим образом&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Создал файл в домашней директории .homebudget-settings со следующим содержанием:
&lt;pre class="brush: python;"&gt;
DATABASE_NAME = 'homebudget'
DATABASE_USER = 'username'
DATABASE_PASSWORD = 'password'
DATABASE_HOST = 'localhost'
DATABASE_PORT = '1234'

MEDIA_ROOT = '/home/username/projects/homebudget/content/'

SECRET_KEY = '-thisismysecretkey!'
TEMPLATE_DIRS = (
    '/home/username/projects/homebudget/templates',
)
LOCAL_DEVELOPMENT = True
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;В файле settings.py потер значения одноименных переменных, примерно так
&lt;pre class="brush: python;"&gt;
DATABASE_NAME = ''
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;В конец файла settings.py была добавлены следующие строчки:
&lt;pre class="brush: python;"&gt;
from os.path import expanduser
execfile(expanduser('~/.homebudget-settings'))
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Проект готов к выкладыванию на публику :)
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Посты по теме:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/04/django-homebudget-part-1.html"&gt;хроники django или homebudget part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-2.html"&gt;хроники django или homebudget part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html"&gt;хроники django или homebudget part 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-5.html"&gt;хроники django или homebudget part 5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-8570095232404493891?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/8570095232404493891/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=8570095232404493891' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/8570095232404493891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/8570095232404493891'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-3.html' title='хроники django или homebudget part 3'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-1643046160306762394</id><published>2010-05-10T22:42:00.006+07:00</published><updated>2010-06-27T16:55:19.558+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><title type='text'>хроники django или homebudget part 2</title><content type='html'>&lt;p&gt;Итак, как обстоят дела: после первого рывка я скурпулезено заполнял базу через существующие странички. Иногда когда скрипели зубы - добавлял функциональность. А так могу сказать - сейчас меня более-менее устраивает, все работает как и должно, есть куда развивать систему, следующие шаги ясны. Собственно, вот продолжение хроник, информация восстановлена по change-ам. Все измения фискируются в source-control'е в качестве которого я успешно использую bazaar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Добавляем просмотр отдельного дня. То есть это выборка из базы по конкретному дню, ссылки на редактирование отдельных записей (покупок), собственно ценники, и сумма за день. Как - добавляем новый метод в контроллер, добавляем отдельную вьюху для html-я, добавляем правило для урлов. all right, let's get started&lt;/li&gt;
&lt;li&gt;Чтобы было понятно - начинаем с правила для урлов. хотим чтобы запрос для странички дня выглядел типа blah-blah-blah/view/day/2010/04/30 ну типа никаких идиотских GET параметров. В urls.py добавляем следующее правило:
&lt;pre class="brush: python;"&gt;
(r&amp;#39;^purchases/view/(?P&amp;lt;year&amp;gt;\d{4})/(?P&amp;lt;month&amp;gt;\d{1,2})/(?P&amp;lt;day&amp;gt;\d{1,2})/$&amp;#39;, &amp;#39;day_view&amp;#39;)
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Новый метод контроллера (в файле purchases/views.py), логика такая - добавленное в предыдущем пункте правило добавит необходимые параметры день/месяц/год, мы из них делаем нормальный объект datetime (если не получается - типа 68 января - то 404), потом забираем из базы все записи Purchase с этой датой, берем сумму по ценам и выводим все во вьюху.
&lt;pre class="brush: python;"&gt;
def day_view(request, year, month, day):
    try:
        dt = datetime.date(year=int(year), month=int(month), day=int(day))
    except:
        raise Http404
    plist = Purchase.objects.filter(purchase_date = dt)
    price_sum = Decimal(&amp;#39;0.0&amp;#39;)
    for it in plist:
        price_sum += it.price_total

    return render_to_response(&amp;#39;purchases/day_view.html&amp;#39;, {
        &amp;#39;date&amp;#39;: dt,
        &amp;#39;list&amp;#39;: plist,
        &amp;#39;price_sum&amp;#39;: price_sum,
    })
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Создаем новый темплейт вьюхи templates/purchases/day_view.html с примерно следующим содержимым. Логика такая - пробегаем по списку значений как на главной странице, выводим ссылки на изменение и тд, выводим отдельно заранее посчитанный тотал.
&lt;pre class="brush: html;"&gt;
&amp;lt;p&amp;gt;Date: {{date}} &amp;lt;/p&amp;gt;
{% if list %}
    &amp;lt;ul&amp;gt;
        {% for p in list %}
            &amp;lt;li&amp;gt;
                {{p.name}} 
                &amp;lt;b&amp;gt;{{p.price_total}}&amp;lt;/b&amp;gt; 
                &amp;lt;a href=&amp;#39;/purchases/edit/{{p.pk}}/&amp;#39;&amp;gt;Edit&amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
{% else %}
    &amp;lt;p&amp;gt;No purchases yet&amp;lt;/p&amp;gt;
{% endif %}
&amp;lt;p&amp;gt;
    Total: &amp;lt;b&amp;gt;{{price_sum}}&amp;lt;/b&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;a href=&amp;#39;/purchases/add/&amp;#39;&amp;gt;Add&amp;lt;/a&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Проверяем на работоспособность. Должно работать :)&lt;/li&gt;
&lt;li&gt;Теперь делаем тоже самое но для месяца. Тупо по аналогии.&lt;/li&gt;
&lt;li&gt;Добавляем аналогично просмотру дня правило для просмотра месяца (доступ по ссылке типа blah-blah-blah/view/day/2010/04/):
&lt;pre class="brush: python;"&gt;
(r&amp;#39;^purchases/view/(?P&amp;lt;year&amp;gt;\d{4})/(?P&amp;lt;month&amp;gt;\d{1,2})/$&amp;#39;, &amp;#39;month_view&amp;#39;)
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;В методе контроллера имеем сразу же несколько проблем. Первая - нужно сделать запрос на совпадение и года, и месяца одновременно (в случае с просмотром дня - все было просто - тупо совпадение даты). Лечим следующим методом:
&lt;pre class="brush: python;"&gt;
from django.db.models import Q
plist = Purchase.objects.filter(Q(purchase_date__year=dt.year) &amp; Q(purchase_date__month=dt.month))
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Вторая проблема - нет в стандартной библиотеке пайтона функций типа добавить месяц. Можно есть - добавления отрезка времени, но так как в месяцах непостоянное кол-во дней такой способ выглядит очень корявым. Какой по-проще способ нашел - по дате составить новую с месяцем+1. 
&lt;pre class="brush: python;"&gt;
# dt - дата
y, m, d = dt.timetuple()[:3]
dt_next = datetime.date(year=y+((m+1)/12), month=(m+1)%12, day=d)
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Третья проблема - как передать данные во вьюху. Я выбрал способ через словарь (dictionary) - в котором date =&amp;gt; purchase_item.
&lt;/li&gt;
&lt;li&gt;Вот получившийся код. Логика такая - получаем год/месяц - строим дату как первое число месяца. Выбираем из базы все записи purchase за этот месяц. Потом прибавляя по дню пока не прийдем к первому числу следующего месяца, &lt;i&gt;попутно собирая суммы за каждый день&lt;/i&gt; и сумму за весь месяц.
&lt;pre class="brush: python;"&gt;
def month_view(request, year, month):
    try:
        dt = datetime.date(year=int(year), month=int(month), day=1)
    except:
        raise Http404
    from django.db.models import Q
    plist = Purchase.objects.filter(Q(purchase_date__year=dt.year) &amp;amp; Q(purchase_date__month=dt.month)).order_by(&amp;#39;purchase_date&amp;#39;)

    y, m, d = dt.timetuple()[:3]
    dt_next = datetime.date(year=y+((m+1)/12), month=(m+1)%12, day=d)
    day_price = {}
    month_total = Decimal(&amp;#39;0.0&amp;#39;)
    while (dt &amp;lt; dt_next):
        purchases_day = plist.filter(purchase_date__day=dt.day)
        day_total = Decimal(&amp;#39;0.0&amp;#39;)
        for it in purchases_day:
            day_total += it.price_total
        month_total += day_total
        day_price[dt] = day_total
        dt += datetime.timedelta(days=1)

    return render_to_response(&amp;#39;purchases/month_view.html&amp;#39;, {
            &amp;#39;date&amp;#39;: datetime.date(year=int(year),month=int(month),day=1),
            &amp;#39;dict&amp;#39;: day_price,
            &amp;#39;total&amp;#39;: month_total,
        })
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Создаем новый темплейт templates/purchases/month_view.html
&lt;pre class="brush: html;"&gt;
&amp;lt;p&amp;gt;Date: {{date}} &amp;lt;/p&amp;gt;
{% if dict %}
    &amp;lt;ul&amp;gt;
        {% for key,value in dict.items %}
            &amp;lt;li&amp;gt;
                {{key}} 
                &amp;lt;b&amp;gt;{{value}}&amp;lt;/b&amp;gt; 
                &amp;lt;a href=&amp;#39;/purchases/view/{{key.year}}/{{key.month}}/{{key.day}}/&amp;#39;&amp;gt;Edit&amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
{% else %}
    &amp;lt;p&amp;gt;No purchases yet&amp;lt;/p&amp;gt;
{% endif %}
&amp;lt;p&amp;gt;
    Total: &amp;lt;b&amp;gt;{{total}}&amp;lt;/b&amp;gt;
&amp;lt;/p&amp;gt;

&amp;lt;a href=&amp;#39;/purchases/add/&amp;#39;&amp;gt;Add&amp;lt;/a&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Проверяем. Опа, не работает - все дни вперемешку, хотя и выставлено для запроса order_by. А тут начинаются темные, не хорошо документированные особенности джанги по сортировке словаря (я об этот момент чуть голову не сломал). Есть миллион решений - типа сделать буферный список из &lt;i&gt;отсортированных&lt;/i&gt; ключей и его передавать с моделью, и тп. Но вот чтобы применить к for-у для словаря сортировку прямо в темплейте - нашел только один:
&lt;pre class="brush: python;"&gt;
for key,value in dict.items|dictsort:"0"
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Сделаем так чтобы после Edit-а или Add-а происходил редирект на просмотр использованного дня.
&lt;/li&gt;
&lt;li&gt;Метод Add-а:
&lt;pre class="brush: python;"&gt;
def purchase_add(request):
    if request.method == &amp;#39;POST&amp;#39;:
        form = PurchaseItemForm(request.POST)
        if form.is_valid():
            form.save()
            purchase_date = form.cleaned_data[&amp;quot;purchase_date&amp;quot;]
            return HttpResponseRedirect(&amp;#39;/purchases/view/%s/%s/%s/&amp;#39; % purchase_date.timetuple()[:3]) 
    else:
        form = PurchaseItemForm()

    return render_to_response(&amp;#39;purchases/purchase_add.html&amp;#39;, {
        &amp;#39;form&amp;#39;: form
    })
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;Метод edit-а:
&lt;pre class="brush: python;"&gt;
def purchase_edit(request, purchase_id):
    try:
        item = Purchase.objects.get(pk=purchase_id)
    except Purchase.DoesNotExist:
        raise Http404

    if request.method == &amp;#39;POST&amp;#39;:
        form = PurchaseItemForm(request.POST, instance=item)
        form.save()
        purchase_date = form.cleaned_data[&amp;quot;purchase_date&amp;quot;]
        return HttpResponseRedirect(&amp;#39;/purchases/view/%s/%s/%s/&amp;#39; % purchase_date.timetuple()[:3])
    
    form = PurchaseItemForm(instance=item)
    return render_to_response(&amp;#39;purchases/purchase_edit.html&amp;#39;, {
        &amp;#39;form&amp;#39;: form,
        &amp;#39;id&amp;#39; : purchase_id
    })
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Делаем добавление покупки на конкретный день (а не только на &lt;i&gt;сегодня&lt;/i&gt;) - те если хотим добавить покупку за &lt;i&gt;вчера&lt;/i&gt; со страницы вчерашнего дня - то поле дата должно быть корректно подставлено на &lt;i&gt;вчера&lt;/i&gt;. Другими словами blah-blah-blah/purchases/add/ - должно добавлять покупку на &lt;i&gt;сегодня&lt;/i&gt;, а blah-blah-blah/purchases/add/2010/04/30 - должно подставлять 2010.04.30 
&lt;/li&gt;
&lt;li&gt;
В urls.py добавляем следующий словарь для определения &lt;i&gt;сегодня&lt;/i&gt;:
&lt;pre class="brush: python;"&gt;
import datetime
year, month, day = datetime.datetime.today().timetuple()[:3]
today_dict = {&amp;#39;year&amp;#39;:year, &amp;#39;month&amp;#39;:month, &amp;#39;day&amp;#39;:day}
&lt;/pre&gt;
И следующее правила взамен старого add-а:
&lt;pre class="brush: python;"&gt;
    (r&amp;#39;^purchases/add/$&amp;#39;, &amp;#39;purchase_add&amp;#39;, today_dict),
    (r&amp;#39;^purchases/add/(?P&amp;lt;year&amp;gt;\d{4})/(?P&amp;lt;month&amp;gt;\d{1,2})/(?P&amp;lt;day&amp;gt;\d{1,2})/$&amp;#39;, &amp;#39;purchase_add&amp;#39;),
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;Метод add должен теперь выглядеть так:
&lt;pre class="brush: python;"&gt;
def purchase_add(request, year, month, day):
    if request.method == &amp;#39;POST&amp;#39;:
        form = PurchaseItemForm(request.POST)
        if form.is_valid():
            form.save()
            purchase_date = form.cleaned_data[&amp;quot;purchase_date&amp;quot;]
            return HttpResponseRedirect(&amp;#39;/purchases/view/%s/%s/%s/&amp;#39; % purchase_date.timetuple()[:3]) 
    else:
        try:
            dt = datetime.date(year=int(year), month=int(month), day=int(day))
        except:
            dt = datetime.today() 
        purchaseitem = Purchase()
        purchaseitem.purchase_date = dt
        form = PurchaseItemForm(instance=purchaseitem)

    return render_to_response(&amp;#39;purchases/purchase_add.html&amp;#39;, {
        &amp;#39;form&amp;#39;: form
    })
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Ну и собственно линка внутри templates/purchases/day_view.html
&lt;pre class="brush: html;"&gt;
&amp;lt;a href=&amp;#39;/purchases/add/{{date.year}}/{{date.month}}/{{date.day}}/&amp;#39;&amp;gt;Add&amp;lt;/a&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Посты по теме:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/04/django-homebudget-part-1.html"&gt;хроники django или homebudget part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-3.html"&gt;хроники django или homebudget part 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html"&gt;хроники django или homebudget part 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-5.html"&gt;хроники django или homebudget part 5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-1643046160306762394?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/1643046160306762394/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=1643046160306762394' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/1643046160306762394'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/1643046160306762394'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-2.html' title='хроники django или homebudget part 2'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-7188654208653706454</id><published>2010-04-04T22:03:00.008+07:00</published><updated>2010-06-27T16:55:40.735+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='homebudget'/><title type='text'>хроники django или homebudget part 1</title><content type='html'>&lt;p&gt;В общем решил вплотную изучить django фреймворк. Есть опыт работы с asp.net mvc фреймворком, есть знания питона. Почему бы и нет?) Как изучить новую технологию максимально эффективно? считаю что нужно сразу же делать какое-нить реальное приложение. Типа учебная тревога. &lt;/p&gt;
&lt;p&gt;Здесь буду публиковать хроники моих изысканий, не хочется чтобы они пропали зря. Не в коем случае не претендую на самое оптимальное решение и описание. Вообще происходящее будет много дублировать офф tutorial и примеры из документации с &lt;a href="http://docs.djangoproject.com/"&gt;http://docs.djangoproject.com/&lt;/a&gt;, в этом нет ничего странного - я по этому и учусь ;)&lt;/p&gt;
&lt;p&gt;Что буду писать - менеджер домашнего бюджета. Требования - весьма размытые - хочу контролировать свои расходы. Впрочем таким требованием никого не удивишь. Ладно, скучно стало. Начинаем.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;инсталируем django (надеюсь питон уже есть)
&lt;pre class="brush: python;"&gt;
sudo apt-get install python-django
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;инсталируем psycopg2 модуль к питону для работы с бд
&lt;pre class="brush: python;"&gt;
sudo apt-get install python-psycopg2
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;инсталируем postgre 
&lt;pre class="brush: python;"&gt;
sudo apt-get install postgresql
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;проверям что все установилось и демон запусчен:
&lt;pre class="brush: python;"&gt;
ps -A | grep postgres
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;запускаем консоль постгреса
&lt;pre class="brush: python;"&gt;
sudo -u postgres psql postgres
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;меняем пользователю postgres пароль
&lt;pre class="brush: python;"&gt;
\password postgres
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;выходим из консоли
&lt;pre class="brush: python;"&gt;
\q
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создаем базу
&lt;pre class="brush: python;"&gt;
sudo -u postgres createdb homebudget
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создаем новый django проект
&lt;pre class="brush: python;"&gt;
django-admin.py startproject homebudget
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;проверям что все работает
&lt;pre class="brush: python;"&gt;
cd homebudget
./manage.py runserver
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;в браузере переходим на линку 
&lt;pre class="brush: python;"&gt;
http://127.0.0.1:8000/
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;редактируем settings.py
&lt;pre class="brush: python;"&gt;
DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME = 'homebudget'
DATABASE_USER = 'postgres'
DATABASE_PASSWORD = 'наш пароль'
DATABASE_HOST = 'localhost'
DATABASE_PORT = '5432'
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;запускаем синхронизацию проекта с базой (нужно чтобы работали installed_applications из настроек проекта)
&lt;pre class="brush: python;"&gt;
./manage.py syncdb
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;на вопрос You just installed Django's auth system, which means you don't have any superusers defined. отвечаем да и вводим данные
&lt;/li&gt;

&lt;li&gt;стартуем новое приложение в проекте
&lt;pre class="brush: python;"&gt;
./manage.py startapp purchases
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;редактируем models.py
&lt;pre class="brush: python;"&gt;
from django.db import models
 
class Purchase(models.Model):
    name = models.CharField('identifier of item', max_length=200)
    quantity = models.FloatField('quantity of purchase')
    price_for_one = models.DecimalField('price for one item', max_digits=10, decimal_places=2)
    price_total = models.DecimalField('price for all', max_digits=10, decimal_places=2)
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;добавляем приложение в installed_apps, в файле settings.py добавляем в список 
&lt;pre class="brush: python;"&gt;
'homebudget.purchases'
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;синхронизируем базу с моделями
&lt;pre class="brush: python;"&gt;
./manage.py syncdb
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;забиваем базу тестовыми данными (можно через pgadmin), заходим в консоль джанги
&lt;pre class="brush: python;"&gt;
./manage.py shell
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;там вбиваем
&lt;pre class="brush: python;"&gt;
from homebudget.purchases.models import Purchase
p = Purchase(name='beer',quantity=3, price_for_one=48, price_total=144)
p.save()
quit()
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;добавляем новое правило для url rewriter-а в файле urls.py
&lt;pre class="brush: python;"&gt;
(r'^purchases/$', 'homebudget.purchases.views.index')
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создаем новое view, редактируем файл purchases/views.py
&lt;pre class="brush: python;"&gt;
from django.template import Context, loader
from homebudget.purchases.models import Purchase
from django.http import HttpResponse

def index(request):
    all_list = Purchase.objects.all()
    t = loader.get_template('purchases/index.html')
    c = Context({
        'all_list': all_list,
    })
    return HttpResponse(t.render(c))
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создадим папку templates в каталоге проекта, и подпапку purchases
&lt;pre class="brush: python;"&gt;
mkdir templates
mkdir templates/purchases
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;добавляем папку templates в проектные шаблоны, правим файл settings.py (путь абсолютный!)
&lt;pre class="brush: python;"&gt;
TEMPLATE_DIRS = (
    '/home/username/projects/homebudget/templates',
)
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создадим файл templates/purchasesindex.html
&lt;pre class="brush: html;"&gt;
{% if all_list %}
    &amp;lt;ul&amp;gt;
        {% for p in all_list %}
            &amp;lt;li&amp;gt;{{p.name}} &amp;lt;b&amp;gt;{{p.price_total}}&amp;lt;/b&amp;gt;&amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
{% else %}
    &amp;lt;p&amp;gt;No purchases yet&amp;lt;/p&amp;gt;
{% endif %}
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;запускаем сервер, в браузере переходим на страницу. ништяк? ;)
&lt;pre class="brush: python;"&gt;
http://127.0.0.1:8000/purchases/
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;добавляем новое поле к модели (purchases/models.py)
&lt;pre class="brush: python;"&gt;
    purchase_date = models.DateField('date of buying')
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;так как джанга не поддерживает миграцию бд, делаем руками
&lt;pre class="brush: python;"&gt;
./manage.py sql purchases
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;получаем sql код, лезем в postgre и правим в нем таблицу
&lt;pre class="brush: python;"&gt;
ALTER TABLE purchases_purchase ADD COLUMN purchase_date date;
UPDATE purchases_purchase SET purchase_date=now();
ALTER TABLE purchases_purchase ALTER COLUMN purchase_date SET NOT NULL;
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создаем add форму
&lt;/li&gt;

&lt;li&gt;в файл models.py добавляем следующий класс
&lt;pre class="brush: python;"&gt;
from django import forms

class PurchaseItemForm(forms.Form):
    name = forms.CharField(max_length=200)
    quantity = forms.FloatField()
    price_for_one = forms.DecimalField(decimal_places=2)
    price_total = forms.DecimalField(max_digits=10, decimal_places=2)
    purchase_date = forms.DateField()
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;в файл views.py добавляем новую вьюхую
&lt;pre class="brush: python;"&gt;
from homebudget.purchases.models import PurchaseItemForm
def purchase_add(request):
    form = PurchaseItemForm()
    t = loader.get_template('purchases/purchase_add.html')
    c = Context({
        'form': form
    })
    return HttpResponse(t.render(c))
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создаем новый файл templates/purchases/purchase_add.html
&lt;pre class="brush: python;"&gt;
&amp;lt;form action=&amp;quot;/purchases/add/&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
{{ form.as_p }}
&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Save&amp;quot; /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;в файл urls.py прописываем новый маппинг паттерн
&lt;pre class="brush: python;"&gt;
(r'^purchases/add/$', 'homebudget.purchases.views.purchase_add'),
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;запускам дев сервер, в браузере переходим на страницу
&lt;pre class="brush: python;"&gt;
http://127.0.0.1:8000/purchases/add/
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;теперь добавляем сохранение данных, это будет происходить на той же самой странице по post запросу
&lt;/li&gt;

&lt;li&gt;в файл views.py добавляем обработку post запроса и сохранение
&lt;pre class="brush: python;"&gt;
from django.http import HttpResponse, HttpResponseRedirect
def purchase_add(request):
    if request.method == 'POST':
        form = PurchaseItemForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data['name']
            quantity = form.cleaned_data['quantity']
            price_for_one = form.cleaned_data['price_for_one']
            price_total = form.cleaned_data['price_total']
            purchase_date = form.cleaned_data['purchase_date']
            item = Purchase(name=name, quantity=quantity, price_for_one=price_for_one, price_total=price_total, purchase_date=purchase_date)
            item.save()
            return HttpResponseRedirect('/purchases/') 
    else:
        form = PurchaseItemForm()
    t = loader.get_template('purchases/purchase_add.html')
    c = Context({
        'form': form
    })
    return HttpResponse(t.render(c))
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;проверям в браузере, в случае удачного сохранения будет переход на список всех
&lt;/li&gt;

&lt;li&gt;займемся совершенствованием
&lt;/li&gt;

&lt;li&gt;в файл views.py добавляем 
&lt;pre class="brush: python;"&gt;
from django.shortcuts import render_to_response
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;ужасные loader.get_template, Context, return HttpResponse(..) заменяем на простую конструкцию
&lt;pre class="brush: python;"&gt;
    return render_to_response('purchases/purchase_add.html', {
        'form': form
    })
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;в файле models.py вставляем ModelForm взамен идиотского дублирующего кода из модели
&lt;pre class="brush: python;"&gt;
class PurchaseItemForm(forms.ModelForm):
    class Meta:
        model = Purchase
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;да и после этого во вьюхе не нужно перечислять все поля.. теперь будет так:
&lt;pre class="brush: python;"&gt;
def purchase_add(request):
    if request.method == 'POST':
        form = PurchaseItemForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/purchases/') 
    else:
        form = PurchaseItemForm()

    return render_to_response('purchases/purchase_add.html', {
        'form': form
    })
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;в файле urls.py тоже изменения:
&lt;pre class="brush: python;"&gt;
urlpatterns = patterns(
    'homebudget.purchases.views',
    (r'^purchases/$', 'index'),
    (r'^purchases/add/$', 'purchase_add'),
)
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;запускаем дев сервер и все перепроверяем, что работает как до рефакторинга
&lt;/li&gt;

&lt;li&gt;добавляем edit для элемента, по быстрому
&lt;/li&gt;

&lt;li&gt;в файл views.py
&lt;pre class="brush: python;"&gt;
def purchase_edit(request, purchase_id):
    try:
        item = Purchase.objects.get(pk=purchase_id)
    except Purchase.DoesNotExist:
        raise Http404

    if request.method == 'POST':
        form = PurchaseItemForm(request.POST, instance=item)
        form.save()
        return HttpResponseRedirect('/purchases/')

    form = PurchaseItemForm(instance=item)
    return render_to_response('purchases/purchase_edit.html', {
        'form': form,
        'id' : purchase_id
    })
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;создадим файл templates/purchases/purchase_edit.html
&lt;pre class="brush: html;"&gt;
&amp;lt;form action=&amp;quot;/purchases/edit/{{ id }}/&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;
{{ form.as_p }}
&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Save&amp;quot; /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;в файл index.html добавим линки для редактирования и добавления нового элемента:
&lt;pre class="brush: html;"&gt;
{% if all_list %}
    &amp;lt;ul&amp;gt;
        {% for p in all_list %}
            &amp;lt;li&amp;gt;
                {{p.name}}
                &amp;lt;b&amp;gt;{{p.price_total}}&amp;lt;/b&amp;gt;
                {{p.purchase_date}}
                &amp;lt;a href='/purchases/edit/{{p.pk}}/'&amp;gt;Edit&amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
{% else %}
    &amp;lt;p&amp;gt;No purchases yet&amp;lt;/p&amp;gt;
{% endif %}

&amp;lt;a href='/purchases/add/'&amp;gt;Add&amp;lt;/a&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;в файл urls.py
&lt;pre class="brush: python;"&gt;
    (r'^purchases/edit/(?P&amp;lt;purchase_id&amp;gt;\d+)/$', 'purchase_edit'),
&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;запускаем дев сервер и наслаждаемся
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;сейчас инструментария хватает чтобы забивать и просматривать базу, но это все очень не удобно, так что продолжение следует&lt;/p&gt;

&lt;p&gt;
Посты по теме:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-2.html"&gt;хроники django или homebudget part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/05/django-homebudget-part-3.html"&gt;хроники django или homebudget part 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-4.html"&gt;хроники django или homebudget part 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexadotcpp.blogspot.com/2010/06/django-homebudget-part-5.html"&gt;хроники django или homebudget part 5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-7188654208653706454?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/7188654208653706454/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=7188654208653706454' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/7188654208653706454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/7188654208653706454'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/04/django-homebudget-part-1.html' title='хроники django или homebudget part 1'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-2237236896171409093</id><published>2010-02-27T02:10:00.003+06:00</published><updated>2010-02-27T02:19:23.211+06:00</updated><title type='text'></title><content type='html'>This is a debug hell routine... 500 page views per working day by one developer - it's possible! 
&lt;a href="http://picasaweb.google.com/lh/photo/CAJ3yFbDEksCP71EjVUddA?authkey=Gv1sRgCJCxk--6oZaOHg&amp;feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_i8vlwHpnrg0/S4gqa4Ki9CI/AAAAAAAAAFs/LaFGmBqDgsM/s800/helldebug_hide.png" /&gt;&lt;/a&gt;

P.S. fucking IE6&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-2237236896171409093?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/2237236896171409093/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=2237236896171409093' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2237236896171409093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2237236896171409093'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/02/this-is-debug-hell-routine.html' title=''/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_i8vlwHpnrg0/S4gqa4Ki9CI/AAAAAAAAAFs/LaFGmBqDgsM/s72-c/helldebug_hide.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-5545574430381075623</id><published>2010-01-17T15:57:00.002+06:00</published><updated>2010-01-17T16:13:34.800+06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bzr'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>bzr+svn friends?</title><content type='html'>Уже говорил про мою &lt;a href="http://alexadotcpp.blogspot.com/2010/01/bzrsvn.html"&gt;проблему с bzr-svn плагином&lt;/a&gt;. Так вот после умозаключений и тестов, я пришел к оптимальному для меня решению - удалить bzr-svn плагин, пользоваться bzr и svn поотдельности. В существующем checkout от svn я проинициализировал bazaar shared репозиторий (bzr init-repo) прямо в папке svn-резозитория. В svn-е я поставил игнор файлов .bzr, а в Bazaar поставил игнор файлов .svn и почти усе. Потом добавил в bazaar все файлы из svn-овской папки (bzr add). Все локальные изменения теперь я бренчую через bzr, туда же постоянно комитаю свои собственные маленькие изменения. Когда фитча локально оттестирована, я сливаю ее в основной bazaar репозиторий. И только потом идет коммит в обычный svn-репозиторий. С апдейтами из svn, благодаря тому что базарный репозиторий mirror - проблем не возникает. Да, я понимаю что комманд стало чуток больше, но прелесть все равно есть.

Мое локальное счастье наступило.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-5545574430381075623?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/5545574430381075623/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=5545574430381075623' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5545574430381075623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5545574430381075623'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/01/bzrsvn-friends.html' title='bzr+svn friends?'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-3211511488740973848</id><published>2010-01-13T10:04:00.005+06:00</published><updated>2010-01-13T10:16:02.110+06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bzr'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>bzr+svn</title><content type='html'>Очень хотел использовать Bazaar для работы с svn, но увы и ах &lt;a href="https://bugs.launchpad.net/bzr-svn/+bug/409668"&gt;bzr-svn needs to access the root to e.g. find out what the repository layout is&lt;/a&gt; что не позволяет использовать его на закрытых репозиториях. Практически выглядит так

bzr checkout svn+https://svn.myserver.com/myproject/src src
bzr: ERROR: A Subversion remote access command failed: Server sent unexpected return value (403 Forbidden) in response to PROPFIND request for '/'

Ну вот только если удастся получить доступ к корню репозитория... или перестать пользоваться такими svn репозиториями. Пока смотрю в сторону Git, там нет такой проблемы.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-3211511488740973848?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/3211511488740973848/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=3211511488740973848' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/3211511488740973848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/3211511488740973848'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2010/01/bzrsvn.html' title='bzr+svn'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-5185608305347114247</id><published>2009-11-19T00:13:00.001+06:00</published><updated>2009-11-19T00:18:20.930+06:00</updated><title type='text'></title><content type='html'>xckd как всегда отжигает. жизненно и в тему.

&lt;img alt="Some engineer out there has solved P=NP and it's locked up in an electric eggbeater calibration routine. For every 0x5f375a86 we learn about, there are thousands we never see." title="Some engineer out there has solved P=NP and it's locked up in an electric eggbeater calibration routine. For every 0x5f375a86 we learn about, there are thousands we never see." src="http://imgs.xkcd.com/comics/academia_vs_business.png"/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-5185608305347114247?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/5185608305347114247/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=5185608305347114247' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5185608305347114247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5185608305347114247'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2009/11/xckd.html' title=''/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-4152830878092455958</id><published>2009-10-24T13:12:00.003+07:00</published><updated>2009-10-24T13:45:39.595+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='objectdatasource'/><category scheme='http://www.blogger.com/atom/ns#' term='asp.net'/><category scheme='http://www.blogger.com/atom/ns#' term='website'/><title type='text'>asp.net, website, objectdatasource</title><content type='html'>&lt;p&gt;В asp.net есть такой тип проекта WebSite. Он отличается от WebApplication тем, что проектный сайт не создается, любой попавшый в папку файл добавляется в сайт, и самое главное - сайт не собирается в отдельную сборку. На продакшн сайт можно добавлять новые странички не компилируя их (ну есть конечно же предкомпиляция сайта, но см. далее). Каждая страничка компилируется через iis сервер в собственную сборку с рандомным именем. Как это разруливает сервер - загадка. 
&lt;p&gt;Для простой логики где все находится в пределах одной страницы все окей, а вот когда требуется вызвать код находящийся на другой страничке - начинаются проблемы: одна страничка ничего не знает о другой (т.к. находится в собственной сборке). Обычное решение - выносить такой нужный код в отдельную собственную сборку, типа библиотеки, закинуть ее в bin директорию сайта, подключать ее в страничках сайта и использовать в коде. Хорошо то оно хорошо. А что делать если ну никак не хочется выносить одну единственную функцию в библиотеку, код которой нужен только на одной единственной страничке? 
&lt;p&gt;Пример - нужно реализовать bind грида через object data source, и не через что-либо иное (такое может понадобится для использования фишек грида типа редактирования, удаления в табличке, сортировки, пейджинга и т.п.). Проблема сводится к тому что в SelectMethod нужно написать имя метода, в SelectParameters - параметры, а в TypeName - полное название типа где находится метод. А класс лежит в своей собственной сборке, а имя генерируется рандомно. Единственное решение проблемы - динамически заполнять поле TypeName на этапе загрузки страницы. Вот финальный код:
&lt;pre class="brush: c#"&gt;
&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; AutoEventWireup=&amp;quot;true&amp;quot; CodeFile=&amp;quot;myprojectclass.aspx.cs&amp;quot; Inherits=&amp;quot;myprojectclass&amp;quot; %&amp;gt;

&amp;lt;asp:GridView ID=&amp;quot;gridView1&amp;quot; runat=&amp;quot;server&amp;quot; DataSourceID=&amp;quot;objectDataSource1&amp;quot; AutoGenerateColumns=&amp;quot;True&amp;quot; /&amp;gt;
&amp;lt;asp:ObjectDataSource ID=&amp;quot;objectDataSource1&amp;quot; runat=&amp;quot;server&amp;quot; SelectMethod=&amp;quot;GetData&amp;quot; TypeName=&amp;quot;&amp;quot; &amp;gt;&amp;lt;/asp:ObjectDataSource&amp;gt;

public partial class myprojectclass : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
         // ...
         objectDataSource1.TypeName = String.Format(&amp;quot;{0}, {1}&amp;quot;, this.GetType().FullName, this.GetType().Assembly);
         gridView1.DataBind();
    }
    
    public System.Collections.Generic.List&amp;lt;DataItem&amp;gt; GetData() {
         // ...
    }
}
&lt;/pre&gt;

&lt;p&gt;Повторюсь - это будет работать в пределах одной страницы, код другой странички получить не получится. Только через собственную сборку. Выводы: 1. никогда не использовать WebSite 2. использовать только WebApplication - так как все получается логично и понятно 3. По возможности никогда не использовать ASP.NET - есть другие более удобные инструментарии веб-разработки&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-4152830878092455958?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/4152830878092455958/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=4152830878092455958' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4152830878092455958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4152830878092455958'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2009/10/aspnet-website-objectdatasource.html' title='asp.net, website, objectdatasource'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-4477472509212363346</id><published>2009-10-18T20:25:00.004+07:00</published><updated>2009-10-18T20:50:57.038+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='syntax-highlighting'/><title type='text'>Подсветка синтаксиса в блоге</title><content type='html'>Прикрутил к блогу подсветку синтаксиса кода. Некоторое время я провел за поисковиком, все примеры были не очень и в основном старые. Нашел кое-что подходящее на &lt;a href='http://heisencoder.net/2009/01/adding-syntax-highlighting-to-blogger.html'&gt;heisencoder.net&lt;/a&gt;. В итоге и использовал эту компоненту &lt;a href='http://alexgorbatchev.com/wiki/'&gt;syntaxhighlighter&lt;/a&gt;. Еще есть старый репозиторий на &lt;a href='http://code.google.com/p/syntaxhighlighter/'&gt;гугл-коде&lt;/a&gt;. Жалко что пример на том блоге устарел и почему-то у меня не заработал. Благо у автора компоненты очень подробная док-ция

Компонента представляет собой набор javascript-ов и css шаблонов, заходим на http://alexgorbatchev.com/pub/sh/ выбираем нужную версию и качаем. Можно и не качать, а использовать прямые ссылки на хостящиеся файлы. В общем внутри head-а странички вставляем следующий код:

&lt;pre class="brush: html;"&gt;
&amp;lt;link href='http://alexgorbatchev.com/pub/sh/2.1.364/styles/shCore.css' rel='stylesheet' type='text/css'/&amp;gt;
&amp;lt;link href='http://alexgorbatchev.com/pub/sh/2.1.364/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/&amp;gt;
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.1.364/scripts/shCore.js' type='text/javascript'/&amp;gt;
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.1.364/scripts/shBrushCSharp.js' type='text/javascript'/&amp;gt;
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.1.364/scripts/shBrushXml.js' type='text/javascript'/&amp;gt;
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.1.364/scripts/shBrushCpp.js' type='text/javascript'/&amp;gt;

&amp;lt;script type='text/javascript'&amp;gt;
  SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/2.1.364/scripts/clipboard.swf';
                SyntaxHighlighter.config.bloggerMode = true;
  SyntaxHighlighter.defaults['gutter'] = true;
                SyntaxHighlighter.all();
&amp;lt;/script&amp;gt;
&lt;/pre&gt;

Тем самым мы добавили нужные стили (у меня используется дефолтный, но вообще доступно несколько) и скрипты (здесь я добавил html, cpp, c#), настроили компоненту должным образом. За подробными комментариями по назначению, списку всех параметров и всех возможных скриптов - на &lt;a href='http://alexgorbatchev.com/wiki/'&gt;сайт производителя&lt;/a&gt;. Потом в html-код сообщения добавляем

&lt;pre class="brush: html;"&gt;
&amp;lt;pre class=&amp;quot;brush: cpp; toolbar: true;&amp;quot;&amp;gt;
 #include &amp;lt;iostream&amp;gt;
    int main() {
  std::cout &amp;lt;&amp;lt; "hello" &amp;lt;&amp;lt; std::endl;
 
 }
&amp;lt;/pre&amp;gt;
&lt;/pre&gt;

P.S. Главное не забывать ескейпить символы...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-4477472509212363346?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/4477472509212363346/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=4477472509212363346' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4477472509212363346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4477472509212363346'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2009/10/blog-post.html' title='Подсветка синтаксиса в блоге'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-5943216071775409886</id><published>2009-10-15T22:40:00.004+07:00</published><updated>2009-10-18T20:17:23.435+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='static'/><category scheme='http://www.blogger.com/atom/ns#' term='c#'/><category scheme='http://www.blogger.com/atom/ns#' term='Индусы'/><title type='text'>Индусы среди нас</title><content type='html'>Один человечек написал подобный код:

&lt;pre class="brush: csharp; toolbar: true;"&gt;
  class Item {
    public static void UpdateItem(Item _item) {
    // ...
    }
  }

  Item it;
  // ...
  Item.UpdateItem(it);
&lt;/pre&gt;

Попахивает безграммотностью. Не логично использовать статический метод который принимает экземпляр собственного класса. Я попросил человечка избавиться от статического метода. Код был переписан вот в это:

&lt;pre class="brush: csharp; toolbar: true;"&gt;
  class Item {
    public void UpdateItem(Item _item) {
    // ...
    }
  }
  
  Item it;
  // ...
  new Item().UpdateItem(it);
&lt;/pre&gt;

Да, оказывается у некоторых в крови индусские гены и они в тайне по ночам молятся шестируким богам. И почему нельзя сделать вот так, по простому:

&lt;pre class="brush: csharp; toolbar: true;"&gt;
  class Item {
    public void UpdateItem() {
    // ...
    }
  } 

  Item it;
  // ...
  it.UpdateItem();
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-5943216071775409886?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/5943216071775409886/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=5943216071775409886' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5943216071775409886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/5943216071775409886'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2009/10/public-static-void-updateitemitem-item.html' title='Индусы среди нас'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-2922789067910981054</id><published>2009-08-19T01:54:00.008+07:00</published><updated>2009-08-21T09:21:20.508+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='локальная сеть'/><category scheme='http://www.blogger.com/atom/ns#' term='две сетевухи'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Не вероятно, но факт</title><content type='html'>Есть десктоп, на него давно заведена сетка, полностью настройка по dhcp и т.п. Есть ноут, сиротливо лежащий без инета. Было решено завести на нем сетку, для начала хотя бы через вторую сетевуху (покупать вайфай точку лом). К тому же на ноуте упала винда и не хотела восстанавливаться.

Взял с полки старенькую сетевуху от dlink-а, воткнул ее в комп. Убунта правильно ее определила, корректно добавила в устройства и сделала на нее интерфейс. Как по инструкции с гугла, во вторую сетевуху втыкаем провод, другой конец заводим в ноут. Т.к. операцонки на ноуте нету - стартуем с live-cd.

на десктопе с помощью ifconfig -a получаем имя нового интерфейса (eth1).

на десктопе в /etc/network/interfaces пишем:

auto lo eth0 eth1
iface lo inet loopback
iface eth0 inet dhcp
iface eth1 inet static
    address 195.168.0.1
    netmask 255.255.255.0
    network 195.168.0.0
    broadcast 195.168.0.255

на ноуте:

auto lo eth0
iface lo inet loopback
iface eth0 inet static
    address 195.168.0.120
    netmask 255.255.255.0
    getway 195.168.0.1

перезапускаем сетку и там и там:
sudo /etc/init.d/networking restart

на ноуте делаем:
ping 195.168.0.1

в ответ получаем: &lt;strong&gt;&lt;b&gt;destination host unreachable&lt;/b&gt;&lt;/strong&gt;. Че за нах?!! Спокойно, пробую еще несколько раз... может быть с проводом что-то не так, да нет только что друзья обжали и просветили. Лезем в dmesg | grep eth ... видим: eth1 бла-бла-бла "no link". Че за нах?!! Провод же воткнут правильно!! Ладно, забиваю - сетевуха очень старая - может быть в ней косяк. И правда - выдергиваю шнур из второй карточки и вставляю в первую - пинг пошел!! В топку карточку значитсо. На первый раз с меня хватило - решил просто перелить данные с ноута на десктоп. Поставил ftp-сервер, под дефолтными настройками заюзал с ноута - и перелил инфу. Забил.

Некоторое время спустя появилась у меня другая карточка. На вид более новая. Втыкаю ее, настраиваю. Та жа лажа - &lt;strong&gt;&lt;b&gt;destination host unreachable&lt;/b&gt;&lt;/strong&gt; и "no link". Ну все, меня начинает коробить. Меняю на десктопе местами шнуры и конфиги, включаю - пинг пошел, локальная сетка провайдера работает!! Не вероятно, но факт. В чем была конкретно проблема - мне не понятно. 

После этого чтобы не парить мозг - ставлю на десктопе проксю 3proxy, настраиваю на айпишник моего ноута. Настраиваю ноут на прокси - вуаля - все пашет как надо, инет есть на двух компах. Потом была установка на ноут полноценной оси, но это уже другая история.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-2922789067910981054?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/2922789067910981054/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=2922789067910981054' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2922789067910981054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2922789067910981054'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2009/08/blog-post.html' title='Не вероятно, но факт'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-6438486994640685830</id><published>2009-06-05T00:20:00.005+07:00</published><updated>2009-06-05T02:03:40.861+07:00</updated><title type='text'></title><content type='html'>&lt;span style="font-style:italic;"&gt;"Как же мне достучаться до этих детей?"&lt;/span&gt; Эрик Картманос

Сегодня появился пост на Хабрахабре &lt;a href="http://habrahabr.ru/blogs/cpp/61323/"&gt;"Всё ли вы знаете про if?"&lt;/a&gt;. Я плакал и смеялся... Человек говорит о "style guide"-ах.. не уже ли все так запущено в понимании?! 

То что описано в пункте про "Фигурные скобки" - это самый обычный стиль Кернигана и Ричи. Но кроме него существуют, и надо заметить имеют право на жизнь, и многие их используют, другие стили типа BSD или GNU. В общем - наверное люди ничего не знают о разнообразии стилей отступов. Прочитать можно &lt;a href="http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B8%D0%BB%D1%8C_%D0%BE%D1%82%D1%81%D1%82%D1%83%D0%BF%D0%BE%D0%B2"&gt;здесь&lt;/a&gt;. Следует заметить - что современные средства разработки позволяют форматировать эти самые стили отступов - великолепный пример ReSharper, VAssist, да и чего там говорить - голую VS тоже можно "поднастроить". 

"размещение нескольких выражений в одной строке ещё и затрудняет отладку" - правда? не замечал. Дебагеры научились с этим справляться. Правда. 

"Используйте преимущества С++. Если переменная нужна только в блоке, то и создавайте её только в блоке. Это сбережёт процессорное время, память… в общем всё, что так дорого каждому человеку, исповедующему гуманистические идеалы :-)", - Гениально Баттерс. Особенно про процессорное время. И про память.

"Описывайте сперва нормальный ход событий" - ну это как-то по-китайски. А ведь мы хотим говорить по-русски. Напиши маленький комментарий в коде - "// FAILED!" - и будет понятно что тут ты обрабатываешь ошибку, "//Ok" - скажет что все идет нормально. А вот заставлять всех писать все под-левый руль - не по фен-шую. 

"// ХОРОШО
while (true) {}
// ПЛОХО
while (1) {}
for (;;) {}"
Те же яйца но в профиль. "Что такое хорошо и что такое плохо?". Вообще если код-тру: то бесконечных циклов не будет. Хотя - это не факт. Помешать пониманию могут только гены 100%-го говно-кодера.

"Разделяйте логические выражения на части"
Ну это правильно. Но (!) почему бы не расставить скобки в логическом выражении? Ну и переносы строк. По моему намного лучше - чем засорять сознание человека лишними переменными.

"Выражения в условиях"
Когда один раз вынесешь операцию из if-а это просто. А вот так еще придется сделать не один десяток раз? Даже самого "настырного читателя" кода достанет что автор писал каждый раз "&lt;code&gt;bool bOk = read(...); if (bOk) {...}&lt;/code&gt;". Думаю здесь не должно быть правила.

А вот то что реально не описано - так это юзабилити и собственно эффективность. 

Про замену &lt;code&gt;if (..) else if (...) else if (...) ...&lt;/code&gt; на &lt;code&gt;switch&lt;/code&gt; - ни слова, хотя для читабельности гораздо лучше. 

Про случай (см. ниже) я вообще молчу. Хотя оч.оч.много программеров (читай говнокодеров) верят что SomeExpression может повлиять и код обозначенный "unreachable" может быть и выполнится по воле всевышнего над-интеллекта.

&lt;code&gt;
bool bTrue = true;
... 
/*bTrue нигде не изменялось*/
...
if (!bTrue&amp;&amp;SomeExpression) {
   // some unreachable code
}
&lt;/code&gt;

Про define и if-ы. При работе с winapi часто приходится проверять результат выполнения функции (HRESULT) - и часто помогают подобные конструкции 

&lt;code&gt;
#define RETURNONFAIL(hr) if (FAILED(hr)) { \
   LOG(GetLastError()); \
   return hr; } 
&lt;/code&gt;

Ну и old-school: про старый-старый goto тоже умолчали - хотя можно привести 10k+ примеров когда код с миллионом вложенных if-ов сокращается в разы.

Я видел много кода и стилей форматирования. Один знакомый утверждает что в коде не должно быть лишних "пустых строк" и "строк только с одной скобкой" - вроде "код сжатый по вертикали" - надо сказать код этого человека я мог прочитать, понять и работать с ним. Другой знакомый, адепт mfc и wtl, - явная противоположность - код "расстянут" - много пробелов и отступов, но зато каждый "абзац" кода - это мысль, причем понятная. Могу сказать с уверенностью - главное чтобы стиль какой-никакой был и чтобы весь проект был в ОДНОМ стиле. И еще - один знакомый как-то сказал что идеальный метод - это тот, что помещается на один экран без скроллинга. А вот писать говно-код, а потом еще спорить о его читабельности и дизайне оформления - это пафос.

В целом - не рекомендую выше обозначенную статью (хотя она висит на главной странице Хабра!) для прочтения.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-6438486994640685830?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/6438486994640685830/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=6438486994640685830' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/6438486994640685830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/6438486994640685830'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2009/06/if.html' title=''/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-3125720739773978017</id><published>2009-01-18T18:04:00.006+06:00</published><updated>2009-10-18T20:09:57.718+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Constraints'/><category scheme='http://www.blogger.com/atom/ns#' term='Generics'/><category scheme='http://www.blogger.com/atom/ns#' term='c#'/><title type='text'>How to Use Constraints in Generics</title><content type='html'>Взято из книжки по подготовке экзамена microsoft "MCTS Self-Paced Training Kit (Экзамен 70-536)", Глава 1 (Framework fundamentals), Урок 3 (What are generics).

How to Use Constraints

Generics would be extremely limited if you could only write code that would compile for any class, because you would be limited to the capabilities of the base Object class.To overcome this limitation, use constraints to place requirements on the types thatconsuming code can substitute for your generic.

Generics support four types of constraints:
- Interface Allow only types that implement specific interfaces to use your generic.
- Base class Allow only types that match or inherit from a specific base class to use your generic.
- Constructor Require types that use your generic to implement a parameterless constructor.
- Reference or value type Require types that use your generic to be either a reference or value type.

Use the As clause in Visual Basic or the where clause in C# to apply a constraint to a generic.

Разберемся - для generic мы можем выставлять ограничение с помощью ключевого слова where, типа как далее:

&lt;pre class="brush: csharp; toolbar: true;"&gt;
class Abc&amp;lt;T&amp;gt; where T : IDisposable
{ ... }
&lt;/pre&gt;
 
Ну это понятно, теперь разберемся с самими ограничениями, с первыми двумя - все и так понятно, а вот на последние два авторы как-то забыли объяснить и дать пример.

1. parameterless constructor - требование безпараметрического конструктора. По-суть очень полезно, если внутри своего класса вы хотите создавать новые экземпляры обобщенного класса. Все очень просто, нужно написать new(). Кстати без данного ограничения вы не сможете написать T it = new T()... :)

&lt;pre class="brush: csharp; toolbar: true;"&gt;
class Abc&amp;lt;T&amp;gt; where T : new()
{
 public void DoSmth()
 {
         T it = new T();
 }
}
&lt;/pre&gt;

2. Reference or value type. Тоже может быть очень полезным - так как мы можем полностью отсечь применения типа Abc&amp;lt;int&amp;gt и т.д. Тут сложнее: для referenced type должно идти ключевое слово class, для value type - ключевое слово struct.

&lt;pre class="brush: csharp; toolbar: true;"&gt;
class Abc&amp;lt;T&amp;gt; where T : class
{ ... }

class Cba&amp;lt;T&amp;gt; where T : struct
{ ... }
&lt;/pre&gt;


Честно говоря, когда я узнал что так можно делать - у меня был шок. Сразу же разрешилось несколько проблем в проектах :) Надеюсь кому-нибудь это тоже пригодится.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-3125720739773978017?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/3125720739773978017/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=3125720739773978017' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/3125720739773978017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/3125720739773978017'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2009/01/how-to-use-constraints-in-generics.html' title='How to Use Constraints in Generics'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-4171645173320422366</id><published>2008-11-20T11:56:00.003+06:00</published><updated>2009-10-18T20:08:21.836+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RichTextBox'/><category scheme='http://www.blogger.com/atom/ns#' term='wordwrap'/><category scheme='http://www.blogger.com/atom/ns#' term='word-breaking'/><title type='text'>Про RichTextBox в .NET 2.0 и перенос строк.</title><content type='html'>Многие знают про то, что RichTextBox поддерживает word-wrap или перенос строк. Но не многие знают - как именно управлять переносом строк. Не так давно я столкнулся со следующей проблемой: в данном контроле перенос строк автоматически начинается с white-space символа или знака препинания, т.е. если хотим вбить 100 символов подряд так чтобы они отображались в 3х строках с произвольным переносом между строк - то проблемы начинаются после того как в середину воткнуть дефис или пробел. Да... выглядит конечно же не очень :( Никакой информации по этому поводу я не нашел.

Казалось бы, что это не беда и все можно поправить, но на этом кол-во проблем начало только расти: у контрола RichTextBox есть булево свойство WordWrap, но нет никаких других свойств по изменению поведения этого самого переноса строк. Перепробовал буквально все. Документации по вопросам word-wrapping, word-breaking, hyphernatition почти не нашел. Значит дотНЕТ просто не имеет такого на вооружении.

Итак, откуда же можно потрогать сей компонент? Откуда он вообще взялся на .Net? Правильно, на MFC уже много парились с ним. Нельзя просто забыть тот момент, когда приходило осознание того, что нужно подгрузить специальную библиотеку для использования этого самого контрола! Тем более дотнетный компонент является всего лишь обмоткой давно известной вещи. Вследствие этого я обратиться к исходникам данного компонента - а именно к файлу RichTextBox.h и MSDN :) После недолгих поисков была найдена следующая весчь: 

1. Оконное сообщение &lt;b&gt;EM_SETHYPHENATEINFO&lt;/b&gt; и связанные с ним стили &lt;b&gt;WBF_WORDWRAP&lt;/b&gt; и т.д
2. Оконное сообщение &lt;b&gt;EM_SETWORDBREAKPROC&lt;/b&gt; и функция &lt;b&gt;EDITWORDBREAKPROC&lt;/b&gt;

Первое отпало сразу же т.к. MSDN сказал, что это используется только в asian-виндовсах. Второе же оказалось намного интереснее: т.к. в качестве параметров в callback-функцию &lt;b&gt;EDITWORDBREAKPROC&lt;/b&gt; передавались стили типа &lt;b&gt;WBF_BREAKAFTER&lt;/b&gt; и &lt;b&gt;WBF_ISWHITE&lt;/b&gt;, ну и собственно указатель на текст. Заинтересовало :) Т.е. теперь можно создать свою функцию с собственными определениями где и как переносить текст в контроле. Окей, накидал собственно код:

&lt;pre class="brush: csharp; toolbar: true;"&gt;
[DllImport("user32.dll")]
extern static IntPtr SendMessage(IntPtr hwnd, uint message, IntPtr wParam, IntPtr lParam);

const uint EM_SETWORDBREAKPROC = 0x00D0;

delegate int EditWordBreakProc(IntPtr text, int pos_in_text, int bCharSet, int action);
event EditWordBreakProc myCallBackEvent;

int myCallBack(IntPtr text, int pos_in_text, int bCharSet, int action)
{
    return 0;
}

public Form1()
{
    InitializeComponent();

    richTextBox.Multiline = true;
    richTextBox.WordWrap = true;
    richTextBox.ScrollBars = RichTextBoxScrollBars.None;

    myCallBackEvent = new EditWordBreakProc(myCallBack);
    IntPtr ptr_func = Marshal.GetFunctionPointerForDelegate(myCallBackEvent);
 
 SendMessage(richTextBox.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, ptr_func);
}
&lt;/pre&gt;

Итак, теперь разберемся: я создал делегат, совпадающий с определением &lt;b&gt;EDITWORDBREAKPROC&lt;/b&gt;, и подписал на него собственную функцию. Через маршалинг получил указатель на мою функцию - теперь можно сделать подмену. Потом взял handle самого контрола и послал ему сообщение &lt;b&gt;EM_SETWORDBREAKPROC&lt;/b&gt; на изменение функции переноса строк. Что должна делать функция myCallBack ? Собственно она должна возвращать позицию, в которой следует разорвать строку, относительно текущей. Т.е. реально надо распарсить весь текст и по "какому-то" правилу сказать - "вот здесь будет перенос строки". Для моей задачи вполне хватило вернуть нулевую позицию. Т.е. текст тупо переносится на другую строчку без всяких правил разрыва. Да это же то что мне нужно! Ура!!

В качестве дополнения: 
1. для остальных случаев, где нужно обрабатывать строки и расставлять переносы, стоит все-таки получить строку из callback функции, это можно сделать с помощью следующего кода:

&lt;pre class="brush: csharp; toolbar: true;"&gt;
int myCallBack(IntPtr text, int pos_in_text, int bCharSet, int action)
{
string str = Marshal.PtrToStringUni(text);
...
}
&lt;/pre&gt;

2. Стили у контрола меняются после различных оконных сообщений - типа смены фокуса, enable/disable и т.п. Значит, для полной работоспособности вышеприведенного примера нужно кидать сообщение &lt;b&gt;EM_SETWORDBREAKPROC&lt;/b&gt; после всех значимых событий. Хотелось бы вообще забыть про такие мелочи. Выход есть)) Создаем новый контрол, наследуемся от RichTextBox ну и... я думаю вы сами догадаетесь :)
3. Пример на MFC выглядел бы куда проще :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-4171645173320422366?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/4171645173320422366/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=4171645173320422366' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4171645173320422366'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/4171645173320422366'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2008/11/richtextbox-net-20.html' title='Про RichTextBox в .NET 2.0 и перенос строк.'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7292835686865407878.post-2086610242836374658</id><published>2008-10-18T18:22:00.000+07:00</published><updated>2008-10-18T18:28:50.666+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='First post'/><title type='text'>Начало</title><content type='html'>Если даже &lt;a href="http://torvalds-family.blogspot.com/"&gt;Линус Торвальдс&lt;/a&gt; завел блог... то и мне стоит по-пробовать =) Предположительно в этом блоге я буду размещать заметки по программированию и моих изысканий в этой области. Поживем - увидим как у меня получится вести этот лог.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7292835686865407878-2086610242836374658?l=alexadotcpp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alexadotcpp.blogspot.com/feeds/2086610242836374658/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7292835686865407878&amp;postID=2086610242836374658' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2086610242836374658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7292835686865407878/posts/default/2086610242836374658'/><link rel='alternate' type='text/html' href='http://alexadotcpp.blogspot.com/2008/10/blog-post.html' title='Начало'/><author><name>alexa</name><uri>http://www.blogger.com/profile/13790080041251149231</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_i8vlwHpnrg0/SbQdPxbGp-I/AAAAAAAAAAM/VNZlBWaeOI4/S220/Vasiliev_Alexey.jpg'/></author><thr:total>0</thr:total></entry></feed>
