<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;D0ENSXs5fSp7ImA9WhRXEkk.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918</id><updated>2011-12-18T23:01:38.525+01:00</updated><category term="Queryset" /><category term="Python" /><category term="Commands" /><category term="EuroPython" /><category term="Architecture" /><category term="Fabric" /><category term="Guerrilla Patch" /><category term="Deployment" /><category term="Structure" /><category term="Import" /><category term="Berlin" /><category term="Production" /><category term="Programming" /><category term="Administration" /><category term="Testing" /><category term="Admin" /><category term="Flash" /><category term="World" /><category term="Intermediate" /><category term="Git" /><category term="EuroDjangoCon" /><category term="Meetup" /><category term="Conference" /><category term="Forms" /><category term="JSON" /><category term="Blog Action Day" /><category term="Staging" /><category term="South" /><category term="App" /><category term="Virtualmin" /><category term="Graphs" /><category term="Subversion" /><category term="Vilnius" /><category term="Project" /><category term="XML" /><category term="Django CMS" /><category term="Basics" /><category term="Fields" /><category term="Search" /><category term="Models" /><category term="Haystack" /><category term="People" /><category term="Template Tags" /><category term="Advanced" /><category term="Widget" /><category term="Patterns" /><category term="Hacking" /><category term="JavaScript" /><category term="Migrations" /><title>DjangoTricks</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://djangotricks.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>28</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/djangotricks" /><feedburner:info uri="djangotricks" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><logo>http://aidas.bendoraitis.lt/images/django-tricks.png</logo><entry gd:etag="W/&quot;DUcFQn08eip7ImA9WhRXEEs.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-2205519145646051793</id><published>2011-12-16T21:23:00.000+01:00</published><updated>2011-12-16T21:23:33.372+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-16T21:23:33.372+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Queryset" /><category scheme="http://www.blogger.com/atom/ns#" term="Forms" /><category scheme="http://www.blogger.com/atom/ns#" term="Basics" /><title>The Usage of the only() Method</title><content type="html">Recently I found the &lt;code&gt;only()&lt;/code&gt; method of a queryset which can significantly improve the loading speed of forms with &lt;code&gt;ModelChoiceField&lt;/code&gt; or &lt;code&gt;ModelMultipleChoiceField&lt;/code&gt;.

Let's say we have a big Event model with thousands of instances:

&lt;pre&gt;&lt;code&gt;
class Event(models.Model):
    title = models.CharField(_("Title"), max_length=200)
    # ... lots of other fields ...
    def __unicode__(self):
        return self.title
&lt;/code&gt;&lt;/pre&gt;

Then we have a form with &lt;code&gt;ModelMultipleChoiceField&lt;/code&gt;:
&lt;pre&gt;&lt;code&gt;
class EventForm(forms.Form):
    related_events = forms.ModelMultipleChoiceField(
        queryset=Event.objects.all().only("id", "title"),
        label=_("Related Events"),
        required=False,
        )
    # ... other fields of the form ...
&lt;/code&gt;&lt;/pre&gt;

The used &lt;code&gt;only()&lt;/code&gt; method loads only the primary key and the title for the selection widget.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-2205519145646051793?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=JGBDMqQocT4:cAWB-FVJWOs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=JGBDMqQocT4:cAWB-FVJWOs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=JGBDMqQocT4:cAWB-FVJWOs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=JGBDMqQocT4:cAWB-FVJWOs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/JGBDMqQocT4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/2205519145646051793/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2011/12/usage-of-only-method.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/2205519145646051793?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/2205519145646051793?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/JGBDMqQocT4/usage-of-only-method.html" title="The Usage of the only() Method" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2011/12/usage-of-only-method.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcARnc-fyp7ImA9WhRSGEQ.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-9038282840911795241</id><published>2011-11-21T18:25:00.000+01:00</published><updated>2011-11-21T18:37:27.957+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-21T18:37:27.957+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><category scheme="http://www.blogger.com/atom/ns#" term="Admin" /><title>Custom Admin Filter Specification</title><content type="html">&lt;p&gt;Let's say we have a model Project with age groups as boolean fields:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
class Project(models.Model):
    # ...
    age_group_0_5 = models.BooleanField(u"From 0 to 5 years")
    age_group_6_12 = models.BooleanField(u"From 6 to 12 years")
    age_group_13_18 = models.BooleanField(u"From 13 to 18 years")
    age_group_19_27 = models.BooleanField(u"Fron 19 to 27 years")
    # ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In order to list them as one selection in the admin list filter, we need a specific filter specification.
Let's create it in the &lt;code&gt;admin.py&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
from django.contrib.admin.filterspecs import FilterSpec

class AgeChoicesFilterSpec(FilterSpec):
    def title(self):
        return u"Age group"
        
    def __init__(self, f, request, params, model, model_admin,
                 field_path=None):
        super(AgeChoicesFilterSpec, self).__init__(f, request, params, model,
                                                model_admin,
                                                field_path=field_path)
        self.request = request
        
    def choices(self, cl):
        yield {'selected': not(
                    self.request.GET.get('age_group_0_5', None) or 
                    self.request.GET.get('age_group_6_12', None) or 
                    self.request.GET.get('age_group_13_18', None) or 
                    self.request.GET.get('age_group_19_27', None) 
                    ),
               'query_string': cl.get_query_string(
                               {},
                               ['age_group_0_5', 'age_group_6_12', 'age_group_13_18', 'age_group_19_27']),
               'display': u'All'}
        yield {'selected': self.request.GET.get('age_group_0_5', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_0_5': 'True'},
                               ['age_group_6_12', 'age_group_13_18', 'age_group_19_27']),
               'display': u"From 0 to 5 years"}
        yield {'selected': self.request.GET.get('age_group_6_12', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_6_12': 'True'},
                               ['age_group_0_5', 'age_group_13_18', 'age_group_19_27']),
               'display': u"From 6 to 12 years"}
        yield {'selected': self.request.GET.get('age_group_13_18', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_13_18': 'True'},
                               ['age_group_0_5', 'age_group_6_12', 'age_group_19_27']),
               'display': u"From 13 to 18 years"}
        yield {'selected': self.request.GET.get('age_group_19_27', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_19_27': 'True'},
                               ['age_group_0_5', 'age_group_6_12', 'age_group_13_18']),
               'display': u"From 19 to 27 years"}

FilterSpec.filter_specs.insert(0, (lambda f: f.name == 'age_group_0_5',
                               AgeChoicesFilterSpec))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here the most important method of &lt;code&gt;FilterSpec&lt;/code&gt; is &lt;code&gt;choices&lt;/code&gt;. It defines the choices for the filter by dictionary with three keys:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;selected&lt;/code&gt; is the condition when this choice should be selected.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;query_string&lt;/code&gt; is the URL query string that will be formed to filter the list by. The query string is formed by &lt;code&gt;get_query_string&lt;/code&gt; method of &lt;code&gt;ChangeList&lt;/code&gt; instance. It takes two parameters: a dictionary of new parameters to add to the current query, and a list of parameters to remove from the current query.
    &lt;li&gt;&lt;code&gt;display&lt;/code&gt; is the human-readable string which will be shown for this choice in the filter.
&lt;/ul&gt;

&lt;p&gt;The new filter specification should be attached to a field. Here it will be applied to all fields with the name "age_group_0_5" and we have just one such field.&lt;/p&gt;

&lt;p&gt;The last thing to do is to define admin options for the &lt;code&gt;Project&lt;/code&gt; model:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
from django.contrib import admin
from models import Project

class ProjectAdmin(admin.ModelAdmin):
    # ...
    list_filter = ("age_group_0_5", )
    # ...
    
admin.site.register(Project, ProjectAdmin)
&lt;/code&gt;&lt;/pre&gt;

Now you know how to create a custom admin filter specification for your model. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-9038282840911795241?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=leqCKVE82Hw:RMSD-uvXZjk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=leqCKVE82Hw:RMSD-uvXZjk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=leqCKVE82Hw:RMSD-uvXZjk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=leqCKVE82Hw:RMSD-uvXZjk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/leqCKVE82Hw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/9038282840911795241/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2011/11/custom-admin-filter-specification.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/9038282840911795241?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/9038282840911795241?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/leqCKVE82Hw/custom-admin-filter-specification.html" title="Custom Admin Filter Specification" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2011/11/custom-admin-filter-specification.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEMQ38_fyp7ImA9WhRSEEk.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-8189100080257149759</id><published>2011-11-11T21:31:00.001+01:00</published><updated>2011-11-11T21:51:22.147+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-11T21:51:22.147+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Search" /><category scheme="http://www.blogger.com/atom/ns#" term="Haystack" /><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><category scheme="http://www.blogger.com/atom/ns#" term="Django CMS" /><title>Django CMS, Haystack, and Custom Plugins</title><content type="html">&lt;p&gt;When you need a full-text search for Django-CMS-based website, you can use &lt;a href="https://github.com/piquadrat/django-cms-search"&gt;Haystack&lt;/a&gt; and &lt;a href="https://github.com/piquadrat/django-cms-search"&gt;django-cms-search&lt;/a&gt;. The latter module ensures that all CMS Pages get indexed.&lt;/p&gt;

&lt;p&gt;One important thing to mention is that if you use any custom Plugins, &lt;code&gt;search_fields&lt;/code&gt; need to be defined for them, so that the Pages using them are indexed properly. For example, here is an Overview plugin which makes its title and description searchable:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
from django.db import models
from django.utils.translation import ugettext_lazy as _
from cms.models import CMSPlugin

class Overview(CMSPlugin):
    title = models.CharField(_('Title'), max_length=200)
    description = models.TextField(_('Description'))
    url = models.URLField(_('url'), max_length=200, blank=True)
    
    search_fields = ("title", "description")
    
    def __unicode__(self):
        return self.title
        
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For more information check the documentation online:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://docs.haystacksearch.org/dev/"&gt;Haystack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://docs.django-cms.org/"&gt;Django CMS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://django-cms-search.readthedocs.org/"&gt;django-cms-search&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-8189100080257149759?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=X7x2qFgbM-Y:a2xX5qoaPyQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=X7x2qFgbM-Y:a2xX5qoaPyQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=X7x2qFgbM-Y:a2xX5qoaPyQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=X7x2qFgbM-Y:a2xX5qoaPyQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/X7x2qFgbM-Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/8189100080257149759/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2011/11/django-cms-haystack-and-custom-plugins.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/8189100080257149759?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/8189100080257149759?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/X7x2qFgbM-Y/django-cms-haystack-and-custom-plugins.html" title="Django CMS, Haystack, and Custom Plugins" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2011/11/django-cms-haystack-and-custom-plugins.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08CSHw_fCp7ImA9WxFXGU8.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-8677451977733937496</id><published>2010-05-24T20:14:00.003+02:00</published><updated>2010-05-27T02:57:49.244+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-27T02:57:49.244+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Administration" /><category scheme="http://www.blogger.com/atom/ns#" term="Deployment" /><category scheme="http://www.blogger.com/atom/ns#" term="South" /><category scheme="http://www.blogger.com/atom/ns#" term="Production" /><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><category scheme="http://www.blogger.com/atom/ns#" term="Subversion" /><category scheme="http://www.blogger.com/atom/ns#" term="EuroDjangoCon" /><category scheme="http://www.blogger.com/atom/ns#" term="Fabric" /><category scheme="http://www.blogger.com/atom/ns#" term="Staging" /><title>Deployment Using Fabric</title><content type="html">&lt;p&gt;Yesterday just a night before &lt;a href="http://djangocon.eu/"&gt;EuroDjangoCon 2010&lt;/a&gt;, I was writing deployment scripts for my personal project. I have been using analogous scripts at least for a year for my work projects of different scopes. And I can prove that it's really worth to have such scripts, because they save a lot of time and let you avoid confusion and mistakes.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Most of the projects are continuously improved, so the main purpose of deployment in my case is not the initial installation, but handy and fast updating.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;For each website that I am managing, there are two environments set up under different domains, where one of them is production environment and another one is staging/testing environment. The configuration of staging environment mirrors the production environment. Both have their own webserver configurations, codebases, databases, and uploaded media files. At first the newest features are tested in the staging environment with the copy of live data, and then they are published in the production environment.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;I am using &lt;a href="http://svnbook.red-bean.com/"&gt;subversion&lt;/a&gt; for version control with third-party modules defined as &lt;a href="http://svnbook.red-bean.com/en/1.0/ch07s03.html"&gt;svn:externals&lt;/a&gt; and put under the same codebase. Unfortunately, some of the modules have to be copied if they were originally taken from Git, Bazaar, or other places. Anyway, keeping all the codebase under SVN makes the initial installation easier and ensures that you can easily update the whole combination of module dependencies.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;In addition, I am using &lt;a href="http://south.aeracode.org/docs/index.html"&gt;south migrations&lt;/a&gt;, to make the changes to the database schema automagically.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Earlier the process of updating consisted of these manual steps:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Set up under-construction page for the staging/testing site either by changing &lt;code&gt;.htaccess&lt;/code&gt; file or &lt;code&gt;httpd.conf&lt;/code&gt; and restarting webserver (depending on the server configuration).&lt;/li&gt;
&lt;li&gt;Export the dump of production database and import it into the staging database.&lt;/li&gt;
&lt;li&gt;Move the media uploads from production environment to staging environment.&lt;/li&gt;
&lt;li&gt;Update code from subversion trunk.&lt;/li&gt;
&lt;li&gt;Migrate database schema and/or do any database changes.&lt;/li&gt;
&lt;li&gt;Restart apache and memcached.&lt;/li&gt;
&lt;li&gt;Test if everything is working as expected.&lt;/li&gt;
&lt;li&gt;Unset under-construction page.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Then some similar instructions should be executed in the production environment:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Set up under-construction page for the production site.&lt;/li&gt;
&lt;li&gt;Backup database in case if anything goes wrong, so that you could recover.&lt;/li&gt;
&lt;li&gt;Update code from subversion.&lt;/li&gt;
&lt;li&gt;Run database migrations.&lt;/li&gt;
&lt;li&gt;Test everything.&lt;/li&gt;
&lt;li&gt;Unset under-construction page.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;p&gt;And that's quite a long routine to do manually and also sometimes when the changes were small, I was always tempted to skip some steps which increased the risk to fail. But then I started using &lt;a href="http://docs.fabfile.org/0.9.0/"&gt;Fabric&lt;/a&gt; to automate the process. Fabric is python-based deployment tool providing simple API for FTP and SSH connections as well as for local shell calls.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;To use Fabric, you have to install it in your local machine and then write scripts called fabfiles for each website, you are updating. For example, updating the staging environment in all my cases is as simple as running&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# cd path/to/directory/with/fabfile.py/
# fab staging deploy&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and then entering user passwords and answering questions like&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Backup database ([y]/n)? _&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The fabfiles could also be without user interaction, but I made those dialogs to be able to monitor the process and stop it at any point in case if I need some specific additional manual work.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;For security reasons, you should avoid keeping passwords under subversion or in the deployment script. Much better it is to write some bash scripts in the remote servers which would do the work and then to address them from your fabfile.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Here is an example of possible fabfile. Keep in mind that different apache configurations and some bash scripts are something that you have to write yourself depending on your server configuration. Also the fabfile itself might slightly differ depending on the server specifics.&lt;/p&gt;&lt;br /&gt;
&lt;script src="http://gist.github.com/412191.js"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;According to &lt;a href="http://www.thilo-fromm.de/"&gt;Thilo Fromm&lt;/a&gt; with whom I talked today at EuroDjangoCon, another way to create deployment scripts is to create linux-distribution-related packages which you could install using installation tools within the distribution, for example apt-get for Debian or rpm for Suse. However, in my opinion, that kind of deployment fits better to those cases when you don't have control of the production server and need to deliver a final product to corporate clients, or where you need to install the same project/product to multiple servers.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-8677451977733937496?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=IqYQQfDV7a4:10vknU3zgks:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=IqYQQfDV7a4:10vknU3zgks:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=IqYQQfDV7a4:10vknU3zgks:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=IqYQQfDV7a4:10vknU3zgks:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/IqYQQfDV7a4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/8677451977733937496/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2010/05/deployment-using-fabric.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/8677451977733937496?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/8677451977733937496?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/IqYQQfDV7a4/deployment-using-fabric.html" title="Deployment Using Fabric" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2010/05/deployment-using-fabric.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08FSXoyfip7ImA9WxNVEEs.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-971687316571891550</id><published>2009-10-20T18:56:00.006+02:00</published><updated>2009-10-20T20:03:38.496+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-20T20:03:38.496+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Git" /><category scheme="http://www.blogger.com/atom/ns#" term="Project" /><category scheme="http://www.blogger.com/atom/ns#" term="Basics" /><title>Weather App on Github</title><content type="html">As promised, I put the code of the &lt;a href="http://github.com/archatas/django-climate-change"&gt;climate_change app online&lt;/a&gt;. For that reason, I created an account on &lt;a href="http://github.com/"&gt;github&lt;/a&gt; trying to use &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; for the first time. This is also the first time I am using a &lt;a href="http://en.wikipedia.org/wiki/Distributed_revision_control"&gt;distributed version control system&lt;/a&gt;, i.e. you can create branches of the project, develop the forks separately on different computers, and merge them in any order. There is no main centralized repository. The code on github that I uploaded is just one initial copy.&lt;br /&gt;&lt;br /&gt;I am still new to Git commands and workflows. You can share your knowledge with me. For example, maybe you know some good cheat sheet or writings about Git. Or you can try to fork the project I committed, change it somehow and then push for merging (not sure if I'm using correct jargon). I will share with you what I find interesting and useful about Git while learning it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-971687316571891550?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=attm1SF0Kh0:2Tm7eSE9U-g:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=attm1SF0Kh0:2Tm7eSE9U-g:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=attm1SF0Kh0:2Tm7eSE9U-g:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=attm1SF0Kh0:2Tm7eSE9U-g:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/attm1SF0Kh0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/971687316571891550/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/10/weather-app-on-github.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/971687316571891550?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/971687316571891550?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/attm1SF0Kh0/weather-app-on-github.html" title="Weather App on Github" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/10/weather-app-on-github.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UBRXw9eCp7ImA9WxNWGUw.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-4515978427128610855</id><published>2009-10-19T02:00:00.004+02:00</published><updated>2009-10-19T02:14:14.260+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-19T02:14:14.260+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Intermediate" /><category scheme="http://www.blogger.com/atom/ns#" term="App" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="JSON" /><category scheme="http://www.blogger.com/atom/ns#" term="Project" /><category scheme="http://www.blogger.com/atom/ns#" term="Graphs" /><category scheme="http://www.blogger.com/atom/ns#" term="Blog Action Day" /><category scheme="http://www.blogger.com/atom/ns#" term="Flash" /><title>Weather App Tutorial. Part 5 of 5. Graph View</title><content type="html">I had some problems with the internet, so I am posting this entry a little later that expected. But anyway, this is it - the final part of the tutorial about the weather app. You can read the first four parts here: &lt;a href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-1-of-5.html"&gt;1&lt;/a&gt;, &lt;a href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-2-of-5-models.html"&gt;2&lt;/a&gt;, &lt;a href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-3-of-5.html"&gt;3&lt;/a&gt;, &lt;a href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-4-of-5.html"&gt;4&lt;/a&gt;. Today I will show you how to display a chart with statistical data from the collected weather details.&lt;br /&gt;&lt;br /&gt;I googled a little to find out what charting system to use for this small project. And I found &lt;a href="http://teethgrinder.co.uk/open-flash-chart-2/tutorial-other-libraries.php#python"&gt;Open Flash Chart&lt;/a&gt; as an interesting solution. It's a flash-based charting system which uses Ajax requests to get the data for the graphs. That means that we'll need a view which will show the graphs and another view, which will provide the data. Each location will have its own page displaying a chart, so it would be nice to have URLs where each location would be accessed by a slug. We already have a field &lt;code&gt;sysname&lt;/code&gt; which could be used here, but my best practice is to have separate fields for url slugs which might be changed at anytime and sysnames which should be defined once and not changed at all because the sysname binds an object with a template.&lt;br /&gt;&lt;br /&gt;So I add this new field to the &lt;code&gt;Location&lt;/code&gt; model in &lt;code&gt;models.py&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class Location(models.Model):&lt;br /&gt;    #...&lt;br /&gt;    slug = models.SlugField(&lt;br /&gt;        _("slug for urls"),&lt;br /&gt;        max_length=200,&lt;br /&gt;        unique=True,&lt;br /&gt;        blank=True,&lt;br /&gt;        default="",&lt;br /&gt;        )&lt;br /&gt;    #...&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then I create and apply a migration for it:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;python manage.py startmigration add_slug --auto&lt;br /&gt;python manage.py migrate climate_change&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's run the webserver once again. Go to &lt;code&gt;http://127.0.0.1:8000/admin/climate_change/location/1/&lt;/code&gt;, add "berlin" as a slug and save the record.&lt;br /&gt;&lt;br /&gt;Next I need to define a view which will display the graphs. This looks as simple as this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# -*- coding: UTF-8 -*-&lt;br /&gt;from django.shortcuts import get_object_or_404, render_to_response&lt;br /&gt;&lt;br /&gt;Location = models.get_model("climate_change", "Location")&lt;br /&gt;&lt;br /&gt;def display_charts(request, slug):&lt;br /&gt;    location = get_object_or_404(Location, slug=slug)&lt;br /&gt;    return render_to_response(&lt;br /&gt;        "climate_change/charts.html",&lt;br /&gt;        {'location': location},&lt;br /&gt;        context_instance=RequestContext(request),&lt;br /&gt;        )&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Whereas the template created at &lt;code&gt;templates/climate_change/charts.html&lt;/code&gt; includes this code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;{% extends "base.html" %}&lt;br /&gt;&lt;br /&gt;{% load i18n %}&lt;br /&gt;&lt;br /&gt;{% block title %}{{ location.name }}{% endblock %}&lt;br /&gt;&lt;br /&gt;{% block header %}&lt;br /&gt;    &amp;lt;/code&amp;gt;&amp;lt;h1&amp;gt;{{ location.name }}&amp;lt;/h1&amp;gt;&lt;br /&gt;{% endblock %}&lt;br /&gt;&lt;br /&gt;{% block content %}&lt;br /&gt;    &amp;lt;div class="chart-temperature"&amp;gt;&lt;br /&gt;        &amp;lt;h2&amp;gt;{% trans "Temperature" %}&amp;lt;/h2&amp;gt;&lt;br /&gt;        &amp;lt;object type="application/x-shockwave-flash" data="/media/flash/open-flash-chart.swf?data-file={% url climate_change_temperature slug=location.slug %}" height="250" width="500"&amp;gt;&lt;br /&gt;            &amp;lt;param name="movie" value="/media/flash/open-flash-chart.swf?data-file={% url climate_change_temperature slug=location.slug %}"&amp;gt;&lt;br /&gt;            &amp;lt;param name="allowScriptAccess" value="sameDomain"&amp;gt;&lt;br /&gt;            &amp;lt;param name="quality" value="high"&amp;gt;&lt;br /&gt;        &amp;lt;/object&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div class="chart-humidity"&amp;gt;&lt;br /&gt;        &amp;lt;h2&amp;gt;{% trans "Humidity" %}&amp;lt;/h2&amp;gt;&lt;br /&gt;        &amp;lt;object type="application/x-shockwave-flash" data="/media/flash/open-flash-chart.swf?data-file={% url climate_change_humidity slug=location.slug %}" height="250" width="500"&amp;gt;&lt;br /&gt;            &amp;lt;param name="movie" value="/media/flash/open-flash-chart.swf?data-file={% url climate_change_humidity slug=location.slug %}"&amp;gt;&lt;br /&gt;            &amp;lt;param name="allowScriptAccess" value="sameDomain"&amp;gt;&lt;br /&gt;            &amp;lt;param name="quality" value="high"&amp;gt;&lt;br /&gt;        &amp;lt;/object&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div class="chart-wind-speed"&amp;gt;&lt;br /&gt;        &amp;lt;h2&amp;gt;{% trans "Wind speed" %}&amp;lt;/h2&amp;gt;&lt;br /&gt;        &amp;lt;object type="application/x-shockwave-flash" data="/media/flash/open-flash-chart.swf?data-file={% url climate_change_wind_speed slug=location.slug %}" height="250" width="500"&amp;gt;&lt;br /&gt;            &amp;lt;param name="movie" value="/media/flash/open-flash-chart.swf?data-file={% url climate_change_wind_speed slug=location.slug %}"&amp;gt;&lt;br /&gt;            &amp;lt;param name="allowScriptAccess" value="sameDomain"&amp;gt;&lt;br /&gt;            &amp;lt;param name="quality" value="high"&amp;gt;&lt;br /&gt;        &amp;lt;/object&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div class="chart-visibility"&amp;gt;&lt;br /&gt;        &amp;lt;h2&amp;gt;{% trans "Visibility" %}&amp;lt;/h2&amp;gt;&lt;br /&gt;        &amp;lt;object type="application/x-shockwave-flash" data="/media/flash/open-flash-chart.swf?data-file={% url climate_change_visibility slug=location.slug %}" height="250" width="500"&amp;gt;&lt;br /&gt;            &amp;lt;param name="movie" value="/media/flash/open-flash-chart.swf?data-file={% url climate_change_visibility slug=location.slug %}"&amp;gt;&lt;br /&gt;            &amp;lt;param name="allowScriptAccess" value="sameDomain"&amp;gt;&lt;br /&gt;            &amp;lt;param name="quality" value="high"&amp;gt;&lt;br /&gt;        &amp;lt;/object&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;{% endblock %}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Also let's not forget to download the Open Flash Chart flash file itself which is in the archive of &lt;a href="http://sourceforge.net/projects/openflashchart/files/open-flash-chart/Version%202%20Lug%20Wyrm%20Charmer/"&gt;this package&lt;/a&gt;. I placed the file &lt;code&gt;open-flash-chart.swf&lt;/code&gt; at &lt;code&gt;media/flash&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Originally, Open Flash Chart uses PHP on the server side to provide data to the flash-based client side. But fortunately there is a port to python which I will be using for forming the data to the charts. I don't want to get dirty fingers by creating all this manually forming JSON object using &lt;code&gt;simplejson&lt;/code&gt;, especially because there is not complete reference of Open-Flash-Chart API (just examples and tutorials). So I downloaded &lt;a href="http://open-flash-chart-python.googlecode.com/files/Python%202.10.zip"&gt;python port&lt;/a&gt;, extracted the content and copied &lt;code&gt;OpenFlashChart.py&lt;/code&gt; to &lt;code&gt;climate_change_env/lib/python2.5/site-packages/&lt;/code&gt; which is the location of python libraries which will be installed a.k.a. under python path for the virtual environment "climate_change_env".&lt;br /&gt;&lt;br /&gt;Using the examples found at a &lt;a href="http://code.google.com/p/open-flash-chart-python/wiki/Tutorial"&gt;tutorial&lt;/a&gt;, I created a view for providing chart data. The &lt;code&gt;views.py&lt;/code&gt; looks now like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# -*- coding: UTF-8 -*-&lt;br /&gt;from datetime import datetime, timedelta&lt;br /&gt;from random import random&lt;br /&gt;&lt;br /&gt;# http://open-flash-chart-python.googlecode.com/files/Python%202.10.zip&lt;br /&gt;from OpenFlashChart import Chart&lt;br /&gt;&lt;br /&gt;from django.db import models&lt;br /&gt;from django.http import HttpResponse&lt;br /&gt;from django.template import RequestContext&lt;br /&gt;from django.shortcuts import get_object_or_404, render_to_response&lt;br /&gt;from django.utils.translation import ugettext as _&lt;br /&gt;from django.utils.encoding import force_unicode&lt;br /&gt;&lt;br /&gt;Location = models.get_model("climate_change", "Location")&lt;br /&gt;WeatherLog = models.get_model("climate_change", "WeatherLog")&lt;br /&gt;&lt;br /&gt;def display_charts(request, slug):&lt;br /&gt;    location = get_object_or_404(Location, slug=slug)&lt;br /&gt;    return render_to_response(&lt;br /&gt;        "climate_change/charts.html",&lt;br /&gt;        {'location': location},&lt;br /&gt;        context_instance=RequestContext(request),&lt;br /&gt;        )&lt;br /&gt;&lt;br /&gt;def rgb_to_html_color(r, g, b):&lt;br /&gt;    """ convert an R, G, B to #RRGGBB """&lt;br /&gt;    return '#%02x%02x%02x' % (r, g, b)&lt;br /&gt;&lt;br /&gt;def json_get_statistics(request, slug, field):&lt;br /&gt;    location = get_object_or_404(Location, slug=slug)&lt;br /&gt;    extreme_values = location.weatherlog_set.aggregate(&lt;br /&gt;        models.Min(field),&lt;br /&gt;        models.Max(field),&lt;br /&gt;        models.Min("timestamp"),&lt;br /&gt;        models.Max("timestamp"),&lt;br /&gt;        )&lt;br /&gt;    elements = []&lt;br /&gt;&lt;br /&gt;    for year in range(&lt;br /&gt;        extreme_values['timestamp__min'].year,&lt;br /&gt;        extreme_values['timestamp__max'].year + 1,&lt;br /&gt;        ):&lt;br /&gt;        element = Chart()&lt;br /&gt;        monthly_values = []&lt;br /&gt;        for month in range (1, 13):&lt;br /&gt;            monthly = location.weatherlog_set.filter(&lt;br /&gt;                timestamp__year=year,&lt;br /&gt;                timestamp__month=month,&lt;br /&gt;                ).aggregate(models.Avg(field))&lt;br /&gt;            monthly_values.append(monthly['%s__avg' % field])&lt;br /&gt;        element.values = monthly_values&lt;br /&gt;        element.type = "line"&lt;br /&gt;        element.dot_style.type = "dot"&lt;br /&gt;        element.dot_style.dot_size = 5&lt;br /&gt;        random_html_color = rgb_to_html_color(&lt;br /&gt;            r=63+int(random() * 128),&lt;br /&gt;            g=63+int(random() * 128),&lt;br /&gt;            b=63+int(random() * 128),&lt;br /&gt;            )&lt;br /&gt;        element.dot_style.colour = random_html_color&lt;br /&gt;        element.width = 4&lt;br /&gt;        element.colour = random_html_color&lt;br /&gt;        element.text = str(year)&lt;br /&gt;        element.font_size = 10&lt;br /&gt;        elements.append(element)&lt;br /&gt;    &lt;br /&gt;    # Create chart&lt;br /&gt;    chart = Chart()&lt;br /&gt;    chart.y_axis.min = float(str(extreme_values['%s__min' % field]))&lt;br /&gt;    chart.y_axis.max = float(str(extreme_values['%s__max' % field]))&lt;br /&gt;    chart.y_axis.font_size = 10&lt;br /&gt;    chart.title.text = force_unicode(WeatherLog._meta.get_field(field).verbose_name)&lt;br /&gt;    chart.x_axis.labels.labels = [&lt;br /&gt;        _("Jan"), _("Feb"), _("Mar"), _("Apr"),&lt;br /&gt;        _("May"), _("Jun"), _("Jul"), _("Aug"),&lt;br /&gt;        _("Sep"), _("Oct"), _("Nov"), _("Dec"),&lt;br /&gt;        ]&lt;br /&gt;&lt;br /&gt;    #&lt;br /&gt;    # here we add our data sets to the chart:&lt;br /&gt;    #&lt;br /&gt;    chart.elements = elements&lt;br /&gt;    &lt;br /&gt;    return HttpResponse(chart.create())&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If I run the webserver again and go to &lt;code&gt;http://127.0.0.1:8000/climate-change/berlin/&lt;/code&gt;, I see a single dot in each graph.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/_nG3SKqiLARM/StUaTh7rlpI/AAAAAAAAAXA/Vi6GOH_GTgE/blog_action_day_2009_single_dot_chart.png" border="0"&gt;&lt;br /&gt;&lt;br /&gt;We need some more data. There is Time Machine app in the Applications directory in my computer, so let's use it to move forward to year 2012 to check weather statistics. Damn it! It didn't function properly. Then I'll need to generate statistical data myself. Anyway, according to Mark Twain, there are three kinds of lies: lies, damned lies, and statistics.&lt;br /&gt;&lt;br /&gt;I'll use python interactive development environment a.k.a. shell to prepopulate that data.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;python manage.py shell&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; from django.db import models&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; Location = models.get_model("climate_change", "Location")&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; location = Location.objects.get(sysname="berlin")&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; extremes = (&lt;br /&gt;...     (-5, 4), (-4, 5), (-2, 10), (2, 15),&lt;br /&gt;...     (6, 20), (9, 24), (11, 25), (10, 25),&lt;br /&gt;...     (7, 20), (4, 15), (0, 9), (-2, 5),&lt;br /&gt;...     )&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; from random import random&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; from datetime import datetime&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; for year in range(2009, 2012):&lt;br /&gt;...     for month in range(1, 13):&lt;br /&gt;...         temp_range = extremes[month-1][1] - extremes[month-1][0]&lt;br /&gt;...         t = extremes[month-1][0] + int(random() * temp_range)&lt;br /&gt;...         location.weatherlog_set.create(&lt;br /&gt;...             timestamp=datetime(year, month, 1),&lt;br /&gt;...             temperature=t,&lt;br /&gt;...             humidity=int(random()*100),&lt;br /&gt;...             wind_speed="%.2f" % (random()*372),,&lt;br /&gt;...             visibility="%.2f" % (random()*100),&lt;br /&gt;...             )&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I defined minimal and maximal temperatures for each month in a tuple of tuples. Then I went through three years and for each month of that year I added random temperatures which would be in the range of minimal and maximal temperatures for that month. Humidity, wind speed and visibility are randomized without monthly ranges. The graph after data population looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://2.bp.blogspot.com/_nG3SKqiLARM/StUabKs6GaI/AAAAAAAAAXI/_7Ki15ixtKM/blog_action_day_2009_charts.png" border="0"&gt;&lt;br /&gt;&lt;br /&gt;That's it. Finally, I completed this tutorial. You had a chance to watch the process of Django app development. You saw, how virtualmin, migrations and third-party libraries can be used. Hopefully, you got some inspirations to create something cool by yourself just for fun.&lt;br /&gt;&lt;br /&gt;I will publish the full code soon somewhere and you'll have a chance to download it and modify.&lt;br /&gt;&lt;br /&gt;If something is too confusing, you don't understand some part of the tutorial, or you think that it could be improved somehow, don't hesitate to write comments.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-4515978427128610855?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=R7PHE_3nS18:oVMsuKu8z0g:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=R7PHE_3nS18:oVMsuKu8z0g:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=R7PHE_3nS18:oVMsuKu8z0g:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=R7PHE_3nS18:oVMsuKu8z0g:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/R7PHE_3nS18" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/4515978427128610855/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-5-of-5-graph.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/4515978427128610855?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/4515978427128610855?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/R7PHE_3nS18/weather-app-tutorial-part-5-of-5-graph.html" title="Weather App Tutorial. Part 5 of 5. Graph View" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_nG3SKqiLARM/StUaTh7rlpI/AAAAAAAAAXA/Vi6GOH_GTgE/s72-c/blog_action_day_2009_single_dot_chart.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-5-of-5-graph.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUEQX05cCp7ImA9WxNWF04.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-6431764789613468661</id><published>2009-10-17T00:30:00.046+02:00</published><updated>2009-10-17T00:30:00.328+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-17T00:30:00.328+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Intermediate" /><category scheme="http://www.blogger.com/atom/ns#" term="Administration" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Template Tags" /><category scheme="http://www.blogger.com/atom/ns#" term="South" /><category scheme="http://www.blogger.com/atom/ns#" term="Blog Action Day" /><category scheme="http://www.blogger.com/atom/ns#" term="Migrations" /><title>Weather App Tutorial. Part 4 of 5. Template Tag</title><content type="html">If you followed the first parts of the tutorial, you should have basic understanding how to create an app with models, set up administration, and retrieve data from third-party services. This part is about displaying collected data in any template using custom template tag.&lt;br /&gt;&lt;br /&gt;At first, we need to create a directory &lt;code&gt;templatetags&lt;/code&gt; containing an empty &lt;code&gt;__init__.py&lt;/code&gt; file in the &lt;code&gt;climate_change&lt;/code&gt; directory.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;mkdir -p climate_change/templatetags&lt;br /&gt;touch climate_change/templatetags/__init__.py&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I will call the template library &lt;code&gt;weather&lt;/code&gt;. So I have to create a file &lt;code&gt;weather.py&lt;/code&gt; in &lt;code&gt;climate_change/templatetags&lt;/code&gt; and define and register the template tag in that file. The template tag &lt;code&gt;get_current_weather&lt;/code&gt; should display the current weather for a chosen location. To define what location you choose, you could refer to &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;location_id&lt;/code&gt;, but none of them is appropriate for this reason. &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;location_id&lt;/code&gt; are not remember-able and not informative enough, whereas the name might be changed to translate the city to another language or to add some more specifics and this change would detach the template tag from the location. For those reasons, it is best to create a new field &lt;code&gt;sysname&lt;/code&gt; for the location model which would have a unique non-changeable value as a textual humanized identifier for templates.&lt;br /&gt;&lt;br /&gt;But wait! It's such a pain to add new fields and modify database schema... Not, if you are using south! Let's quickly install it and then put it under INSTALLED_APPS.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;easy_install south&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#...&lt;br /&gt;INSTALLED_APPS = (&lt;br /&gt;    # django core&lt;br /&gt;    "django.contrib.auth",&lt;br /&gt;    "django.contrib.contenttypes",&lt;br /&gt;    "django.contrib.sessions",&lt;br /&gt;    "django.contrib.sites",&lt;br /&gt;    "django.contrib.admin",&lt;br /&gt;    # third-party&lt;br /&gt;    "south",&lt;br /&gt;    # project-specific&lt;br /&gt;    "climate_change",&lt;br /&gt;    )&lt;br /&gt;#...&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that south was installed in the virtual environment not spoiling the global python namespace. Now we will syncronize the database to create &lt;code&gt;south_history&lt;/code&gt; table, create the initial migration for &lt;code&gt;climate_change&lt;/code&gt; and apply it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# create the missing database table from south app&lt;br /&gt;python manage.py syncdb&lt;br /&gt;# create initial migration for climate_change app which will be used by new projects&lt;br /&gt;python manage.py startmigration climate_change --initial&lt;br /&gt;# fake this migration for this project&lt;br /&gt;python manage.py migrate climate_change --fake&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we can finally add the new &lt;code&gt;sysname&lt;/code&gt; field, create migration for it and apply it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#...&lt;br /&gt;class Location(models.Model):&lt;br /&gt;    sysname = models.SlugField(&lt;br /&gt;        _("system name"),&lt;br /&gt;        max_length=200,&lt;br /&gt;        unique=True,&lt;br /&gt;        blank=True,&lt;br /&gt;        default="",&lt;br /&gt;        help_text=_("Do not change this value"),&lt;br /&gt;        )&lt;br /&gt;    name = models.CharField(_("name"), max_length=200)&lt;br /&gt;    location_id = models.CharField(&lt;br /&gt;        _("location ID"),&lt;br /&gt;        max_length=20,&lt;br /&gt;        help_text=_("Location IDs can be retrieved from URLs of weather "&lt;br /&gt;            "at specific cities at Yahoo! Weather, e.g. GMXX0008 from "&lt;br /&gt;            "http://weather.yahoo.com/forecast/GMXX0008.html"),&lt;br /&gt;        )&lt;br /&gt;#...&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# create a new migration called "add_sysname"&lt;br /&gt;python manage.py startmigration climate_change add_sysname --auto&lt;br /&gt;# apply it to the database&lt;br /&gt;python manage.py migrate climate_change&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I had to set the default value to empty string because otherwise south throws exception when I use sqlite3. Anyway, after running those commands, I started the built in webserver again and added the &lt;code&gt;sysname&lt;/code&gt; "berlin" to the record of Berlin's location.&lt;br /&gt;&lt;br /&gt;We can get back to &lt;code&gt;weather.py&lt;/code&gt; and add the template tag there&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# -*- coding: UTF-8 -*-&lt;br /&gt;from django.db import models&lt;br /&gt;from django import template&lt;br /&gt;from django.template import loader&lt;br /&gt;&lt;br /&gt;register = template.Library()&lt;br /&gt;&lt;br /&gt;### TAGS ###&lt;br /&gt;&lt;br /&gt;def do_get_current_weather(parser, token):&lt;br /&gt;    """&lt;br /&gt;    Returns the latest known weather information.&lt;br /&gt;&lt;br /&gt;    Usage::&lt;br /&gt;&lt;br /&gt;        {% get_current_weather in &amp;lt;location_sysname&gt; [using &amp;lt;template_path&gt;] [as &amp;lt;var_name&gt;] %}&lt;br /&gt;    &lt;br /&gt;    Examples::&lt;br /&gt;&lt;br /&gt;        {% get_current_weather in "berlin" using "climate_change/custom_weather.html" %}&lt;br /&gt;&lt;br /&gt;        {% get_current_weather in "london" as current_weather %}&lt;br /&gt;        var sCurrentWeather = "{{ current_weather|escapejs }}";&lt;br /&gt;&lt;br /&gt;    """    &lt;br /&gt;    bits = token.split_contents()&lt;br /&gt;    tag_name = bits.pop(0)&lt;br /&gt;    template_path = ""&lt;br /&gt;    var_name = ""&lt;br /&gt;    location_sysname = ""&lt;br /&gt;    try:&lt;br /&gt;        while bits:&lt;br /&gt;            first_word = bits.pop(0)&lt;br /&gt;            second_word = bits.pop(0)&lt;br /&gt;            if first_word == "in":&lt;br /&gt;                location_sysname = second_word&lt;br /&gt;            elif first_word == "using":&lt;br /&gt;                template_path = second_word&lt;br /&gt;            elif first_word == "as":&lt;br /&gt;                var_name = second_word&lt;br /&gt;                    &lt;br /&gt;    except ValueError:&lt;br /&gt;        raise template.TemplateSyntaxError, "get_current_weather tag requires a following syntax: {% get_current_weather [using &amp;lt;template_path&gt;] [as &amp;lt;var_name&gt;] %}"&lt;br /&gt;    return CurrentWeatherNode(tag_name, location_sysname, template_path, var_name)&lt;br /&gt;&lt;br /&gt;class CurrentWeatherNode(template.Node):&lt;br /&gt;    def __init__(self, tag_name, location_sysname, template_path, var_name):&lt;br /&gt;        self.tag_name = tag_name&lt;br /&gt;        self.location_sysname = location_sysname&lt;br /&gt;        self.template_path = template_path&lt;br /&gt;        self.var_name = var_name&lt;br /&gt;    def render(self, context):&lt;br /&gt;        location_sysname = template.resolve_variable(&lt;br /&gt;            self.location_sysname,&lt;br /&gt;            context,&lt;br /&gt;            )&lt;br /&gt;        template_path = ""&lt;br /&gt;        if self.template_path:&lt;br /&gt;            template_path = template.resolve_variable(&lt;br /&gt;                self.template_path,&lt;br /&gt;                context,&lt;br /&gt;                )&lt;br /&gt;        context.push()&lt;br /&gt;        WeatherLog = models.get_model("climate_change", "WeatherLog")&lt;br /&gt;        logs = WeatherLog.objects.filter(&lt;br /&gt;            location__sysname=location_sysname,&lt;br /&gt;            ).order_by("-timestamp")&lt;br /&gt;        if logs:&lt;br /&gt;            context['weather'] = logs[0]&lt;br /&gt;        output = loader.render_to_string(&lt;br /&gt;            [template_path, "climate_change/current_weather.html"],&lt;br /&gt;            context,&lt;br /&gt;            )&lt;br /&gt;        context.pop()&lt;br /&gt;        if self.var_name:&lt;br /&gt;            context[self.var_name] = output&lt;br /&gt;            return ""&lt;br /&gt;        else:&lt;br /&gt;            return output&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;register.tag("get_current_weather", do_get_current_weather)&lt;br /&gt;&lt;br /&gt;### FILTERS ###&lt;br /&gt;&lt;br /&gt;# none at the moment&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you might see from the code, the template tag is using a template which can be redefined by the template designer. We still need the default template itself, so I will create a directory &lt;code&gt;templates/climate_change&lt;/code&gt; and a file &lt;code&gt;current_weather.html&lt;/code&gt; with this content:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;{% load i18n %}&lt;br /&gt;&amp;lt;div class="current-wheather"&gt;&lt;br /&gt;    &amp;lt;h3&gt;{{ weather.location.name }}&amp;lt;/h3&gt;&lt;br /&gt;    &amp;lt;dl&gt;&lt;br /&gt;        &amp;lt;dt&gt;{% trans "Temperature" %}:&amp;lt;/dt&gt;&lt;br /&gt;        &amp;lt;dd&gt;{{ weather.temperature }}° C&amp;lt;/dd&gt;&lt;br /&gt;        &amp;lt;dt&gt;{% trans "Humidity" %}:&amp;lt;/dt&gt;&lt;br /&gt;        &amp;lt;dd&gt;{{ weather.humidity }} %&amp;lt;/dd&gt;&lt;br /&gt;        &amp;lt;dt&gt;{% trans "Wind speed" %}:&amp;lt;/dt&gt;&lt;br /&gt;        &amp;lt;dd&gt;{{ weather.wind_speed }} km/h&amp;lt;/dd&gt;&lt;br /&gt;        &amp;lt;dt&gt;{% trans "Visibility" %}:&amp;lt;/dt&gt;&lt;br /&gt;        &amp;lt;dd&gt;{{ weather.visibility }} km&amp;lt;/dd&gt;&lt;br /&gt;    &amp;lt;/dl&gt;&lt;br /&gt;&amp;lt;/div&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;How can I test the template tag? I will need a new page which will include it. So I will add a rule in &lt;code&gt;urls.py&lt;/code&gt; to redirect root url to &lt;code&gt;index.html&lt;/code&gt; which will extend from &lt;code&gt;base.html&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;So the &lt;code&gt;base.html&lt;/code&gt; looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;{% block doctype %}&amp;gt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;{% endblock %}&lt;br /&gt;&lt;br /&gt;{% load i18n %}&lt;br /&gt;&lt;br /&gt;&amp;lt;html&gt;&lt;br /&gt;&amp;lt;head&gt;&lt;br /&gt;    &amp;lt;meta http-equiv="Content-type" content="text/html; charset=utf-8" /&gt;&lt;br /&gt;    &amp;lt;title&gt;{% block title %}simple document{% endblock %}&amp;lt;/title&gt;&lt;br /&gt;    {% block extra_head %}{% endblock %}&lt;br /&gt;&amp;lt;/head&gt;&lt;br /&gt;&amp;lt;body&gt;&lt;br /&gt;    &amp;lt;div id="header"&gt;{% block header %}{% endblock %}&amp;lt;/div&gt;&lt;br /&gt;    &amp;lt;div id="content"&gt;{% block content %}{% endblock %}&amp;lt;/div&gt;&lt;br /&gt;    &amp;lt;div id="footer"&gt;{% block footer %}{% endblock %}&amp;lt;/div&gt;&lt;br /&gt;&amp;lt;/body&gt;&lt;br /&gt;&amp;lt;/html&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;index.html&lt;/code&gt; looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;{% extends "base.html" %}&lt;br /&gt;{% load i18n weather %}&lt;br /&gt;{% block content %}&lt;br /&gt;    {% get_current_weather in "berlin" %}&lt;br /&gt;{% endblock %}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And also we'll need an extension in the &lt;code&gt;urls.py&lt;/code&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;from django.conf.urls.defaults import *&lt;br /&gt;from django.contrib import admin&lt;br /&gt;&lt;br /&gt;admin.autodiscover()&lt;br /&gt;&lt;br /&gt;urlpatterns = patterns("",&lt;br /&gt;    (&lt;br /&gt;        r"^$",&lt;br /&gt;        "django.views.generic.simple.direct_to_template",&lt;br /&gt;        {'template': "index.html"},&lt;br /&gt;        ),&lt;br /&gt;    (r"^admin/", include(admin.site.urls)),&lt;br /&gt;    )&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When I run the development server and go to &lt;code&gt;http://127.0.0.1:8000/&lt;/code&gt;, I see this:&lt;br /&gt;&lt;br /&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_nG3SKqiLARM/StUT5HL8yGI/AAAAAAAAAW4/gdYXBUPKsiI/s320/blog_action_day_2009_templatetag.png" /&gt;&lt;br /&gt;&lt;br /&gt;It's time for the graphs! The end of this tutorial will be published here tomorrow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-6431764789613468661?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=qBkTQTOKKOg:xt-uzfsNy9o:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=qBkTQTOKKOg:xt-uzfsNy9o:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=qBkTQTOKKOg:xt-uzfsNy9o:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=qBkTQTOKKOg:xt-uzfsNy9o:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/qBkTQTOKKOg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/6431764789613468661/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-4-of-5.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/6431764789613468661?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/6431764789613468661?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/qBkTQTOKKOg/weather-app-tutorial-part-4-of-5.html" title="Weather App Tutorial. Part 4 of 5. Template Tag" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_nG3SKqiLARM/StUT5HL8yGI/AAAAAAAAAW4/gdYXBUPKsiI/s72-c/blog_action_day_2009_templatetag.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-4-of-5.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08EQH4zeSp7ImA9WxNWFkk.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-2083403276431649532</id><published>2009-10-16T00:30:00.015+02:00</published><updated>2009-10-16T00:30:01.081+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-16T00:30:01.081+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Intermediate" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Import" /><category scheme="http://www.blogger.com/atom/ns#" term="Project" /><category scheme="http://www.blogger.com/atom/ns#" term="Blog Action Day" /><category scheme="http://www.blogger.com/atom/ns#" term="XML" /><category scheme="http://www.blogger.com/atom/ns#" term="Commands" /><title>Weather App Tutorial. Part 3 of 5. Management Command</title><content type="html">As you might have noticed, this is a continuous tutorial about an app which regularly checks weather and reports how it changes throughout years. Previously I wrote how to start a project and define the models. The interestingness of the app starts now. I will show you how to import weather details from Yahoo! Weather using a management command.&lt;br /&gt;
&lt;br /&gt;
Management commands in Django are those which are recognized by &lt;code&gt;manage.py&lt;/code&gt; and &lt;code&gt;django-admin.py&lt;/code&gt;, e.g. &lt;code&gt;syncdb&lt;/code&gt;, &lt;code&gt;shell&lt;/code&gt;, &lt;code&gt;runserver&lt;/code&gt;, etc. It's not difficult to inject your own custom commands for your app. We'll need one that will be set as a scheduled task to check the current weather.&lt;br /&gt;
&lt;br /&gt;
To create a management command, you have to create directories &lt;code&gt;management/commands/&lt;/code&gt; inside of your &lt;code&gt;climate_change&lt;/code&gt; directory and create empty files &lt;code&gt;__init__.py&lt;/code&gt; in each of them.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
mkdir -p climate_change/management/commands
touch climate_change/management/__init__.py
touch climate_change/management/commands/__init__.py
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Now I'll create a file &lt;code&gt;climate_change/management/commands/check_weather.py&lt;/code&gt; which will aggregate the data from the feed. Just looking at the examples of Django core commands, you can find, that management-command modules should have a class &lt;code&gt;Command&lt;/code&gt; with an overridden method &lt;code&gt;handle_norargs&lt;/code&gt; which will have all the logic of the command. I found &lt;a href="http://developer.yahoo.com/python/python-xml.html"&gt;an example of importing weather from Yahoo!&lt;/a&gt; and will be using it here.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
# -*- coding: UTF-8 -*-
import urllib
from xml.dom import minidom
from pprint import pprint
from datetime import datetime

from django.db import models
from django.core.management.base import NoArgsCommand

Location = models.get_model("climate_change", "Location")
WeatherLog = models.get_model("climate_change", "WeatherLog")

SILENT, NORMAL, VERBOSE = 0, 1, 2

WEATHER_URL = 'http://xml.weather.yahoo.com/forecastrss?p=%s&amp;u=c'
WEATHER_NS = 'http://xml.weather.yahoo.com/ns/rss/1.0'

def weather_for_location(location_id):
    # taken from http://developer.yahoo.com/python/python-xml.html
    # and modified a little
    url = WEATHER_URL % location_id
    dom = minidom.parse(urllib.urlopen(url))
    forecasts = []
    for node in dom.getElementsByTagNameNS(WEATHER_NS, 'forecast'):
        forecasts.append({
            'date': node.getAttribute('date'),
            'low': node.getAttribute('low'),
            'high': node.getAttribute('high'),
            'condition': node.getAttribute('text')
        })
    ycondition = dom.getElementsByTagNameNS(WEATHER_NS, 'condition')[0]
    ywind = dom.getElementsByTagNameNS(WEATHER_NS, 'wind')[0]
    yatmosphere = dom.getElementsByTagNameNS(WEATHER_NS, 'atmosphere')[0]
    return {
        'current_condition': ycondition.getAttribute('text'),
        'current_temp': ycondition.getAttribute('temp'),
        'current_humidity': yatmosphere.getAttribute('humidity'),
        'current_visibility': yatmosphere.getAttribute('visibility'),
        'current_wind_speed': ywind.getAttribute('speed'),
        'forecasts': forecasts,
        'title': dom.getElementsByTagName('title')[0].firstChild.data,
        'guid': dom.getElementsByTagName('guid')[0].firstChild.data,
    }            

class Command(NoArgsCommand):
    help = "Aggregates data from weather feed"
    def handle_noargs(self, **options):
        verbosity = int(options.get('verbosity', NORMAL))
        
        created_count = 0
        for l in Location.objects.all():
            weather = weather_for_location(l.location_id)
            if verbosity &gt; NORMAL:
                pprint(weather)
            timestamp_parts = map(int, weather['guid'].split("_")[1:-1])
            timestamp = datetime(*timestamp_parts)
            log, created = WeatherLog.objects.get_or_create(
                 location=l,
                 timestamp=timestamp,
                 defaults={
                    'temperature': weather['current_temp'],
                    'humidity': weather['current_humidity'],
                    'wind_speed': weather['current_wind_speed'],
                    'visibility': weather['current_visibility'],
                    }
                 )
            if created:
                created_count += 1
        if verbosity &gt; NORMAL:
            print "New weather logs: %d" % created_count
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Now we can try running the command from the project directory using the following:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
python manage.py check_weather --verbosity=2
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
I get this result:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
{'current_condition': u'Mostly Cloudy',
 'current_humidity': u'94',
 'current_temp': u'10',
 'current_visibility': u'9.99',
 'current_wind_speed': u'16.09',
 'forecasts': [{'condition': u'Partly Cloudy',
                'date': u'6 Oct 2009',
                'high': u'19',
                'low': u'14'},
               {'condition': u'PM Light Rain',
                'date': u'7 Oct 2009',
                'high': u'23',
                'low': u'14'}],
 'guid': u'GMXX0008_2009_10_06_10_20_CEST',
 'title': u'Yahoo! Weather - Berlin/Schonefeld, GM'}
New weather logs: 1
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
To define this task as a cron job running hourly, you can either type &lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
echo 0 * * * * cd /path/to/climate_change_env/ &amp;&amp; source bin/activate &amp;&amp; cd blog_action_day_2009 &amp;&amp; python manage.py check_weather &amp;&amp; deactivate &gt; crontab.txt
crontab crontab.txt
del crontab.txt
&lt;/code&gt;&lt;/pre&gt;.. or use &lt;a href="http://code.google.com/p/django-poormanscron/"&gt;django-poormanscron&lt;/a&gt; app and set the &lt;code&gt;check_weather&lt;/code&gt; command there.&lt;br /&gt;
&lt;br /&gt;
OK. Cool. The next thing to do is the template tag which displays the details of recent weather.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-2083403276431649532?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=HCilVaWtuUU:acOEsWF64ec:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=HCilVaWtuUU:acOEsWF64ec:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=HCilVaWtuUU:acOEsWF64ec:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=HCilVaWtuUU:acOEsWF64ec:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/HCilVaWtuUU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/2083403276431649532/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-3-of-5.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/2083403276431649532?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/2083403276431649532?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/HCilVaWtuUU/weather-app-tutorial-part-3-of-5.html" title="Weather App Tutorial. Part 3 of 5. Management Command" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-3-of-5.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0MEQXczeCp7ImA9WxNWFUg.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-3422974565318708176</id><published>2009-10-15T00:30:00.008+02:00</published><updated>2009-10-15T00:30:00.980+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-15T00:30:00.980+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="App" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Project" /><category scheme="http://www.blogger.com/atom/ns#" term="Admin" /><category scheme="http://www.blogger.com/atom/ns#" term="Basics" /><category scheme="http://www.blogger.com/atom/ns#" term="Blog Action Day" /><category scheme="http://www.blogger.com/atom/ns#" term="Models" /><category scheme="http://www.blogger.com/atom/ns#" term="Berlin" /><title>Weather App Tutorial. Part 2 of 5. Models</title><content type="html">This is the second part of my &lt;a href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-1-of-5.html"&gt;tutorial how to make a weather app&lt;/a&gt;. This time I will show you how to create the models and set up the admin.&lt;br /&gt;
&lt;br /&gt;
Open the &lt;code&gt;models.py&lt;/code&gt; file in the &lt;code&gt;climate_change&lt;/code&gt; directory and enter the following:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
# -*- coding: UTF-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings

class Location(models.Model):
    name = models.CharField(_("name"), max_length=200)
    location_id = models.CharField(
        _("location ID"),
        max_length=20,
        help_text=_("Location IDs can be retrieved from URLs of weather "
            "at specific cities at Yahoo! Weather, e.g. GMXX0008 from "
            "http://weather.yahoo.com/forecast/GMXX0008.html"),
        )
    
    class Meta:
        verbose_name=_("location")
        verbose_name_plural=_("locations")
    
    def __unicode__(self):
        return self.name

class WeatherLog(models.Model):
    location = models.ForeignKey(Location, verbose_name=_("location"))
    timestamp = models.DateTimeField(_("timestamp"))
    temperature = models.IntegerField(_("temperature (C°)"))
    humidity = models.IntegerField(_("humidity (%)"))
    wind_speed = models.DecimalField(
         _("wind speed (km/h)"),
         max_digits=5,
         decimal_places=2,
         )
    visibility = models.DecimalField(
         _("visibility (km)"),
         max_digits=5,
         decimal_places=2,
         )
    
    class Meta:
        verbose_name=_("weather log")
        verbose_name_plural=_("weather logs")
        ordering = ("-timestamp",)
    
    def __unicode__(self):
        return "%s @ %s" % (
            self.location.name,
            self.timestamp.strftime("%Y-%m-%dT%H:%M"),
            )
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The models are created. Now let's create the database schema for them.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
python manage.py syncdb
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Also in this step when asked, I created a superuser called "demo" to be able to access django-contributed administration. Let's try it out. We'll need to add a file &lt;code&gt;admin.py&lt;/code&gt; to &lt;code&gt;climate_change&lt;/code&gt; with this content:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib import admin

Location = models.get_model("climate_change", "Location")
WeatherLog = models.get_model("climate_change", "WeatherLog")

admin.site.register(Location)
admin.site.register(WeatherLog)
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Then uncomment admin related lines in &lt;code&gt;urls.py&lt;/code&gt; of the project:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
# -*- coding: utf-8 -*-
from django.conf.urls.defaults import *
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns("",
    (r"^admin/", include(admin.site.urls)),
    )
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Finally, run the development server and check if the models really work.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
python shell runserver
# now you can go to http://127.0.0.1:8000/admin/ in a browser
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;img border="0" src="http://2.bp.blogspot.com/_nG3SKqiLARM/StUJL4Qn84I/AAAAAAAAAWo/9I5MO_-h1uM/s320/blog_action_day_2009_admin.png" /&gt;&lt;br /&gt;
&lt;br /&gt;
Yes! Everything works as expected for now. I will add my current location and its ID in Yahoo! Weather to the database. For me it's Berlin, Germany and the ID is &lt;code&gt;GMXX0008&lt;/code&gt;. I found the ID in the URL of the page showing the weather for Berlin.&lt;br /&gt;
&lt;br /&gt;
&lt;img border="0" src="http://3.bp.blogspot.com/_nG3SKqiLARM/StUNpwKUdmI/AAAAAAAAAWw/3Rl_63KB06Y/s320/blog_action_day_2009_admin_berlin.png" /&gt;&lt;br /&gt;
&lt;br /&gt;
Tomorrow I will show you how to import weather details from Yahoo!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-3422974565318708176?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=TQglqmnWYbo:4IClgkYfk-E:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=TQglqmnWYbo:4IClgkYfk-E:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=TQglqmnWYbo:4IClgkYfk-E:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=TQglqmnWYbo:4IClgkYfk-E:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/TQglqmnWYbo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/3422974565318708176/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-2-of-5-models.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3422974565318708176?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3422974565318708176?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/TQglqmnWYbo/weather-app-tutorial-part-2-of-5-models.html" title="Weather App Tutorial. Part 2 of 5. Models" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_nG3SKqiLARM/StUJL4Qn84I/AAAAAAAAAWo/9I5MO_-h1uM/s72-c/blog_action_day_2009_admin.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-2-of-5-models.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkAMRX8ycSp7ImA9WxNWFEU.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-3241057969932475529</id><published>2009-10-14T00:30:00.003+02:00</published><updated>2009-10-14T02:39:44.199+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-14T02:39:44.199+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Virtualmin" /><category scheme="http://www.blogger.com/atom/ns#" term="App" /><category scheme="http://www.blogger.com/atom/ns#" term="Administration" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Project" /><category scheme="http://www.blogger.com/atom/ns#" term="Basics" /><category scheme="http://www.blogger.com/atom/ns#" term="Blog Action Day" /><title>Weather App Tutorial. Part 1 of 5. Preparation</title><content type="html">It is &lt;a href="http://www.blogactionday.org/"&gt;Blog Action Day&lt;/a&gt; tomorrow so I decided to participate in it as a blogger &lt;a href="http://djangotricks.blogspot.com/2008/10/exploiting-spammers-search-engines-and.html"&gt;again&lt;/a&gt;. The theme for this year is "Climate Change". After a little brainstorm, I came up with an idea to write a tutorial how to build an app which regularly checks local weather and lets you compare different weather features for months of different years.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.blogactionday.org/"&gt;&lt;img border="0" src="http://www.blogactionday.org/imgs/badges/bad-300-250.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'll use &lt;a href="http://developer.yahoo.com/weather/"&gt;Yahoo! Weather&lt;/a&gt; to check the current weather. From all information that it provides, we'll be mostly interested in temperature, humidity, wind speed, and visibility in the current location. The app will check the weather regularly, will allow you to show the current weather, and also provide a graph comparing average monthly weathers throughout years.&lt;br /&gt;&lt;br /&gt;So let's start with the new project. I have quite a clean computer and want to do the app the nice way. So first of all, I will install &lt;code&gt;virtualenv&lt;/code&gt; to be able to install third-party python libraries in a closed environment which will only be used for this project (I have already installed &lt;code&gt;setuptools&lt;/code&gt; and &lt;code&gt;django&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# install virtualenv&lt;br /&gt;sudo easy_install virtualenv&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I created a directory &lt;code&gt;Projects&lt;/code&gt; in my home directory and &lt;code&gt;cd&lt;/code&gt; to it.&lt;br /&gt;&lt;br /&gt;Let's create a virtual environment and start the new project and app.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# create virtual environment "climate_change_env"&lt;br /&gt;virtualenv climate_change_env&lt;br /&gt;cd climate_change_env/&lt;br /&gt;# activate the environment&lt;br /&gt;source bin/activate&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since now I see &lt;code&gt;(climate_change_env)&lt;/code&gt; as a prefix to each command line. I'll type &lt;code&gt;deactivate&lt;/code&gt; at some point later to get out of this virtual environment.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# create django project "blog_action_day_2009"&lt;br /&gt;django-admin.py startproject blog_action_day_2009&lt;br /&gt;cd blog_action_day_2009/&lt;br /&gt;# create django app "climate_change"&lt;br /&gt;django-admin.py startapp climate_change&lt;br /&gt;# create folders for templates and media&lt;br /&gt;mkdir templates media&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To get started quickly, I will use sqlite3 database now. As I am using python 2.5, I don't need to install sqlite3 module, because it's already there. So I open the &lt;code&gt;settings.py&lt;/code&gt; and define those settings:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;import os&lt;br /&gt;PROJECT_DIR = os.path.dirname(__file__)&lt;br /&gt;# ...&lt;br /&gt;DATABASE_ENGINE = "sqlite3"&lt;br /&gt;DATABASE_NAME = "blog_action_day_2009.sqlite3"&lt;br /&gt;# ...&lt;br /&gt;MEDIA_ROOT = os.path.join(PROJECT_DIR, "media")&lt;br /&gt;MEDIA_URL = "/media/"&lt;br /&gt;ADMIN_MEDIA_PREFIX = "/admin/media/"&lt;br /&gt;# ...&lt;br /&gt;TEMPLATE_DIRS = [&lt;br /&gt;    os.path.join(PROJECT_DIR, "templates"),&lt;br /&gt;    ]&lt;br /&gt;# ...&lt;br /&gt;INSTALLED_APPS = (&lt;br /&gt;    "django.contrib.auth",&lt;br /&gt;    "django.contrib.contenttypes",&lt;br /&gt;    "django.contrib.sessions",&lt;br /&gt;    "django.contrib.sites",&lt;br /&gt;    "django.contrib.admin",&lt;br /&gt;    "climate_change",&lt;br /&gt;    )&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we'll need those parts:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Models for locations and imported weather information.&lt;/li&gt;&lt;li&gt;Management command for importing data from Yahoo! Weather.&lt;/li&gt;&lt;li&gt;Template tag for displaying latest weather.&lt;/li&gt;&lt;li&gt;View for displaying a graph.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;But I'll continue about that tomorrow.&lt;br /&gt;&lt;br /&gt;To end today's post, let's watch a video about a regular guy who talks about weather when he has nothing to say:&lt;br /&gt;&lt;br /&gt;&lt;object height="282" width="333"&gt;&lt;param name="movie" value="http://www.youtube.com/v/GmG4X9PGOXs&amp;hl=de&amp;fs=1&amp;rel=0&amp;color1=0x234900&amp;color2=0x4e9e00&amp;border=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/GmG4X9PGOXs&amp;hl=de&amp;fs=1&amp;rel=0&amp;color1=0x234900&amp;color2=0x4e9e00&amp;border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="333" height="282"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-3241057969932475529?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=kTZKDBgp1_Y:hHuEuLWJUSc:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=kTZKDBgp1_Y:hHuEuLWJUSc:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=kTZKDBgp1_Y:hHuEuLWJUSc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=kTZKDBgp1_Y:hHuEuLWJUSc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/kTZKDBgp1_Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/3241057969932475529/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-1-of-5.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3241057969932475529?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3241057969932475529?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/kTZKDBgp1_Y/weather-app-tutorial-part-1-of-5.html" title="Weather App Tutorial. Part 1 of 5. Preparation" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/10/weather-app-tutorial-part-1-of-5.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcHR3gyfSp7ImA9WxJSFUs.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-8607436259011232496</id><published>2009-05-05T23:00:00.002+02:00</published><updated>2009-05-06T01:43:56.695+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-06T01:43:56.695+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><title>Applying Patches in Guerrilla Way</title><content type="html">How many times did you submit patches that have never been approved? How many times did you need to patch Django for some third-party application to work? How many times did you bumped into the wall working on real-world projects just because of some bugfixes waiting for tests or nicer solution? In all those cases, you should not panic nor run into circles, because you can still apply the existing patches in guerrilla way (or so called monkey patching).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/22225522@N00/359620178/"&gt;&lt;img src="http://farm1.static.flickr.com/156/359620178_4dd6ec39a5.jpg?v=0" alt="Guerrilla Advertising might also be powerful, but it's always controversial" width="333" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Guerrilla patches overwrite existing code not on a disk, but in memory at execution time. Everything is an object in Python. Numbers, strings, functions, methods, classes, instances, modules, etc. are objects. Each object might be conceived as an attribute of a module where it is defined. In addition, properties and methods of classes can be conceived as attributes of the class. The principle of guerrilla patching is loading a module or class which attributes need to be changed, and assigning new values to those attributes before the module or class is used. All that has to happen somewhere in the beginning of execution. Usually, I put or import guerrilla patches in &lt;code&gt;models.py&lt;/code&gt; of some app, because all models are loaded in one of the first steps of execution. So if you need to change a full class (&lt;code&gt;DjangoClass&lt;/code&gt;), you would import the module (&lt;code&gt;djangomodule&lt;/code&gt;) where the class resides, create another class (&lt;code&gt;NewDjangoClass&lt;/code&gt;) and assign it to the attribute of the module by the name of the original class (&lt;code&gt;djangomodule.DjangoClass = NewDjangoClass&lt;/code&gt;). If you need to change just one method (&lt;code&gt;django_method&lt;/code&gt;) of a class (&lt;code&gt;DjangoClass&lt;/code&gt;), then you would import the class, write a new function which takes the object to which it will be assigned as the first argument (&lt;code&gt;def new_django_method(self, *args, **kwargs)&lt;/code&gt;) and assigned it to the class by the name of the original function (&lt;code&gt;DjangoClass.django_method = new_django_method&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;Let's have a look at an example. You might need nested fieldsets for more organized grouping, suggested in the ticket &lt;a href="http://code.djangoproject.com/ticket/10590"&gt;#10590&lt;/a&gt;. That allows you to create fieldset definitions as items of the 'fields' value of a parent fieldset.&lt;br /&gt;&lt;br /&gt;As you might see from the applied patch &lt;code&gt;nested_fieldsets.diff&lt;/code&gt;, the main change of the patch is changing most of the code for classes &lt;code&gt;Fieldset&lt;/code&gt; and &lt;code&gt;Fieldline&lt;/code&gt; residing in &lt;code&gt;django/contrib/admin/helpers.py&lt;/code&gt;, also changing the function &lt;code&gt;flatten_fieldsets&lt;/code&gt; in &lt;code&gt;django/contrib/admin/util.py&lt;/code&gt;, introducing a new template tag and finally modifying a template.&lt;br /&gt;&lt;br /&gt;The file with guerrilla patches should import &lt;code&gt;helpers&lt;/code&gt;, &lt;code&gt;util&lt;/code&gt;, and all other modules that will be used in the overwritten objects. Browse through the code below and check the differences with &lt;a href="http://code.djangoproject.com/attachment/ticket/10590/nested_fieldsets.diff"&gt;the original patch&lt;/a&gt; to understand the concept better.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django &lt;span class="syntax8"&gt;import&lt;/span&gt; forms&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.contrib.admin &lt;span class="syntax8"&gt;import&lt;/span&gt; helpers&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.contrib.admin &lt;span class="syntax8"&gt;import&lt;/span&gt; util&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.contrib.admin &lt;span class="syntax8"&gt;import&lt;/span&gt; options&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.utils.safestring &lt;span class="syntax8"&gt;import&lt;/span&gt; mark_safe&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.forms.formsets &lt;span class="syntax8"&gt;import&lt;/span&gt; all_valid&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.conf &lt;span class="syntax8"&gt;import&lt;/span&gt; settings&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt;##&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;Guerilla&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;patches&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;for&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;nested&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;fieldsets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;flatten_fieldsets&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;fieldsets&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax14"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="syntax14"&gt;Returns&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;a&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;list&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;of&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;field&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;names&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;from&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;an&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;admin&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;fieldsets&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;structure&lt;/span&gt;&lt;span class="syntax14"&gt;.&lt;/span&gt;&lt;span class="syntax14"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    field_names &lt;span class="syntax18"&gt;=&lt;/span&gt; []&lt;br /&gt;    &lt;span class="syntax8"&gt;for&lt;/span&gt; name, opts &lt;span class="syntax8"&gt;in&lt;/span&gt; fieldsets&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;for&lt;/span&gt; field &lt;span class="syntax8"&gt;in&lt;/span&gt; opts[&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;fields&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;]&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax9"&gt;isinstance&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax9"&gt;tuple&lt;/span&gt;, &lt;span class="syntax9"&gt;list&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax9"&gt;len&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;2&lt;/span&gt; &lt;span class="syntax8"&gt;and&lt;/span&gt; &lt;span class="syntax9"&gt;isinstance&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;], &lt;span class="syntax9"&gt;dict&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                    &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;it's&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;a&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;nested&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;fieldset&lt;/span&gt;&lt;br /&gt;                    field_names.&lt;span class="syntax6"&gt;extend&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax6"&gt;flatten_fieldsets&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field,&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;else&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                    &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;it's&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;a&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;tuple&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;of&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;names&lt;/span&gt;&lt;br /&gt;                    field_names.&lt;span class="syntax6"&gt;extend&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;else&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;it's&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;a&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;name&lt;/span&gt;&lt;br /&gt;                field_names.&lt;span class="syntax6"&gt;append&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;return&lt;/span&gt; field_names&lt;br /&gt;&lt;br /&gt;options.flatten_fieldsets &lt;span class="syntax18"&gt;=&lt;/span&gt; util.flatten_fieldsets &lt;span class="syntax18"&gt;=&lt;/span&gt; flatten_fieldsets&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Fieldset&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax9"&gt;object&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    is_fieldset &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax10"&gt;__init__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, form, name&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;None&lt;/span&gt;, fields&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;, classes&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;, description&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;None&lt;/span&gt;, level&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;0&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.form &lt;span class="syntax18"&gt;=&lt;/span&gt; form&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.name, &lt;span class="syntax8"&gt;self&lt;/span&gt;.fields &lt;span class="syntax18"&gt;=&lt;/span&gt; name, fields&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.classes &lt;span class="syntax18"&gt;=&lt;/span&gt; u&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;.&lt;span class="syntax6"&gt;join&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;classes&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.description &lt;span class="syntax18"&gt;=&lt;/span&gt; description&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.level &lt;span class="syntax18"&gt;=&lt;/span&gt; level&lt;br /&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;_media&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;collapse&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt; &lt;span class="syntax8"&gt;in&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.classes&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;return&lt;/span&gt; forms.&lt;span class="syntax6"&gt;Media&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;js&lt;span class="syntax18"&gt;=&lt;/span&gt;[&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;js&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;admin&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;CollapsedFieldsets&lt;/span&gt;&lt;span class="syntax13"&gt;.&lt;/span&gt;&lt;span class="syntax13"&gt;js&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; settings.ADMIN_MEDIA_PREFIX]&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;return&lt;/span&gt; forms.&lt;span class="syntax6"&gt;Media&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    media &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;property&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;_media&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax10"&gt;__iter__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;for&lt;/span&gt; field &lt;span class="syntax8"&gt;in&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.fields&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax9"&gt;len&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;2&lt;/span&gt; &lt;span class="syntax8"&gt;and&lt;/span&gt; &lt;span class="syntax9"&gt;isinstance&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;], &lt;span class="syntax9"&gt;dict&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;nested&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;fieldset&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;yield&lt;/span&gt; &lt;span class="syntax6"&gt;Fieldset&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.form,&lt;br /&gt;                    name&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;0&lt;/span&gt;],&lt;br /&gt;                    fields&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;].&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;fields&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;                    classes&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;].&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;classes&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;                    description&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;].&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;description&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;                    level&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.level &lt;span class="syntax18"&gt;+&lt;/span&gt; &lt;span class="syntax5"&gt;1&lt;/span&gt;,&lt;br /&gt;                    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;else&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;name&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;or&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;a&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;tuple&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;of&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;names&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;yield&lt;/span&gt; helpers.&lt;span class="syntax6"&gt;Fieldline&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.form, field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;br /&gt;helpers.Fieldset &lt;span class="syntax18"&gt;=&lt;/span&gt; Fieldset&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;InlineFieldset&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;Fieldset&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax10"&gt;__init__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, formset, &lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.formset &lt;span class="syntax18"&gt;=&lt;/span&gt; formset&lt;br /&gt;        &lt;span class="syntax9"&gt;super&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;InlineFieldset, &lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax10"&gt;__init__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax10"&gt;__iter__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        fk &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;getattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.formset, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;fk&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;for&lt;/span&gt; field &lt;span class="syntax8"&gt;in&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.fields&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;if&lt;/span&gt; fk &lt;span class="syntax8"&gt;and&lt;/span&gt; fk.name &lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt; field&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;continue&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax9"&gt;len&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;2&lt;/span&gt; &lt;span class="syntax8"&gt;and&lt;/span&gt; &lt;span class="syntax9"&gt;isinstance&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;], &lt;span class="syntax9"&gt;dict&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;nested&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;fieldset&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;yield&lt;/span&gt; &lt;span class="syntax6"&gt;Fieldset&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.form,&lt;br /&gt;                    name&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;0&lt;/span&gt;],&lt;br /&gt;                    fields&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;].&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;fields&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;                    classes&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;].&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;classes&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;                    description&lt;span class="syntax18"&gt;=&lt;/span&gt;field[&lt;span class="syntax5"&gt;1&lt;/span&gt;].&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;description&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;                    level&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.level &lt;span class="syntax18"&gt;+&lt;/span&gt; &lt;span class="syntax5"&gt;1&lt;/span&gt;,&lt;br /&gt;                    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;else&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;name&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;or&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;a&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;tuple&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;of&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;names&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;yield&lt;/span&gt; helpers.&lt;span class="syntax6"&gt;Fieldline&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.form, field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;helpers.InlineFieldset &lt;span class="syntax18"&gt;=&lt;/span&gt; InlineFieldset&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Further, the template can be overwritten putting your own template path before the django admin path in &lt;code&gt;settings.TEMPLATE_DIRS&lt;/code&gt;. The missing template tag can be defined in your own app. Just don't forget to load the library in the template (&lt;code&gt;{% load mytemplatetags %}&lt;/code&gt;). CSS might be overwritten by including a custom CSS file to the bottom of HEAD section in a modified &lt;code&gt;admin/change_form.html&lt;/code&gt; template.&lt;br /&gt;&lt;br /&gt;That's all what you need and you'll be controlling the situation without branching Django. You have full power, but don't overuse this technique as it might be difficult to manage the updates, especially when you are guerrilla-patching undocumented features or helping functions which change backwards-incompatibly in the future. Still guerrilla-patched code in a third-party app is better than a requirement to patch Django itself.&lt;br /&gt;&lt;br /&gt;Guerrilla patching might also be used in a similar way to do some slight modification to other third party apps which are not scalable by design. For example, you can add additional fields or modify methods of third-party apps which will be updatable separately.&lt;br /&gt;&lt;br /&gt;Tomorrow (Wednesday) evening I am leaving the wonderful Prague and &lt;a href="http://euro.djangocon.org/"&gt;EuroDjangoCon&lt;/a&gt;. If you are here as well and want to discuss some concepts which I introduced in this blog, that's the last chance to meet me live. :D&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-8607436259011232496?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=LoEGrYq0WQs:nGnE-7g3VhU:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=LoEGrYq0WQs:nGnE-7g3VhU:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=LoEGrYq0WQs:nGnE-7g3VhU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=LoEGrYq0WQs:nGnE-7g3VhU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/LoEGrYq0WQs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/8607436259011232496/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/05/how-many-times-did-you-submit-patches.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/8607436259011232496?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/8607436259011232496?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/LoEGrYq0WQs/how-many-times-did-you-submit-patches.html" title="Applying Patches in Guerrilla Way" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/05/how-many-times-did-you-submit-patches.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4MRXYyfCp7ImA9WxJSFUs.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-1876431566352405992</id><published>2009-04-12T16:00:00.001+02:00</published><updated>2009-05-06T01:43:04.894+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-06T01:43:04.894+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Intermediate" /><category scheme="http://www.blogger.com/atom/ns#" term="Structure" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Architecture" /><category scheme="http://www.blogger.com/atom/ns#" term="Patterns" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><title>Design Pattern for Reusable Apps: Getting into Details</title><content type="html">This article is an attempt to explain a technique for creating reusable and overridable apps that were partly mentioned in my &lt;a href="http://djangotricks.blogspot.com/2009/02/abstract-models-and-dynamicly-assigned.html"&gt;last post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First of all, let's remember the presentation by James Bennett about reusable apps from DjangoCon 2008:&lt;br /&gt;&lt;br /&gt;&lt;object width="333" height="282"&gt;&lt;param name="movie" value="http://www.youtube.com/v/A-S0tqpPga4&amp;hl=de&amp;fs=1&amp;rel=0&amp;color1=0x234900&amp;color2=0x4e9e00&amp;border=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/A-S0tqpPga4&amp;hl=de&amp;fs=1&amp;rel=0&amp;color1=0x234900&amp;color2=0x4e9e00&amp;border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="333" height="282"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Also the upgraded version &lt;a href="http://toys.jacobian.org/presentations/2009/pycon/real-world.pdf"&gt;Real World Django&lt;/a&gt; by Jacob Kaplan-Moss presented at PyCon 2009 should be checked.&lt;br /&gt;&lt;br /&gt;Imagine, that you have a bunch of predefined apps somewhere under your python path. Some of them have models with relations to models from other apps. You will surely want to reuse those apps for different projects. You'll want to be able to activate the necessary set of apps. Probably sometimes you'll want to use an extended version of an existing app. As shown in the presentations given above, reusable apps should allow you easy overrides. Although all apps should be either under python path directly or under a package, nothing should be set in stone. The method &lt;code&gt;get_model()&lt;/code&gt; should be used to import models from installed apps. But apps consist of more components than just models. There are urls, views, forms, template tags, context processors, middlewares, etc.&lt;br /&gt;&lt;br /&gt;Let's have a look at a simple example. Two reusable apps could be &lt;code&gt;events&lt;/code&gt; and &lt;code&gt;venues&lt;/code&gt; where &lt;code&gt;events&lt;/code&gt; would contain an &lt;code&gt;Event&lt;/code&gt; model with a foreign key to &lt;code&gt;Venue&lt;/code&gt; model from the &lt;code&gt;venues&lt;/code&gt; app. In order to make both models overridable, I'll put the definitions in abstract base classes in the files &lt;code&gt;base.py&lt;/code&gt;. The files &lt;code&gt;models.py&lt;/code&gt; will import from &lt;code&gt;base.py&lt;/code&gt; and create the leaf classes which will be the models to use further in forms and elsewhere.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;venues&lt;ul&gt;&lt;br /&gt;&lt;li&gt;__init__.py&lt;/li&gt;&lt;br /&gt;&lt;li&gt;base.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db &lt;span class="syntax8"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;VenueBase&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    title &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    street_address &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    postal_code &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    city &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    country &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;class&lt;/span&gt; Meta&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        abstract &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;models.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.base &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Venue&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;VenueBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;pass&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;events&lt;ul&gt;&lt;br /&gt;&lt;li&gt;__init__.py&lt;/li&gt;&lt;br /&gt;&lt;li&gt;base.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db &lt;span class="syntax8"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;br /&gt;Venue &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;get_model&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;venues&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Venue&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;EventBase&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    title &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    venue &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;ForeignKey&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;Venue&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    from_date &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;DateField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    to_date &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;DateField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;class&lt;/span&gt; Meta&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        abstract &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;models.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; events.base &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Event&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;EventBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;pass&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Venues (as well as events) will probably have some urls, views, and forms which should be extensible too.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;venues&lt;ul&gt;&lt;br /&gt;&lt;li&gt;...&lt;/li&gt;&lt;br /&gt;&lt;li&gt;forms.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db &lt;span class="syntax8"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.forms &lt;span class="syntax8"&gt;import&lt;/span&gt; ModelForm&lt;br /&gt;Venue &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;get_model&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;venues&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Venue&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;VenueForm&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;ModelForm&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;class&lt;/span&gt; Meta&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        model &lt;span class="syntax18"&gt;=&lt;/span&gt; Venue&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;views.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db &lt;span class="syntax8"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.utils &lt;span class="syntax8"&gt;import&lt;/span&gt; importlib&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.shortcuts &lt;span class="syntax8"&gt;import&lt;/span&gt; render_to_response&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;importing&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;the&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;form&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;depending&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;on&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;the&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;path&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;of&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;installed&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;app&lt;/span&gt;&lt;br /&gt;venues_app &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;get_app&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;venues&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;venues_forms &lt;span class="syntax18"&gt;=&lt;/span&gt; importlib.&lt;span class="syntax6"&gt;import_module&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;venues_app.&lt;span class="syntax10"&gt;__name__&lt;/span&gt;[&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;span class="syntax18"&gt;-&lt;/span&gt;&lt;span class="syntax5"&gt;6&lt;/span&gt;] &lt;span class="syntax18"&gt;+&lt;/span&gt; &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;forms&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;VenueForm &lt;span class="syntax18"&gt;=&lt;/span&gt; venues_forms.VenueForm&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;add_venue&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;request&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;if&lt;/span&gt; request.method &lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;POST&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        form &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;VenueForm&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;request.POST, request.FILES&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; form.&lt;span class="syntax6"&gt;is_valid&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            form.&lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;else&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        form &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;VenueForm&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;return&lt;/span&gt; &lt;span class="syntax6"&gt;render_to_response&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;venues&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;add_venue&lt;/span&gt;&lt;span class="syntax13"&gt;.&lt;/span&gt;&lt;span class="syntax13"&gt;html&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, {&lt;br /&gt;        &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;form&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt; form,&lt;br /&gt;    }&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;change_venue&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;request, pk&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;urls.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.conf.urls.defaults &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db &lt;span class="syntax8"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.utils &lt;span class="syntax8"&gt;import&lt;/span&gt; importlib&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;importing&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;the&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;form&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;depending&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;on&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;the&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;path&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;of&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;installed&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;app&lt;/span&gt;&lt;br /&gt;venues_app &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;get_app&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;venues&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;venues_views &lt;span class="syntax18"&gt;=&lt;/span&gt; importlib.&lt;span class="syntax6"&gt;import_module&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;venues_app.&lt;span class="syntax10"&gt;__name__&lt;/span&gt;[&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;span class="syntax18"&gt;-&lt;/span&gt;&lt;span class="syntax5"&gt;6&lt;/span&gt;] &lt;span class="syntax18"&gt;+&lt;/span&gt; &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;views&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;urlpatterns &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;patterns&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;,&lt;br /&gt;    ...&lt;br /&gt;    &lt;span class="syntax6"&gt;url&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;r&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;^&lt;/span&gt;&lt;span class="syntax13"&gt;add&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;$&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, venues_views.add_venue, name&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;add_venue&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax6"&gt;url&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;r&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;^&lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;\d&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;$&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, venues_views.change_venue, name&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;change_venue&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    ...&lt;br /&gt;    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;You might ask why such a strange and tricky importing is done in the views and urls. If we know the path to the app, it is not going to change or something. Even &lt;a href="http://www.b-list.org/"&gt;James Bennett&lt;/a&gt; himself didn't see any advantages of importing forms and views dynamically when closing &lt;a href="http://code.djangoproject.com/ticket/10703"&gt;ticket #10703&lt;/a&gt; for Django:&lt;br /&gt;&lt;blockquote&gt;I don't really see the utility of this – a properly-written Django application is just a Python module, and is importable the same as any other Python module. It's not like that module is suddenly going to have a different path (and if it does, you're doing something wrong). &lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;The answer hides in the overriding apps.&lt;br /&gt;&lt;br /&gt;The combo of current &lt;code&gt;events&lt;/code&gt; and &lt;code&gt;venues&lt;/code&gt; might fit most of your projects. But then you might need a specific &lt;code&gt;venues&lt;/code&gt; app with additional features for a specific project. Note, that you probably don't want to change or recreate the &lt;code&gt;events&lt;/code&gt; app just because of the relation to the &lt;code&gt;venues&lt;/code&gt;. The app and model names can be still the same, just put the new app under your new project and include the new app into INSTALLED_APPS instead of the original app. Project name will serve as a namespace for distinguishing the original app and the overridden app.&lt;br /&gt;&lt;br /&gt;If you want to extend just the model, but not the other components, your specific app might look like this:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;myproject&lt;ul&gt;&lt;br /&gt;&lt;li&gt;venues&lt;ul&gt;&lt;br /&gt;&lt;li&gt;__init__.py&lt;/li&gt;&lt;br /&gt;&lt;li&gt;models.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.base &lt;span class="syntax8"&gt;import&lt;/span&gt; VenueBase&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Venue&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;VenueBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    description &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;TextField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;forms.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.forms &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;views.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.views &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;urls.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.urls &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;If the model is OK as is, but you need an additional behavior for the form, like saving an image for each venue, you can extend the form instead:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;myproject&lt;ul&gt;&lt;br /&gt;&lt;li&gt;venues&lt;ul&gt;&lt;br /&gt;&lt;li&gt;__init__.py&lt;/li&gt;&lt;br /&gt;&lt;li&gt;models.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.models &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;forms.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django &lt;span class="syntax8"&gt;import&lt;/span&gt; forms&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.forms &lt;span class="syntax8"&gt;import&lt;/span&gt; VenueForm &lt;span class="syntax8"&gt;as&lt;/span&gt; VenueFormBase&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;VenueForm&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;VenueFormBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    image &lt;span class="syntax18"&gt;=&lt;/span&gt; forms.&lt;span class="syntax6"&gt;ImageField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;...&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, &lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax9"&gt;super&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;VenueForm, &lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        ...&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;views.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.views &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;urls.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.urls &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;If models and forms are alright, but you want to add an additional view, you can do that too:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;myproject&lt;ul&gt;&lt;br /&gt;&lt;li&gt;venues&lt;ul&gt;&lt;br /&gt;&lt;li&gt;__init__.py&lt;/li&gt;&lt;br /&gt;&lt;li&gt;models.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.models &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;forms.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.forms &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;views.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.views &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;delete_venue&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;request, pk&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;urls.py&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; venues.urls &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;urlpatterns &lt;span class="syntax18"&gt;+&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;patterns&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax6"&gt;url&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;r&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;^&lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;\d&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;delete&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;$&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, venues_views.delete_venue, name&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;delete_venue&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;As you see from the case examples above, you don't have to duplicate any code of the original app. Still you can now enhance or overwrite just specific parts that you need very flexibly. The &lt;code&gt;events&lt;/code&gt; could be extended in the same manner. If the original extensible apps live under a specific package, then the imports in the extending app should be changed appropriately.&lt;br /&gt;&lt;br /&gt;Ufff.. That's about all what I wanted to explain this time. If you have any questions, suggestions or disagree with some concepts, please write a comment bellow.&lt;br /&gt;&lt;br /&gt;If you reside in Berlin or going to be here next Wednesday by accident or so, don't miss &lt;a href="http://www.django-de.org/news/58-zweiter-django-stammtisch-in-berlin/"&gt;the Django meetup at Schleusenkrug&lt;/a&gt; at 19:30.&lt;br /&gt;&lt;br /&gt;Also have a nice Easter Holiday!&lt;br /&gt;&lt;br /&gt;&lt;img src="http://3.bp.blogspot.com/_nG3SKqiLARM/SeFNM4RsmAI/AAAAAAAAATc/LSvn6DBUg_E/s400/django-easter-egg.png" border="0" alt="Django Easter Egg" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-1876431566352405992?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=_F2E4bEWIh0:rlrKI2dj-Hs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=_F2E4bEWIh0:rlrKI2dj-Hs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/djangotricks?a=_F2E4bEWIh0:rlrKI2dj-Hs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/djangotricks?i=_F2E4bEWIh0:rlrKI2dj-Hs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/_F2E4bEWIh0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/1876431566352405992/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/04/design-pattern-for-reusable-apps.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/1876431566352405992?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/1876431566352405992?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/_F2E4bEWIh0/design-pattern-for-reusable-apps.html" title="Design Pattern for Reusable Apps: Getting into Details" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_nG3SKqiLARM/SeFNM4RsmAI/AAAAAAAAATc/LSvn6DBUg_E/s72-c/django-easter-egg.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/04/design-pattern-for-reusable-apps.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0MEQXoyeSp7ImA9WxVWEE4.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-6181919280956479356</id><published>2009-02-19T11:30:00.000+01:00</published><updated>2009-02-19T11:30:00.491+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-19T11:30:00.491+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><title>Abstract Models and Dynamicly Assigned Foreign Keys</title><content type="html">&lt;a href="http://docs.djangoproject.com/en/dev/topics/db/models/#id4"&gt;Model inheritance&lt;/a&gt; enables creating extensible apps. You can define a reusable core app which includes &lt;code&gt;base.py&lt;/code&gt; with abstract models and &lt;code&gt;models.py&lt;/code&gt; with models extending the abstract ones and inheriting all the features. In the specific project you can either use the core app directly, or create a specific app which models extend from the base abstract models of the core app and additionally introduce new features.&lt;br /&gt;&lt;br /&gt;This is a quick example skipping all the unrelated parts like settings, urls, and templates:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;core_project&lt;ul&gt;&lt;li&gt;apps&lt;ul&gt;&lt;li&gt;player&lt;ul&gt;&lt;li&gt;&lt;strong&gt;base.py&lt;/strong&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db impport models&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;PlayerBase&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    name &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;100&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;class&lt;/span&gt; Meta&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        abstract &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;models.py&lt;/strong&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; core_project.apps.player.base &lt;span class="syntax8"&gt;import&lt;/span&gt; PlayerBase&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Player&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;PlayerBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;pass&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;specific_project&lt;ul&gt;&lt;br /&gt;&lt;li&gt;apps&lt;ul&gt;&lt;br /&gt;&lt;li&gt;player&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;models.py&lt;/strong&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; core_project.apps.player.base &lt;span class="syntax8"&gt;import&lt;/span&gt; PlayerBase&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Player&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;PlayerBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    points &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;IntegerField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The concept works fine until you need to use foreign keys or many-to-many relations in the abstract models. As &lt;a href="http://stackoverflow.com/questions/367461/setting-up-a-foreign-key-to-an-abstract-base-class-with-django"&gt;Josh Smeaton has already noticed&lt;/a&gt;, you can't set foreign keys to abstract models as they have no own database tables and they know nothing about the models which will extend them.&lt;br /&gt;&lt;br /&gt;Let's say, we have the following situation: &lt;code&gt;GameBase&lt;/code&gt; and &lt;code&gt;MissionBase&lt;/code&gt; are abstract models and the model extending &lt;code&gt;MissionBase&lt;/code&gt; should receive a foreign key to the model extending &lt;code&gt;GameBase&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/_nG3SKqiLARM/SZskz2eMmaI/AAAAAAAAASc/ue1czeQpVLI/s400/dynamic-foreign-keys.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5303873459372988834" /&gt;&lt;br /&gt;&lt;br /&gt;Thanks to &lt;a href="http://prodjango.com/"&gt;Pro Django book by Marty Alchin&lt;/a&gt;, I understood how the models get created in the background. By default, all python classes are constructed by the &lt;code&gt;type&lt;/code&gt; class. But whenever you use &lt;code&gt;__metaclass__&lt;/code&gt; property for your classes, you can define a different constructor. Django models are classes constructed by &lt;code&gt;ModelBase&lt;/code&gt; class which extends the &lt;code&gt;type&lt;/code&gt; class.&lt;br /&gt;&lt;br /&gt;In order to solve the problem of foreign keys to the models extending the abstract classes, we can have a custom constructor extending the &lt;code&gt;ModelBase&lt;/code&gt; class.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;base.py&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;-*-&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;coding:&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;utf-8&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;-*-&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db &lt;span class="syntax8"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db.models.base &lt;span class="syntax8"&gt;import&lt;/span&gt; ModelBase&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db.models.fields &lt;span class="syntax8"&gt;import&lt;/span&gt; FieldDoesNotExist&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;GameMissionCreator&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;ModelBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax14"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;The&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;model&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;extending&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;MissionBase&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;should&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;get&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;a&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;foreign&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;key&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;to&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;the&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;model&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;extending&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;GameBase&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    GameModel &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;br /&gt;    MissionModel &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax10"&gt;__new__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;cls, name, bases, attrs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        model &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;super&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;GameMissionCreator, cls&lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax10"&gt;__new__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;cls, name, bases, attrs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;for&lt;/span&gt; b &lt;span class="syntax8"&gt;in&lt;/span&gt; bases&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;if&lt;/span&gt; b.&lt;span class="syntax10"&gt;__name__&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;GameBase&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                cls.GameModel &lt;span class="syntax18"&gt;=&lt;/span&gt; model&lt;br /&gt;            &lt;span class="syntax8"&gt;elif&lt;/span&gt; b.&lt;span class="syntax10"&gt;__name__&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;MissionBase&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                cls.MissionModel &lt;span class="syntax18"&gt;=&lt;/span&gt; model&lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; cls.GameModel &lt;span class="syntax8"&gt;and&lt;/span&gt; cls.MissionModel&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;try&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                cls.MissionModel._meta.&lt;span class="syntax6"&gt;get_field&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;game&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;except&lt;/span&gt; FieldDoesNotExist&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                cls.MissionModel.&lt;span class="syntax6"&gt;add_to_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;                    &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;game&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;,&lt;br /&gt;                    models.&lt;span class="syntax6"&gt;ForeignKey&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;cls.GameModel&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;                    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;return&lt;/span&gt; model&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;GameBase&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax10"&gt;__metaclass__&lt;/span&gt; &lt;span class="syntax18"&gt;=&lt;/span&gt; GameMissionCreator&lt;br /&gt;&lt;br /&gt;    title &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;100&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;class&lt;/span&gt; Meta&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        abstract &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;MissionBase&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax10"&gt;__metaclass__&lt;/span&gt; &lt;span class="syntax18"&gt;=&lt;/span&gt; GameMissionCreator&lt;br /&gt;    &lt;br /&gt;    title &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;100&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;class&lt;/span&gt; Meta&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        abstract &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;models.py&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;-*-&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;coding:&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;utf-8&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;-*-&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; base &lt;span class="syntax8"&gt;import&lt;/span&gt; &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Game&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;GameBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;pass&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Mission&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;MissionBase&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;pass&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;GameMissionCreator&lt;/code&gt; is a constructor of &lt;code&gt;GameBase&lt;/code&gt;, &lt;code&gt;MissionBase&lt;/code&gt;, &lt;code&gt;Game&lt;/code&gt;, and &lt;code&gt;Mission&lt;/code&gt; classes. When it creates a class extending &lt;code&gt;GameBase&lt;/code&gt;, the game model is registered as a property. When it creates a class extending &lt;code&gt;MissionBase&lt;/code&gt;, the mission model is registered as a property. When both models are registered, a foreign key is added dynamically from one model to the other.&lt;br /&gt;&lt;br /&gt;One drawback of this constructor-class example is that if there are more than one classes extending &lt;code&gt;GameBase&lt;/code&gt; or &lt;code&gt;MissionBase&lt;/code&gt;, then the code won't function correctly.&lt;br /&gt;&lt;br /&gt;Anyway, the example shown illustrates the possible solution and gives a direction for further development of the idea.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-6181919280956479356?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=xu2G5Dbi"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=xu2G5Dbi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=GLqGtZbJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=GLqGtZbJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/tgB5unLi24g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/6181919280956479356/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2009/02/abstract-models-and-dynamicly-assigned.html#comment-form" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/6181919280956479356?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/6181919280956479356?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/tgB5unLi24g/abstract-models-and-dynamicly-assigned.html" title="Abstract Models and Dynamicly Assigned Foreign Keys" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_nG3SKqiLARM/SZskz2eMmaI/AAAAAAAAASc/ue1czeQpVLI/s72-c/dynamic-foreign-keys.png" height="72" width="72" /><thr:total>5</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2009/02/abstract-models-and-dynamicly-assigned.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEIAQX06eip7ImA9WxRaE0w.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-5614470167500439938</id><published>2008-12-15T06:00:00.001+01:00</published><updated>2008-12-15T05:49:00.312+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-15T05:49:00.312+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><category scheme="http://www.blogger.com/atom/ns#" term="Fields" /><title>Clarification for Confusion: Creating New Django Fields. Part 1</title><content type="html">Let's say you've just published your first Django-based project. You've learned a lot, you know how to create a new app, views, forms, middleware, and template tags. That's cool, but there is still much space for making your code cleaner and better-organized. One of such improvements could be joining multiple fields into a single field.&lt;br /&gt;&lt;br /&gt;To be clear, there are several types of fields in Django web development and all of them are related to each other. First of all, there are the &lt;strong&gt;fields of database tables&lt;/strong&gt; in the lowest level. Then there are &lt;strong&gt;model fields&lt;/strong&gt; representing the database fields in python. &lt;strong&gt;Form fields&lt;/strong&gt; go on top in the abstraction level. Then there are &lt;strong&gt;bound fields&lt;/strong&gt; which bound the form fields with data from the database (or somewhere else). And finally there are &lt;strong&gt;HTML fields&lt;/strong&gt; which represent the rendered bound fields and which can be spiced up with Javascript for creating new widgets.&lt;br /&gt;&lt;br /&gt;Now imagine, that you have a Contact model which includes two phone numbers where each of them has three parts. They are the country code, area code, and the serial number. In the simplest case you would have something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;COUNTRY_CODE_CHOICES &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;getattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;settings, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;COUNTRY_CODE_CHOICES&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;44&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;United&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Kingdom&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;44&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;49&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Germany&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;49&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;370&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Lithuania&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;370&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Contact&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    user &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;OneToOneField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;User&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    website &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;URLField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Website&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    primary_phone_country &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Primary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Country&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Code&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;        max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;4&lt;/span&gt;,&lt;br /&gt;        choices&lt;span class="syntax18"&gt;=&lt;/span&gt;COUNTRY_CODE_CHOICES,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    primary_phone_area &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Primary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Area&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Code&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;        max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;4&lt;/span&gt;,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    primary_phone_number &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Primary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Number&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;        max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;10&lt;/span&gt;,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    secondary_phone_country &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Secondary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Country&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Code&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;        max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;4&lt;/span&gt;,&lt;br /&gt;        choices&lt;span class="syntax18"&gt;=&lt;/span&gt;COUNTRY_CODE_CHOICES,&lt;br /&gt;        blank&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;True&lt;/span&gt;,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    secondary_phone_area &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Secondary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Area&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Code&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;        max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;4&lt;/span&gt;,&lt;br /&gt;        blank&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;True&lt;/span&gt;,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    secondary_phone_number &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Secondary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Number&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;        max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;10&lt;/span&gt;,&lt;br /&gt;        blank&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;True&lt;/span&gt;,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But it seems to be such a mess when it could be something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;Contact&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    user &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;OneToOneField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;User&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    website &lt;span class="syntax18"&gt;=&lt;/span&gt; models.&lt;span class="syntax6"&gt;URLField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Website&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    primary_phone &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;PhoneField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Primary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Phone&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    secondary_phone &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;PhoneField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Secondary&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Code&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;, blank&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So the question now is how to create a model field that would be saved as three separate database fields and would be shown as three bound fields when editing. Let's have a look what information is available about custom model fields. There is a &lt;a href="http://docs.djangoproject.com/en/dev/howto/custom-model-fields/"&gt;page in the documentation&lt;/a&gt; which might help us here. Also there are articles by &lt;a href="http://www.davidcramer.net/code/181/custom-fields-in-django.html"&gt;David Cramer&lt;/a&gt; and &lt;a href="http://blog.elsdoerfer.name/2008/01/08/fuzzydates-or-one-django-model-field-multiple-database-columns/"&gt;Michael Elsdörfer&lt;/a&gt; which might help to solve this problem. In addition, I found a snippet by &lt;a href="http://www.djangosnippets.org/snippets/1169/"&gt;Myles Braithwaite&lt;/a&gt; which also deals about custom model fields.&lt;br /&gt;&lt;br /&gt;Combining the ideas from the given sources with the analysis of the Django core code, I defined the &lt;code&gt;PhoneField&lt;/code&gt; in the following way:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;-*-&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;coding:&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;utf-8&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;-*-&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.db &lt;span class="syntax8"&gt;import&lt;/span&gt; models&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.conf &lt;span class="syntax8"&gt;import&lt;/span&gt; settings&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.utils.translation &lt;span class="syntax8"&gt;import&lt;/span&gt; ugettext_lazy &lt;span class="syntax8"&gt;as&lt;/span&gt; _&lt;br /&gt;&lt;br /&gt;PHONE_COUNTRY_CODE_CHOICES &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;getattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;settings, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;PHONE_COUNTRY_CODE_CHOICES&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;44&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;United&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Kingdom&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;44&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;49&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Germany&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;49&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;370&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Lithuania&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;370&lt;/span&gt;&lt;span class="syntax13"&gt;)&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;PhoneField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax10"&gt;__init__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, verbose_name&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;None&lt;/span&gt;, country_code_choices&lt;span class="syntax18"&gt;=&lt;/span&gt;PHONE_COUNTRY_CODE_CHOICES, &lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax9"&gt;super&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;PhoneField, &lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax10"&gt;__init__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.country_code_choices &lt;span class="syntax18"&gt;=&lt;/span&gt; country_code_choices&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.verbose_name &lt;span class="syntax18"&gt;=&lt;/span&gt; verbose_name&lt;br /&gt;        &lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;contribute_to_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, cls, name&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.name &lt;span class="syntax18"&gt;=&lt;/span&gt; name&lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.verbose_name &lt;span class="syntax8"&gt;is&lt;/span&gt; &lt;span class="syntax10"&gt;None&lt;/span&gt; &lt;span class="syntax8"&gt;and&lt;/span&gt; name&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;self&lt;/span&gt;.verbose_name &lt;span class="syntax18"&gt;=&lt;/span&gt; name.&lt;span class="syntax6"&gt;replace&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;_&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, &lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;creating&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;three&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;model&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;fields&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;on&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;the&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;fly&lt;/span&gt;&lt;br /&gt;        models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Country&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Code&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.verbose_name,&lt;br /&gt;            max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;4&lt;/span&gt;,&lt;br /&gt;            choices&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.country_code_choices,&lt;br /&gt;            blank&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.blank,&lt;br /&gt;            &lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax6"&gt;contribute_to_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;cls, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_country&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; name&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Area&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Code&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.verbose_name,&lt;br /&gt;            max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;4&lt;/span&gt;,&lt;br /&gt;            blank&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.blank,&lt;br /&gt;            &lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax6"&gt;contribute_to_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;cls, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_area&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; name&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        models.&lt;span class="syntax6"&gt;CharField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Number&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.verbose_name,&lt;br /&gt;            max_length&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax5"&gt;10&lt;/span&gt;,&lt;br /&gt;            blank&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.blank,&lt;br /&gt;            &lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax6"&gt;contribute_to_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;cls, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_number&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; name&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;when&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;accessing&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;the&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;phone&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;by&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;original&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;model&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;field&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;name,&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;we'll&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;manage&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;tuples&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;of&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;country&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;code,&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;area&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;code,&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;and&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;number&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax9"&gt;setattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;cls, &lt;span class="syntax8"&gt;self&lt;/span&gt;.name, &lt;span class="syntax6"&gt;PhoneFieldCreator&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;PhoneFieldCreator&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax9"&gt;object&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax10"&gt;__init__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, field&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;  &lt;br /&gt;        &lt;span class="syntax8"&gt;self&lt;/span&gt;.field &lt;span class="syntax18"&gt;=&lt;/span&gt; field&lt;br /&gt;   &lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;__get__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, obj, &lt;span class="syntax9"&gt;type&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; obj &lt;span class="syntax8"&gt;is&lt;/span&gt; &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;  &lt;br /&gt;            &lt;span class="syntax8"&gt;raise&lt;/span&gt; &lt;span class="syntax10"&gt;AttributeError&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;Can&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;only&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;be&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;accessed&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;via&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;an&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;instance&lt;/span&gt;&lt;span class="syntax13"&gt;.&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;  &lt;br /&gt;        country &lt;span class="syntax18"&gt;=&lt;/span&gt; obj.&lt;span class="syntax10"&gt;__dict__&lt;/span&gt;.&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_country&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.field.name, &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        area &lt;span class="syntax18"&gt;=&lt;/span&gt; obj.&lt;span class="syntax10"&gt;__dict__&lt;/span&gt;.&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_area&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.field.name, &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        number &lt;span class="syntax18"&gt;=&lt;/span&gt; obj.&lt;span class="syntax10"&gt;__dict__&lt;/span&gt;.&lt;span class="syntax6"&gt;get&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_number&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.field.name, &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;return&lt;/span&gt; &lt;span class="syntax18"&gt;(&lt;/span&gt;country &lt;span class="syntax8"&gt;and&lt;/span&gt; area &lt;span class="syntax8"&gt;and&lt;/span&gt; number&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax8"&gt;and&lt;/span&gt; &lt;span class="syntax18"&gt;(&lt;/span&gt;country, area, number&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax8"&gt;or&lt;/span&gt; &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;br /&gt;   &lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;__set__&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, obj, value&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;  &lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax9"&gt;isinstance&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;value, &lt;span class="syntax9"&gt;tuple&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax8"&gt;and&lt;/span&gt; &lt;span class="syntax9"&gt;len&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;value&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax5"&gt;3&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax9"&gt;setattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;obj, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_country&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.field.name, value[&lt;span class="syntax5"&gt;0&lt;/span&gt;]&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax9"&gt;setattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;obj, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_area&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.field.name, value[&lt;span class="syntax5"&gt;1&lt;/span&gt;]&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax9"&gt;setattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;obj, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;_number&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.field.name, value[&lt;span class="syntax5"&gt;2&lt;/span&gt;]&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that the instances of the model with the &lt;code&gt;PhoneField&lt;/code&gt; can be filtered by any part of the phone number. In addition, the field value can be accessed as a tuple of three parts of the number. For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax18"&gt;&amp;gt;&lt;/span&gt;&lt;span class="syntax18"&gt;&amp;gt;&lt;/span&gt;&lt;span class="syntax18"&gt;&amp;gt;&lt;/span&gt; Contact.objects.&lt;span class="syntax9"&gt;filter&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;primary_phone_country&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;370&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;[&lt;span class="syntax5"&gt;0&lt;/span&gt;].primary_phone&lt;br /&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;u&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;+&lt;/span&gt;&lt;span class="syntax13"&gt;370&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, u&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;628&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, u&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;12345&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now the phone number can be presented in different ways in templates depending on the business requirements or local flavor:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;{{ obj.primary_phone|join:"" }}&lt;br /&gt;{{ obj.primary_phone|join:" " }}&lt;br /&gt;{{ obj.primary_phone|join:"-" }}&lt;br /&gt;{{ obj.primary_phone.0 }} ({{ obj.primary_phone.1 }}) {{ obj.primary_phone.2 }}&lt;br /&gt;{{ obj.primary_phone_country }} ({{ obj.primary_phone_area }}) {{ obj.primary_phone_number }}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's check how to create model fields which have single database table field, but multiple form fields in the Part 2.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-5614470167500439938?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=oiXv4rh3"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=oiXv4rh3" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=2q2WOAkO"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=2q2WOAkO" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/zCButXyAxMs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/5614470167500439938/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/09/clarification-for-confusion-creating.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5614470167500439938?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5614470167500439938?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/zCButXyAxMs/clarification-for-confusion-creating.html" title="Clarification for Confusion: Creating New Django Fields. Part 1" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/09/clarification-for-confusion-creating.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0YBR3w7eip7ImA9WxRXFUk.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-4904133927245244495</id><published>2008-10-21T01:14:00.005+02:00</published><updated>2008-10-21T01:25:56.202+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-21T01:25:56.202+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="App" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Project" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><title>Django Poor Man's Cron</title><content type="html">As I have promised in the &lt;a href="http://djangotricks.blogspot.com/2008/10/exploiting-spammers-search-engines-and.html"&gt;last blog entry&lt;/a&gt;, I am sharing a link to an experimental conceptual app &lt;a href="http://code.google.com/p/django-poormanscron/"&gt;Poor Man's Cron&lt;/a&gt; which tends to exploit various bots to implement scheduled tasks.&lt;br /&gt;&lt;br /&gt;Criticisms, praises, thanks, and suggestions are very welcome!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-4904133927245244495?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=Bn3A18iP"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=Bn3A18iP" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=b8lRJiwF"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=b8lRJiwF" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/rBNlajH0j0E" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/4904133927245244495/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/10/django-poor-mans-cron.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/4904133927245244495?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/4904133927245244495?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/rBNlajH0j0E/django-poor-mans-cron.html" title="Django Poor Man's Cron" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/10/django-poor-mans-cron.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMDQH47fSp7ImA9WxRXEUk.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-5674009625618583899</id><published>2008-10-16T11:00:00.001+02:00</published><updated>2008-10-16T11:14:31.005+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-16T11:14:31.005+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="People" /><category scheme="http://www.blogger.com/atom/ns#" term="Blog Action Day" /><category scheme="http://www.blogger.com/atom/ns#" term="World" /><title>Exploiting Spammers, Search Engines, and Other Bots</title><content type="html">Django is a great platform for building web projects in a clean and manageable way. But when you want to find its relationship with &lt;strong&gt;poverty&lt;/strong&gt; for a blog post on &lt;a href="http://blogactionday.org/"&gt;Blog Action Day&lt;/a&gt;, it becomes a difficult puzzle. But still that's a great challenge!&lt;br /&gt;&lt;br /&gt;Django has a &lt;a href="http://djangopeople.net/"&gt;large community&lt;/a&gt; all over the world and someone who is gold at statistics could write a cool analytical report about djangoers' relations with &lt;a href="http://graphs.gapminder.org/world/#$majorMode=chart$is;shi=t;ly=2003;lb=f;il=t;fs=11;al=30;stl=t;st=t;nsl=t;se=t$wst;tts=C$ts;sp=6;ti=2006$zpv;v=1$inc_x;mmid=XCOORDS;iid=phAwcNAVuyj1jiMAkmq1iMg;by=ind$inc_y;mmid=YCOORDS;iid=phAwcNAVuyj2tPLxKvvnNPA;by=ind$inc_s;uniValue=8.21;iid=phAwcNAVuyj0XOoBL_n5tAQ;by=ind$inc_c;uniValue=255;gid=CATID0;by=grp$map_x;scale=log;dataMin=199;dataMax=42642$map_y;scale=lin;dataMin=25;dataMax=84$map_s;sma=49;smi=2.65$cd;bd=0$inds=#$majorMode=chart$is;shi=t;ly=2003;lb=f;il=t;fs=11;al=30;stl=t;st=t;nsl=t;se=t$wst;tts=C$ts;sp=6;ti=2006$zpv;v=1$inc_x;mmid=XCOORDS;iid=phAwcNAVuyj1jiMAkmq1iMg;by=ind$inc_y;mmid=YCOORDS;iid=phAwcNAVuyj0xiDjWaypQTg;by=ind$inc_s;uniValue=8.21;iid=phAwcNAVuyj0XOoBL%5Fn5tAQ;by=ind$inc_c;uniValue=255;gid=CATID0;by=grp$map_x;scale=log;dataMin=199;dataMax=42642$map_y;scale=log;dataMin=0;dataMax=89$map_s;sma=49;smi=2.65$cd;bd=0$inds="&gt;poverty in the world&lt;/a&gt; or the &lt;a href="http://my8.statcounter.com/project/standard2/visitor_map.php?project_id=4087455"&gt;user activity&lt;/a&gt; on Blog Action Day site using the maps and numbers provided in the links.&lt;br /&gt;&lt;br /&gt;The Django community or the social groups mentioned above are just small parts of the big network. Looking at the &lt;a href="http://www.youtube.com/watch?v=yDYCf4ONh5M"&gt;bigger image of the world, internet, and the future&lt;/a&gt;, you might realize that everybody is connected to somebody and something, all those connections get defined in social networks, and the web is like a living and evolving organism. We are kind of going towards a machine that we can use and it can use us (See &lt;a href="http://whatisthematrix.warnerbros.com/"&gt;the Matrix&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;We can look at a newly created site based on Django as at a small node in the web. The site together with its visitors forms a network. Different APIs and inter-site communication extend the network even more. One part of this network is various bots, i.e. search engine indexers and comment spambots. Thank to the error-reporting mechanism in Django, recently I found out that you can find bugs in &lt;a href="http://www.creative-city-berlin.de"&gt;large-scale projects&lt;/a&gt; not only by manual browsing or test cases. Bugs might be detected by visits of spammers and search engines. Broken pages or unexpected usage are reported by email to the administrator of the site. Moreover, I started brainstorming how to exploit spammers instead of fighting against them.&lt;br /&gt;&lt;br /&gt;And then I remembered the &lt;a href="http://drupal.org/project/poormanscron"&gt;Poor Man's Cron&lt;/a&gt; module for &lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt;. The module is an alternative for projects which have no cron jobs supported. It runs scheduled tasks in approximately regular intervals of time, triggered by page views. Executing scheduled tasks via the page views for people might be annoying if that takes much time. But I wouldn't mind to waste a few seconds of machines. On one hand this idea supports the poverty theme for the Blog Action Day, because this kind of module is dedicated to those who can't afford getting a server with cron jobs. On another hand, we can punish spammers using their time for our needs. When the tasks are quite time consuming and takes 5 or more minutes to execute, let's use the time of comment spammers. Spammers might be recognized by &lt;a href="http://www.djangosnippets.org/snippets/433/"&gt;captchas&lt;/a&gt;, &lt;a href="http://sciyoshi.com/blog/2008/aug/27/using-akismet-djangos-new-comments-framework/"&gt;Akismet filters&lt;/a&gt;, and &lt;a href="http://www.djangosnippets.org/snippets/1092/"&gt;similar technologies&lt;/a&gt;. When more precise intervals between executions are necessary, let's exploit the feeds-subscriber calls and search indexers. Those can be recognized by &lt;code&gt;request.META['HTTP_USER_AGENT']&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;I shall point you to the experimental conceptual code of this idea soon.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blogactionday.org"&gt;&lt;img border="0" src="http://blogactionday.org/img/c01744538a98b43378f34a2befd646d60302af84.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Similar projects:&lt;br /&gt;http://code.google.com/p/django-cron/&lt;br /&gt;http://www.djangosnippets.org/snippets/1126/&lt;br /&gt;&lt;br /&gt;How would you suggest to exploit spambots? What drawbacks do you see from the suggested approach?&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;P.S. To those who reside in Berlin! Today at 19:00 there will be Djangoers' meetup at newthinking store, Tucholskystraße 48, 10117 Berlin.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-5674009625618583899?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=UhexjT13"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=UhexjT13" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=76B9CoPz"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=76B9CoPz" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/FyWSfnd_5Ro" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/5674009625618583899/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/10/exploiting-spammers-search-engines-and.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5674009625618583899?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5674009625618583899?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/FyWSfnd_5Ro/exploiting-spammers-search-engines-and.html" title="Exploiting Spammers, Search Engines, and Other Bots" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/10/exploiting-spammers-search-engines-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkIEQXw_fSp7ImA9WxRSFE8.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-3795162285993781824</id><published>2008-09-13T05:48:00.002+02:00</published><updated>2008-09-14T22:35:00.245+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-14T22:35:00.245+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Intermediate" /><category scheme="http://www.blogger.com/atom/ns#" term="Administration" /><category scheme="http://www.blogger.com/atom/ns#" term="Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="Testing" /><category scheme="http://www.blogger.com/atom/ns#" term="Deployment" /><title>Gotchas about Fixtures for Initial Data</title><content type="html">One part of Django testing environment is &lt;a href="http://docs.djangoproject.com/en/dev/topics/testing/#fixture-loading"&gt;fixtures&lt;/a&gt;. They are pre-prepared datasets to fill into the database before running tests. Django uses a separate database for running tests, but the fixtures from the files &lt;code&gt;initial_data.*&lt;/code&gt; are also loaded into the main database when you synchronize it.&lt;br /&gt;&lt;br /&gt;I make fixtures for my application like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;python manage.py dumpdata --format=json --indent=4 &lt;em&gt;myapp&lt;/em&gt; &amp;gt; apps/&lt;em&gt;myapp&lt;/em&gt;/fixtures/initial_data.json &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The initial data is read out from &lt;code&gt;apps/&lt;em&gt;myapp&lt;/em&gt;/fixtures/initial_data.json&lt;/code&gt; and written to the main or the test database when I synchronize the database&lt;br /&gt;&lt;pre&gt;&lt;code&gt;python manage.py syncdb&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;or when I run the tests&lt;br /&gt;&lt;pre&gt;&lt;code&gt;python manage.py test &lt;em&gt;myapp&lt;/em&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Fixtures are great for deploying standard data like predefined categories, lists of countries and languages, default flatpages, default navigation, default user groups, and so on. However, you should be very cautious with them while developing.&lt;br /&gt;&lt;br /&gt;When I create new models, it's common practice to me to &lt;code&gt;sync db&lt;/code&gt; so that the new database tables are created and the database schema reflects the models. Just after creation of new tables all fixtures called &lt;code&gt;initial_data&lt;/code&gt; from all applications will be loaded. &lt;strong&gt;N.B. The fixtures from &lt;code&gt;initial_data&lt;/code&gt; overwrite all existing data while synchronizing database&lt;/strong&gt;. So if you have some important data that differs from the defaults, better make some database backup before syncing or use sql command to print out the SQL statements and execute them for the database manually:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;python manage.py sql &lt;em&gt;myapp_with_new_models&lt;/em&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You might have &lt;a href="http://docs.djangoproject.com/en/dev/topics/signals/#connecting-to-signals-sent-by-specific-senders"&gt;&lt;code&gt;pre_save&lt;/code&gt; signal handlers&lt;/a&gt; or custom save methods (check an example below) which should recognize newly created objects and do something special with them, i.e. prepare PDF reports, generate images, send emails, index for global text search, or something else. Usually in such cases I checked the existence of the primary key: the object is new if it has no primary key. But this is wrong when you use fixtures, because fixtures come with primary keys. &lt;strong&gt;N.B. The object is new only if there is no object in the database which primary key equals to the primary key of the current object.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;&lt;em&gt;MyModel&lt;/em&gt;&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    ...&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, &lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        is_new &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;        pk &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.&lt;span class="syntax6"&gt;_get_pk_val&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        model &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;type&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; pk &lt;span class="syntax8"&gt;and&lt;/span&gt; model._default_manager.&lt;span class="syntax9"&gt;filter&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;pk&lt;span class="syntax18"&gt;=&lt;/span&gt;pk&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            is_new &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;False&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;something&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;before&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;saving&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax9"&gt;super&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;model, &lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;something&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;after&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;saving&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;        &lt;br /&gt;aka&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;&lt;em&gt;MyModel&lt;/em&gt;&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;models.Model&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    ...&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, &lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        is_new &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;True&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax8"&gt;self&lt;/span&gt;.&lt;span class="syntax9"&gt;id&lt;/span&gt; &lt;span class="syntax8"&gt;and&lt;/span&gt; &lt;em&gt;MyModel&lt;/em&gt;.objects.&lt;span class="syntax9"&gt;filter&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax9"&gt;id&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;.&lt;span class="syntax9"&gt;id&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            is_new &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax10"&gt;False&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;something&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;before&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;saving&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax9"&gt;super&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;em&gt;MyModel&lt;/em&gt;, &lt;span class="syntax8"&gt;self&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;something&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;after&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;saving&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another alternative for storing default data would be &lt;a href="http://docs.djangoproject.com/en/dev/ref/django-admin/#sqlcustom-appname-appname"&gt;custom sql&lt;/a&gt; located at &lt;code&gt;apps/&lt;em&gt;myapp&lt;/em&gt;/sql/&lt;em&gt;mymodel&lt;/em&gt;.sql&lt;/code&gt;, but I haven't tried that yet and probably won't.&lt;br /&gt;&lt;br /&gt;BTW, happy &lt;a href="http://programmerday.info/"&gt;programmer day&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-3795162285993781824?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=qTXU4VIv"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=qTXU4VIv" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=bVrSGQWf"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=bVrSGQWf" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/jXnZpUz_NpA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/3795162285993781824/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/09/gotchas-about-fixtures-for-initial-data.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3795162285993781824?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3795162285993781824?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/jXnZpUz_NpA/gotchas-about-fixtures-for-initial-data.html" title="Gotchas about Fixtures for Initial Data" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/09/gotchas-about-fixtures-for-initial-data.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08NQnY_cSp7ImA9WxRTFEg.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-5981732625085035088</id><published>2008-09-03T13:54:00.011+02:00</published><updated>2008-09-03T17:31:33.849+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-03T17:31:33.849+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Administration" /><category scheme="http://www.blogger.com/atom/ns#" term="Basics" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><title>A Note on Python Paths</title><content type="html">This time I decided to share some knowledge about Python paths which seemed a little bit confusing to me in the beginning of diving into Python. I am working with Django in different platforms like Mac OS X, Windows, and Linux, therefore the common patterns how to activate new python modules in all of those environments should be familiar to me.&lt;br /&gt;&lt;br /&gt;Python modules are either &lt;code&gt;*.py&lt;/code&gt; files or directories containing &lt;code&gt;__init__.py&lt;/code&gt;. When defining paths to python modules, you will usually need to deal with the latter ones. A module is meant to be under python path if you can run python and import that module.&lt;br /&gt;&lt;br /&gt;For example, if you can run the following, then django is under your python path.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;python&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; import django&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Stay tuned to get deeper into python paths.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Installing modules&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;If a module is installable, usually all you need to do is to extract its setup directory, &lt;code&gt;cd&lt;/code&gt; to it, and run&lt;br /&gt;&lt;pre&gt;&lt;code&gt;python setup.py install&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This will copy the module into the &lt;strong&gt;site-packages&lt;/strong&gt; directory of the current python installation. It might be that you have multiple Python versions on your computer. According to &lt;a href="http://docs.djangoproject.com/en/dev/topics/install/#remove-any-old-versions-of-django"&gt;django documentation&lt;/a&gt;, you can find the currently used site-packages by&lt;br /&gt;&lt;pre&gt;&lt;code&gt;python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Or you can use &lt;a href="http://peak.telecommunity.com/DevCenter/EasyInstall#downloading-and-installing-a-package"&gt;PEAK EasyInstall&lt;/a&gt; for installing python modules even faster.&lt;br /&gt;&lt;br /&gt;But sometimes you will need the latest and greatest versions of your modules directly from version control system. To make them accessible from python you should either check them out directly to site-packages (very messy and inflexible) or keep them somewhere else and do some additional magic.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Sym-linking&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;You can create symbolic links (symlinks) in unix-based systems like Linux or Mac OS X. A symlink is like a shortcut to a file or directory. If you create a symlink in site-packages which points to a python module which is located somewhere else, it will work as if the module was copied into site-packages.&lt;br /&gt;&lt;br /&gt;To create a symlink, type the following in a console/terminal:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ln -s &amp;lt;source&amp;gt; &amp;lt;target&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;For example, if you want python to access &lt;code&gt;django&lt;/code&gt; which is under &lt;code&gt;/Library/Subversion/django_src/trunk/django&lt;/code&gt;, you need to write something like this (considering that your site-packages are at &lt;code&gt;/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/&lt;/code&gt;)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ln -s /Library/Subversion/django_src/trunk/django /Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/django&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;To delete the symlink, simply remove it (this won't delete the original module):&lt;br /&gt;&lt;pre&gt;&lt;code&gt;rm /Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/django&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But as I've already mentioned, this works only in unix-based environments and you can't use shortcuts in Windows for the same purpose.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;code&gt;*.pth&lt;/code&gt; files&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Python supports &lt;code&gt;*.pth&lt;/code&gt; files which contain &lt;strong&gt;the paths to the parent directories of your python modules&lt;/strong&gt; one per line. Those files should be placed in site-packages and can be called whatever you want them to call. For example, you can create a file &lt;code&gt;my-project.pth&lt;/code&gt; and write&lt;br /&gt;&lt;pre&gt;&lt;code&gt;/Library/Subversion/django_src/trunk&lt;br /&gt;/Library/Subversion/myproject_src/trunk&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;or&lt;br /&gt;&lt;pre&gt;&lt;code&gt;C:\Subversion\django_src\trunk&lt;br /&gt;C:\Subversion\myproject\trunk&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;into it. Then django and your project files will be importable in python.&lt;br /&gt;&lt;br /&gt;However, you might have no permissions to create files under site-packages or you might need to activate different locations of python modules for different projects.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;code&gt;PYTHONPATH&lt;/code&gt; variable&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The other way is to set additional paths for python just before running the externally kept modules. This is done by setting the python paths to the environment variable &lt;code&gt;PYTHONPATH&lt;/code&gt;. &lt;strong&gt;Note again that python paths point not to the modules themselves, but to their parent directories!&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The syntax slightly differs among different platforms.&lt;br /&gt;&lt;br /&gt;Linux and Mac OS X: &lt;br /&gt;&lt;pre&gt;&lt;code&gt;# checking value&lt;br /&gt;echo $PYTHOPATH&lt;br /&gt;# setting value&lt;br /&gt;export PYTHONPATH="/Library/Subversion/django_src/trunk"&lt;br /&gt;# appending to the existing value&lt;br /&gt;export PYTHONPATH="$PYTHONPATH;/Library/Subversion/django_src/trunk"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Windows: &lt;br /&gt;&lt;pre&gt;&lt;code&gt;# checking value&lt;br /&gt;echo %PYTHOPATH%&lt;br /&gt;# setting value&lt;br /&gt;set PYTHONPATH="C:\\Subversion\\django_src\\trunk"&lt;br /&gt;# appending to the existing value&lt;br /&gt;set PYTHONPATH="%PYTHOPATH%;C:\\Subversion\\django_src\\trunk"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Multiple paths can be separated by a colon (";").&lt;br /&gt;&lt;br /&gt;&lt;code&gt;PYTHONPATH&lt;/code&gt; can be used in scripts and webserver configuration files, but it is not very comfortable in daily use.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Adding paths to &lt;code&gt;sys.path&lt;/code&gt;&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;For the projects that you develop and which should run as standalone applications, you can set the required python paths relatively inside you python code.&lt;br /&gt;&lt;br /&gt;Note that all python paths which you set in the &lt;code&gt;PYTHONPATH&lt;/code&gt; variable or &lt;code&gt;*.pth&lt;/code&gt; files as well as the path of default python libraries and the path of site-packages get listed in python variable &lt;code&gt;sys.path&lt;/code&gt;. When you import a module, it is loaded from the first location which contains the required module. So if you have two paths to different django versions in your python paths and you import django, the django version from the first location will be used.&lt;br /&gt;&lt;br /&gt;You can read the list of loaded python paths like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import sys&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; sys.path&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can also freely modify it, for example:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import sys&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; sys.path.append("/Library/Subversion/django_src/trunk")&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; import django&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And this is an example, how to get and use paths relative to the currently loaded file:&lt;br /&gt;&lt;pre&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;import&lt;/span&gt; os, sys&lt;br /&gt;&lt;br /&gt;SVN_PATH &lt;span class="syntax18"&gt;=&lt;/span&gt; os.path.&lt;span class="syntax6"&gt;abspath&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;os.path.&lt;span class="syntax6"&gt;join&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;os.path.&lt;span class="syntax6"&gt;dirname&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax10"&gt;__file__&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;.&lt;/span&gt;&lt;span class="syntax13"&gt;.&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;.&lt;/span&gt;&lt;span class="syntax13"&gt;.&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;DJANGO_PATH &lt;span class="syntax18"&gt;=&lt;/span&gt; os.path.&lt;span class="syntax6"&gt;join&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;SVN_PATH, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;django_src&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;trunk&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;PROJECT_PATH &lt;span class="syntax18"&gt;=&lt;/span&gt; os.path.&lt;span class="syntax6"&gt;join&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;SVN_PATH, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;myproject&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;trunk&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;sys.path &lt;span class="syntax18"&gt;+&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt; [DJANGO_PATH, PROJECT_PATH]&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I hope this introduction was useful for &lt;a href="http://www.youtube.com/watch?v=KMU0tzLwhbE"&gt;developers&lt;/a&gt; and made the big picture about the paths clearer.&lt;br /&gt;&lt;br /&gt;Some more related information can be found at the official &lt;a href="http://docs.python.org/inst/search-path.html"&gt;python documentation&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-5981732625085035088?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=hOH9s3ID"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=hOH9s3ID" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=pP1LsUw1"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=pP1LsUw1" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/fY8ZlExowjE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/5981732625085035088/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/09/note-on-python-paths.html#comment-form" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5981732625085035088?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5981732625085035088?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/fY8ZlExowjE/note-on-python-paths.html" title="A Note on Python Paths" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>5</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/09/note-on-python-paths.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMGSXk-cSp7ImA9WxdbEkw.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-4978844507322928721</id><published>2008-08-08T18:05:00.003+02:00</published><updated>2008-08-08T18:20:28.759+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-08-08T18:20:28.759+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Intermediate" /><category scheme="http://www.blogger.com/atom/ns#" term="Forms" /><category scheme="http://www.blogger.com/atom/ns#" term="Widget" /><title>MultipleSubmitButton Widget for ChoiceField</title><content type="html">Recently I published a &lt;a href="http://www.djangosnippets.org/snippets/951/"&gt;snippet&lt;/a&gt; with a widget rendering a choice field as a series of submit buttons.&lt;br /&gt;&lt;br /&gt;So the &lt;code&gt;{{ form.do }}&lt;/code&gt; field from the following form:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="syntax0"&gt;SUBMIT_CHOICES &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;save&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Save&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;save&lt;/span&gt;&lt;span class="syntax13"&gt;-&lt;/span&gt;&lt;span class="syntax13"&gt;add&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;, &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;Save&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;and&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Add&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Another&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;    &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;TestForm&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;forms.Form&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    do &lt;span class="syntax18"&gt;=&lt;/span&gt; forms.&lt;span class="syntax6"&gt;ChoiceField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        widget&lt;span class="syntax18"&gt;=&lt;/span&gt;MultipleSubmitButton,&lt;br /&gt;        choices&lt;span class="syntax18"&gt;=&lt;/span&gt;SUBMIT_CHOICES,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;will be rendered as:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;ul&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;li&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;button&lt;/span&gt;&lt;span class="syntax17"&gt; &lt;/span&gt;&lt;span class="syntax17"&gt;type&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;submit&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax17"&gt; &lt;/span&gt;&lt;span class="syntax17"&gt;name&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;do&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax17"&gt; &lt;/span&gt;&lt;span class="syntax17"&gt;value&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;save&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;/&lt;/span&gt;&lt;span class="syntax17"&gt;button&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;/&lt;/span&gt;&lt;span class="syntax17"&gt;li&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;li&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;button&lt;/span&gt;&lt;span class="syntax17"&gt; &lt;/span&gt;&lt;span class="syntax17"&gt;type&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;submit&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax17"&gt; &lt;/span&gt;&lt;span class="syntax17"&gt;name&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;do&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax17"&gt; &lt;/span&gt;&lt;span class="syntax17"&gt;value&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;save-add&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;Save and Add Another&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;/&lt;/span&gt;&lt;span class="syntax17"&gt;button&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;/&lt;/span&gt;&lt;span class="syntax17"&gt;li&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;/&lt;/span&gt;&lt;span class="syntax17"&gt;ul&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Can somebody enhance this widget so that it supports iteration through different choices and getting specific buttons by indexes in the template? My trials failed, but maybe you will succeed!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-4978844507322928721?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=VL5gnTza"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=VL5gnTza" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=eIlUL9lp"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=eIlUL9lp" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/g9-Xad4Ibmg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/4978844507322928721/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/08/multiplesubmitbutton-widget-for.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/4978844507322928721?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/4978844507322928721?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/g9-Xad4Ibmg/multiplesubmitbutton-widget-for.html" title="MultipleSubmitButton Widget for ChoiceField" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/08/multiplesubmitbutton-widget-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMBQXw4eyp7ImA9WxdUFEQ.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-263994628919061999</id><published>2008-07-16T01:16:00.019+02:00</published><updated>2008-07-31T11:44:10.233+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-07-31T11:44:10.233+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Vilnius" /><category scheme="http://www.blogger.com/atom/ns#" term="EuroPython" /><category scheme="http://www.blogger.com/atom/ns#" term="Conference" /><category scheme="http://www.blogger.com/atom/ns#" term="Meetup" /><title>EuroPython 2008</title><content type="html">I started writing this post at the airport just before flying back from Vilnius to Berlin. EuroPython 2008 - the three-days conference and sprints afterwards - gave me loads of information, new relations, and better understanding of the big image of Python developers' community.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://europython.org/"&gt;&lt;img src="http://4.bp.blogspot.com/_nG3SKqiLARM/SHrDTFLntAI/AAAAAAAAANk/mf48aYxhXn8/s400/europython.png" border="0" alt="EuroPython" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Some highlights from the conference follow.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;During video conference the author of Python programming language &lt;a href="http://en.wikipedia.org/wiki/Guido_van_Rossum"&gt;Guido van Rossum&lt;/a&gt; encouraged using Python 2.6 just after the release, but said that Python 3.0 still shouldn't be used for production. The good future-proof practices are inheriting from &lt;code&gt;object&lt;/code&gt; for classes, using &lt;code&gt;xrange()&lt;/code&gt;, &lt;code&gt;sorted()&lt;/code&gt;, and &lt;code&gt;zip()&lt;/code&gt; functions, "&lt;code&gt;//&lt;/code&gt;" for floor divisions, and &lt;code&gt;dict.iterkeys()&lt;/code&gt;. The full porting to 3.0 guide will be probably written and published in the python.org site.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://clonedigger.sourceforge.net/"&gt;Clone Digger&lt;/a&gt; is a project from Google Summer of Code 2008 for finding similar pieces of code which could be optimized by moving that to parameterized functions.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Restrictive Python is a project which blocks the user from executing dangerous functions or methods or accessing files which might harm the system. You can try Restrictive Python out in the &lt;a href="http://codespeak.net:7777/"&gt;simulated terminal&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Python is an interpreted language and if you want to deploy a project writen in Python, you have to be sure that the end user will have Python interpreter installed there. O you can create an executable file from Python project using one of the following tools: &lt;a href="http://www.py2exe.org/"&gt;Py2exe&lt;/a&gt;, &lt;a href="http://pyinstaller.python-hosting.com/"&gt;PyInstaller&lt;/a&gt;, or &lt;a href="http://pypi.python.org/pypi/bbfreeze/0.95.4"&gt;BBFreeze&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There is a combined installer of Python, SQLite, and Django for Windows and it is called &lt;a href="http://www.instantdjango.com/"&gt;Instant Django&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/deseb/"&gt;Django DB External Scheme Evolution Branch&lt;/a&gt; is a separate application for doing DB scheme changes easily without manual DB field editing.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Honza Král showed the essentials of the &lt;a href="http://www.europython.org/TalkMaterials?action=AttachFile&amp;do=view&amp;target=django-nfa.pdf"&gt;new-forms admin&lt;/a&gt; to which I am going to port our Django projects the next days. Directly from him I learned that new forms will still have validators which will work only to check the value of that one specific field (not related to other fields). If some multiple field validation is necessary, the clean method for the form should be overwritten. All the global field-unrelated form errors will be passed to &lt;code&gt;{{ form.non_field_errors }}&lt;/code&gt; variable.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Christian Scholtz presented &lt;a href="http://mrtopf.de/blog/secondlife/python-and-the-open-grid-protocol-status-of-the-pyogp-project-technical/"&gt;PYOGP&lt;/a&gt; which is Python-based environment for testing the Open Grid Protocol used for exchanging avatars and other objects among virtual realms like different regions of Second Life.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ricardo Quesada and Lucio Torre told us about &lt;a href="http://europython.wordpress.com/2008/07/16/video-cocos2d-a-framework-for-building-2d-games/"&gt;cocos2d&lt;/a&gt; - a framework made in Python for presentations and sprite-based 2D games.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you ever need to export some data from a database to Excel Sheet or import from Excel, you can do that not only in CSV (Comma Separated Values) format. You can also use the original XLS format with one of the following libraries: &lt;a href="http://pypi.python.org/pypi/xlrd/0.5.2"&gt;xlrd&lt;/a&gt;, &lt;a href="https://secure.simplistix.co.uk/svn/xlwt/trunk/"&gt;xlwt&lt;/a&gt;, or &lt;a href="http://pypi.python.org/pypi/pyExcelerator/0.5.3a"&gt;pyExcelerator&lt;/a&gt;. I successfully used the latter one last week.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;CVS and SVN are not the only version control management systems in the world :D. The others like &lt;a href="http://www.selenic.com/mercurial/wiki/"&gt;Mercurial&lt;/a&gt;, &lt;a href="http://git.or.cz/"&gt;Git&lt;/a&gt; and &lt;a href="http://bazaar-vcs.org/"&gt;Bazaar&lt;/a&gt; are also worthy checking. These are all distributed systems which mean that they don't have one centralized source. Bazaar is written in Python and has API for manipulating information about revisions, committers, and other version-control-related stuff.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Some other useful links:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.djangoproject.com/wiki/SprintEuroPython2008"&gt;Django Sprint at EuroPython 2008&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.europython.org/TalkMaterials"&gt;Talk Materials&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.europython.org/OreillyDiscount"&gt;Discount for O'Reilly Books&lt;br /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Others blogged about EuroPython 2008 as well:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ville Säävuori: &lt;a href="http://www.unessa.net/en/hoyci/2008/07/europython-day-1/"&gt;Day 1&lt;/a&gt;, &lt;a href="http://www.unessa.net/en/hoyci/2008/07/europython-day-2/"&gt;Day 2&lt;/a&gt;, &lt;a href="http://www.unessa.net/en/hoyci/2008/07/europython-day-3/"&gt;Day 3&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://ollehost.dk/blog/2008/07/14/europython-2008-my-little-log/"&gt;Olle Jonsson Morningstar&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.sauria.com/blog/2008/07/11/my-first-europython/"&gt;Ted Leung&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.cheeming.com/blog/index.php/2008/07/18/europython-2008-in-vilnius"&gt;Chee Ming Chew&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-263994628919061999?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=d5tfOguC"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=d5tfOguC" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=reLkSTHx"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=reLkSTHx" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/F82E_ExDD5o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/263994628919061999/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/07/europython-2008.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/263994628919061999?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/263994628919061999?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/F82E_ExDD5o/europython-2008.html" title="EuroPython 2008" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_nG3SKqiLARM/SHrDTFLntAI/AAAAAAAAANk/mf48aYxhXn8/s72-c/europython.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/07/europython-2008.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IERH4_eCp7ImA9WxdRFE4.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-5734075177808803650</id><published>2008-06-02T19:28:00.012+02:00</published><updated>2008-06-02T22:45:05.040+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-06-02T22:45:05.040+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Meetup" /><category scheme="http://www.blogger.com/atom/ns#" term="Berlin" /><title>Django Meetup Postmortem</title><content type="html">Thanks to the organizer &lt;a href="http://jannisleidel.com/"&gt;Jannis&lt;/a&gt;, last Friday German djangoers met successfully, got some beer at &lt;a href="http://www.schleusenkrug.de/"&gt;Schleusenkrug&lt;/a&gt; in Tiergarten, Berlin, and became better aquainted with each other. We discussed the latest actualities like licenses, project deployment, business models, the future of Django, who from the gods of Django said what in which blog, and other geeky stuff.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_nG3SKqiLARM/SERX6VYQ7zI/AAAAAAAAAME/gr5mIJBsh0A/s1600-h/left.jpg"&gt;&lt;img src="http://4.bp.blogspot.com/_nG3SKqiLARM/SERX6VYQ7zI/AAAAAAAAAME/gr5mIJBsh0A/s320/left.jpg" border="0" alt="Some djangoers on the left side of the table" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_nG3SKqiLARM/SERYOfChfXI/AAAAAAAAAMM/YkxhTXGjHGQ/s1600-h/right.jpg"&gt;&lt;img src="http://1.bp.blogspot.com/_nG3SKqiLARM/SERYOfChfXI/AAAAAAAAAMM/YkxhTXGjHGQ/s320/right.jpg" border="0" alt="Some djangoers on the right side of the table" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you want to get in touch with German djangoers, you can join the &lt;code&gt;#django-de&lt;/code&gt; IRC channel on &lt;code&gt;irc.freenode.net&lt;/code&gt;. Also you can subscribe to their blogs at &lt;a href="http://www.django-de.org/community/"&gt;django-de.org&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-5734075177808803650?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=u3PNFRev"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=u3PNFRev" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=4S7hOai8"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=4S7hOai8" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/so0wwpj3fqQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/5734075177808803650/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/06/django-meetup-postmortem.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5734075177808803650?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/5734075177808803650?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/so0wwpj3fqQ/django-meetup-postmortem.html" title="Django Meetup Postmortem" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_nG3SKqiLARM/SERX6VYQ7zI/AAAAAAAAAME/gr5mIJBsh0A/s72-c/left.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/06/django-meetup-postmortem.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0MMQH46eip7ImA9WxdSGUw.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-1038541416139231025</id><published>2008-05-27T19:34:00.007+02:00</published><updated>2008-05-27T20:04:41.012+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-27T20:04:41.012+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Meetup" /><category scheme="http://www.blogger.com/atom/ns#" term="Berlin" /><title>Django Meetup at Linuxtag</title><content type="html">Pssst. Don't tell anybody! There is an unofficial &lt;a href="http://jannisleidel.com/2008/05/django-user-meetup-in-berlin-at-linuxtag-2008/"&gt;Django user meetup&lt;/a&gt; happening in Berlin this Friday at five. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;datetime.&lt;span class="syntax6"&gt;datetime&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax5"&gt;2008&lt;/span&gt;, &lt;span class="syntax5"&gt;5&lt;/span&gt;, &lt;span class="syntax5"&gt;27&lt;/span&gt;, &lt;span class="syntax5"&gt;17&lt;/span&gt;, &lt;span class="syntax5"&gt;0&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The gathering point is at the &lt;a href="http://www.linuxtag.org/2008/en/besucher/anreise.html"&gt;main entrance of Linuxtag conference&lt;/a&gt; (Messe Berlin).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://flickr.com/photos/67855182@N00/1124152390/"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px;" src="http://farm2.static.flickr.com/1295/1124152390_8af3273de4.jpg?v=0" border="0" alt="" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-1038541416139231025?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=whknbH43"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=whknbH43" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=wz7BAH2L"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=wz7BAH2L" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/nBK0zoWZ_Ac" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/1038541416139231025/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/05/django-meetup-at-linuxtag.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/1038541416139231025?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/1038541416139231025?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/nBK0zoWZ_Ac/django-meetup-at-linuxtag.html" title="Django Meetup at Linuxtag" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/05/django-meetup-at-linuxtag.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkQBQ3kzcSp7ImA9WxdaE0k.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-460345051571027815</id><published>2008-05-23T23:07:00.015+02:00</published><updated>2008-08-21T20:45:52.789+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-08-21T20:45:52.789+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><category scheme="http://www.blogger.com/atom/ns#" term="Guerrilla Patch" /><title>Decorating the Render Methods of New-Form Widgets</title><content type="html">Perhaps all template masters have already faced the problem of styling HTML input fields of different types. The selectors like &lt;code&gt;input[type=checkbox]&lt;/code&gt; and similar in CSS are not supported by IE so people working with templates and CSS obviously need some other way to select and style specific types of input fields.&lt;br /&gt;&lt;br /&gt;There are a few ugly ways to do that which violate the &lt;acronym title="Don't Repeat Yourself"&gt;DRY&lt;/acronym&gt; principle:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Encompassing the form element in the template with HTML tag which has a class representing specific type of the input field.&lt;/strong&gt;&lt;br /&gt;For example:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;span&lt;/span&gt;&lt;span class="syntax17"&gt; &lt;/span&gt;&lt;span class="syntax17"&gt;class&lt;/span&gt;&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;form_checkbox&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    {{ form.is_privacy_policy_confirmed }}&lt;br /&gt;&lt;span class="syntax17"&gt;&amp;lt;&lt;/span&gt;&lt;span class="syntax17"&gt;/&lt;/span&gt;&lt;span class="syntax17"&gt;span&lt;/span&gt;&lt;span class="syntax17"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Defining the specific CSS class for each form field widget in the form.&lt;/strong&gt;&lt;br /&gt;For example:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;class&lt;/span&gt; &lt;span class="syntax6"&gt;FormExample&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;forms.Form&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    is_privacy_policy_confirmed &lt;span class="syntax18"&gt;=&lt;/span&gt; forms.&lt;span class="syntax6"&gt;BooleanField&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;        required&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax10"&gt;True&lt;/span&gt;,&lt;br /&gt;        widget&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax6"&gt;CheckboxInput&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;attrs&lt;span class="syntax18"&gt;=&lt;/span&gt;{&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;class&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt; &lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;form_checkbox&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;}&lt;span class="syntax18"&gt;)&lt;/span&gt;,&lt;br /&gt;        &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Extending all Fields and all Widgets which use HTML input fields and using the extended versions instead of the originals.&lt;/strong&gt;&lt;br /&gt;&lt;/li&gt; &lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I don't like any of them, because they force me or the template formatters to repeat ourselves and make plenty of replacements in our existing forms.&lt;br /&gt;&lt;br /&gt;Although &lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/40dde7a2de7f2d8d/5d62405623d6502b?#5d62405623d6502b"&gt;&amp;quot;most sane developers consider it a bad idea&amp;quot;&lt;/a&gt;, I see the &lt;strong&gt;Guerrilla patching&lt;/strong&gt; of the Widget's render method as the nicest solution to solve this problem.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Monkey_patch"&gt;Guerrilla patch&lt;/a&gt; is the modification of the runtime code in dynamic languages without changing the original source code.&lt;br /&gt;&lt;br /&gt;The method &lt;code&gt;render&lt;/code&gt; of the &lt;code&gt;Widget&lt;/code&gt; class draws the input field in HTML. As it takes a parameter &lt;code&gt;attrs&lt;/code&gt; for additional input field attributes, my idea was to create a decorator which modifies the incoming parameters and adds a CSS class &lt;code&gt;"form_TYPE"&lt;/code&gt;, where &lt;code&gt;TYPE&lt;/code&gt; is the input field type.&lt;br /&gt;&lt;br /&gt;With a little help by &lt;a href="http://blog.sandbox.lt/en/"&gt;Dalius Dobravolskas&lt;/a&gt;, I succeeded to code a &lt;strong&gt;decorator&lt;/strong&gt; having an optional parameter which defines the CSS class name for the field. If the class name is not defined, the attribute &lt;code&gt;input_type&lt;/code&gt; of the Widget class is used for forming the CSS class name (N.B. not all widgets have this attribute).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.newforms.widgets &lt;span class="syntax8"&gt;import&lt;/span&gt; Input, CheckboxInput, RadioSelect, CheckboxSelectMultiple&lt;br /&gt;&lt;br /&gt;&lt;span class="syntax1"&gt;#&lt;/span&gt;&lt;span class="syntax1"&gt;##&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;adding&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;class=&amp;quot;form_*&amp;quot;&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;to&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;all&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;html&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;input&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;fields&lt;/span&gt;&lt;span class="syntax1"&gt; &lt;/span&gt;&lt;span class="syntax1"&gt;###&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;add_css_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;css_class&lt;span class="syntax18"&gt;=&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;modify_input_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;function&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;        _css_class &lt;span class="syntax18"&gt;=&lt;/span&gt; css_class&lt;br /&gt;        &lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;new_function&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;            arg_names &lt;span class="syntax18"&gt;=&lt;/span&gt; function.func_code.co_varnames&lt;br /&gt;            new_kwargs &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;dict&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax9"&gt;zip&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;arg_names, args&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            new_kwargs.&lt;span class="syntax6"&gt;update&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            css_class &lt;span class="syntax18"&gt;=&lt;/span&gt; _css_class &lt;span class="syntax8"&gt;or&lt;/span&gt; &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;form_&lt;/span&gt;&lt;span class="syntax16"&gt;%s&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax18"&gt;%&lt;/span&gt; &lt;span class="syntax9"&gt;getattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;br /&gt;                new_kwargs[&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;self&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;],&lt;br /&gt;                &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;input_type&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;,&lt;br /&gt;                &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;undefined&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;,&lt;br /&gt;                &lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;self&lt;/span&gt; &lt;span class="syntax18"&gt;=&lt;/span&gt; new_kwargs.&lt;span class="syntax6"&gt;pop&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;self&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            attrs &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;getattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;attrs&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax10"&gt;None&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt; &lt;span class="syntax8"&gt;or&lt;/span&gt; {}&lt;br /&gt;            &lt;span class="syntax8"&gt;if&lt;/span&gt; &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;class&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax8"&gt;in&lt;/span&gt; attrs&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                css_classes &lt;span class="syntax18"&gt;=&lt;/span&gt; attrs[&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;class&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;].&lt;span class="syntax6"&gt;split&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span class="syntax8"&gt;if&lt;/span&gt; css_class &lt;span class="syntax8"&gt;not&lt;/span&gt; &lt;span class="syntax8"&gt;in&lt;/span&gt; css_classes&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                    css_classes.&lt;span class="syntax6"&gt;append&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;css_class&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;                attrs[&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;class&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;] &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;.&lt;span class="syntax6"&gt;join&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;css_classes&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="syntax8"&gt;else&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;                attrs[&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;class&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;] &lt;span class="syntax18"&gt;=&lt;/span&gt; css_class&lt;br /&gt;            &lt;span class="syntax8"&gt;self&lt;/span&gt;.attrs &lt;span class="syntax18"&gt;=&lt;/span&gt; attrs&lt;br /&gt;            &lt;span class="syntax8"&gt;return&lt;/span&gt; &lt;span class="syntax6"&gt;function&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax8"&gt;self&lt;/span&gt;, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;new_kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="syntax8"&gt;return&lt;/span&gt; new_function&lt;br /&gt;    &lt;span class="syntax8"&gt;return&lt;/span&gt; modify_input_class&lt;br /&gt;Input.render &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;add_css_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;Input.render&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;CheckboxInput.render &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;add_css_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;form_checkbox&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;CheckboxInput.render&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;RadioSelect.render &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;add_css_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;form_radio&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;RadioSelect.render&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;CheckboxSelectMultiple.render &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;add_css_class&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;form_checkbox&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;CheckboxSelectMultiple.render&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To use this code, just place it in some &lt;code&gt;models.py&lt;/code&gt; file in your project.&lt;br /&gt;&lt;br /&gt;The &lt;strong&gt;strange part&lt;/strong&gt; here was that the variable &lt;code&gt;css_class&lt;/code&gt; isn't recognized by the sub-child function &lt;code&gt;new_function&lt;/code&gt; directly although the scope of the variable &lt;code&gt;css_class&lt;/code&gt; should let it be accessed there. Anyway, the value got easily accessible when I reassigned it to another variable like &lt;code&gt;_css_class&lt;/code&gt; in the child function &lt;code&gt;modify_input_class&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;del&gt;The &lt;strong&gt;tricky part&lt;/strong&gt; of this snippet was getting the &lt;code&gt;attrs&lt;/code&gt; argument from the decorated function as it was not clear whether it would be passed as a positional or as a named argument. The first three lines of the function &lt;code&gt;new_function&lt;/code&gt; collects all the incoming arguments to a dictionary &lt;code&gt;new_kwargs&lt;/code&gt;. They can be modified and then passed to the original function to decorate.&lt;/del&gt;&lt;br /&gt;&lt;br /&gt;Ups. I am late to the studio. So see you next time!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-460345051571027815?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=KXocWMNg"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=KXocWMNg" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=SvO5pBVH"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=SvO5pBVH" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/UEm2DXqqW3Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/460345051571027815/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/05/decorating-render-methods-of-new-form.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/460345051571027815?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/460345051571027815?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/UEm2DXqqW3Q/decorating-render-methods-of-new-form.html" title="Decorating the Render Methods of New-Form Widgets" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/05/decorating-render-methods-of-new-form.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8BRXY8fCp7ImA9WxdSF0s.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-1660906358687899457</id><published>2008-04-08T20:37:00.008+02:00</published><updated>2008-05-26T02:47:34.874+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-26T02:47:34.874+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="Basics" /><title>Tips #2</title><content type="html">What time is it now? It's time to give you some more tips about Django development!&lt;br /&gt;&lt;br /&gt;&lt;strong id="tips_2.1"&gt;Use batch scripts to automate manual routines.&lt;/strong&gt; Do not repeat yourself extracting and compiling translatable strings, starting and stopping development web-servers, updating and committing your project to the version-control system in the console. Write batch scripts which you can run within one mouse click instead.&lt;br /&gt;&lt;br /&gt;&lt;strong id="tips_2.2"&gt;Define overwritable constants in your applications.&lt;/strong&gt; Your applications are likely using some values that might be defined as constant values, i.e. the dimensions for avatars of users. Define those constants so, that you could overwrite them in the project settings if necessary. &lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.conf &lt;span class="syntax8"&gt;import&lt;/span&gt; settings&lt;br /&gt;SOME_SETTING &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax9"&gt;getattr&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;settings, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;SOME_SETTING&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;, &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;default&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;value&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong id="tips_2.3"&gt;Have one view for site-related JavaScript globals.&lt;/strong&gt; Django views usually return (X)HTML-based responses, but it can return XML, JavaScript or others as well. Usually you will hold all you JavaScript functions in static files, but there might be some situation, where you need to get information related to database or project settings, for example, the MEDIA_URL.&lt;br /&gt;The following view might be used to display a javascript page directly from a template. Just pass the &lt;code&gt;template&lt;/code&gt; to the view in your urls.py file.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; datetime &lt;span class="syntax8"&gt;import&lt;/span&gt; datetime, timedelta&lt;br /&gt;&lt;span class="syntax8"&gt;from&lt;/span&gt; django.views.generic.simple &lt;span class="syntax8"&gt;import&lt;/span&gt; direct_to_template&lt;br /&gt;&lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;direct_to_js_template&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;request, &lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    response &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;direct_to_template&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;request, &lt;span class="syntax18"&gt;*&lt;/span&gt;args, &lt;span class="syntax18"&gt;*&lt;/span&gt;&lt;span class="syntax18"&gt;*&lt;/span&gt;kwargs&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    response[&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;Content&lt;/span&gt;&lt;span class="syntax13"&gt;-&lt;/span&gt;&lt;span class="syntax13"&gt;Type&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;] &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;application&lt;/span&gt;&lt;span class="syntax13"&gt;/&lt;/span&gt;&lt;span class="syntax13"&gt;x&lt;/span&gt;&lt;span class="syntax13"&gt;-&lt;/span&gt;&lt;span class="syntax13"&gt;javascript&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;    now &lt;span class="syntax18"&gt;=&lt;/span&gt; datetime.&lt;span class="syntax6"&gt;utcnow&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    response[&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;Last&lt;/span&gt;&lt;span class="syntax13"&gt;-&lt;/span&gt;&lt;span class="syntax13"&gt;Modified&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;] &lt;span class="syntax18"&gt;=&lt;/span&gt; now.&lt;span class="syntax6"&gt;strftime&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;a&lt;/span&gt;&lt;span class="syntax13"&gt;,&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax16"&gt;%d&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;b&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;Y&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;H&lt;/span&gt;&lt;span class="syntax13"&gt;:&lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;M&lt;/span&gt;&lt;span class="syntax13"&gt;:&lt;/span&gt;&lt;span class="syntax16"&gt;%S&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;GMT&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    expires &lt;span class="syntax18"&gt;=&lt;/span&gt; now &lt;span class="syntax18"&gt;+&lt;/span&gt; &lt;span class="syntax6"&gt;timedelta&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax5"&gt;0&lt;/span&gt;, &lt;span class="syntax5"&gt;2678400&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    response[&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;Expires&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;] &lt;span class="syntax18"&gt;=&lt;/span&gt; expires.&lt;span class="syntax6"&gt;strftime&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;a&lt;/span&gt;&lt;span class="syntax13"&gt;,&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax16"&gt;%d&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;b&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;Y&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;H&lt;/span&gt;&lt;span class="syntax13"&gt;:&lt;/span&gt;&lt;span class="syntax13"&gt;%&lt;/span&gt;&lt;span class="syntax13"&gt;M&lt;/span&gt;&lt;span class="syntax13"&gt;:&lt;/span&gt;&lt;span class="syntax16"&gt;%S&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;GMT&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;return&lt;/span&gt; response&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;And now it's time to go home and to get relaxed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-1660906358687899457?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=QXLkVGi3"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=QXLkVGi3" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=Hjzg089T"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=Hjzg089T" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/g05s93wiHv4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/1660906358687899457/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/04/tips-2.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/1660906358687899457?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/1660906358687899457?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/g05s93wiHv4/tips-2.html" title="Tips #2" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/04/tips-2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEECRHo_fyp7ImA9WxdSF0s.&quot;"><id>tag:blogger.com,1999:blog-9014457088394059918.post-3087673719979239344</id><published>2008-03-24T09:34:00.007+01:00</published><updated>2008-05-26T02:44:25.447+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-26T02:44:25.447+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Advanced" /><title>DRY While Working With Choices for Forms</title><content type="html">When creating dozens of forms with selection fields for some many-to-one or many-to-many  relations, you might find that it's ineffective to create choices for the form fields from querysets formed by the relations defined by ForeignKeys and ManyToManyFields. You have to import the related models, filter the choices analogously to the limit_choices_to parameter, and form a list of tuples again and again.&lt;br /&gt;&lt;br /&gt;To get the same choices from the model as in the admin form, you can use the following:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;FIELD_CHOICES &lt;span class="syntax18"&gt;=&lt;/span&gt; SomeModel._meta.&lt;span class="syntax6"&gt;get_field&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;field_name&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;.&lt;span class="syntax6"&gt;get_choices&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then you can modify the text for the null-value choice, like&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;FIELD_CHOICES[&lt;span class="syntax5"&gt;0&lt;/span&gt;] &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt; &lt;span class="syntax6"&gt;_&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax13"&gt;-&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;Choose&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;One&lt;/span&gt;&lt;span class="syntax13"&gt; &lt;/span&gt;&lt;span class="syntax13"&gt;-&lt;/span&gt;&lt;span class="syntax13"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;or even remove it:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;del&lt;/span&gt; FIELD_CHOICES[&lt;span class="syntax5"&gt;0&lt;/span&gt;]&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;To save the selected object you can simply assign the chosen value to the foreign key, like:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;new_instance &lt;span class="syntax18"&gt;=&lt;/span&gt; &lt;span class="syntax6"&gt;SomeModel&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;new_instance.field_name_id &lt;span class="syntax18"&gt;=&lt;/span&gt; form.cleaned_data[&lt;span class="syntax13"&gt;'&lt;/span&gt;&lt;span class="syntax13"&gt;field_name&lt;/span&gt;&lt;span class="syntax13"&gt;'&lt;/span&gt;]&lt;br /&gt;new_instance.&lt;span class="syntax6"&gt;save&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;If you need to do something with the selected object, you can still live without importing specific models and filtering the entries in the same manner as limit_choices_to. To save time, you can use the following function, which returns the queryset containing all the choosable objects:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;span class="syntax0"&gt;&lt;span class="syntax8"&gt;def&lt;/span&gt; &lt;span class="syntax6"&gt;get_related_queryset&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;model, field_name&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;span class="syntax8"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax14"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;Get&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;the&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;queryset&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;for&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;the&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;choices&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;of&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;the&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;field&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;in&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;a&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;model&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;Example&lt;/span&gt;&lt;span class="syntax14"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;objects&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;=&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;get_related_queryset&lt;/span&gt;&lt;span class="syntax14"&gt;(&lt;/span&gt;&lt;span class="syntax14"&gt;SomeModel&lt;/span&gt;&lt;span class="syntax14"&gt;,&lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax14"&gt;field_name&lt;/span&gt;&lt;span class="syntax14"&gt;&amp;quot;&lt;/span&gt;&lt;span class="syntax14"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt; &lt;/span&gt;&lt;span class="syntax14"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    f &lt;span class="syntax18"&gt;=&lt;/span&gt; model._meta.&lt;span class="syntax6"&gt;get_field&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;field_name&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    qs &lt;span class="syntax18"&gt;=&lt;/span&gt; f.rel.to._default_manager.&lt;span class="syntax6"&gt;complex_filter&lt;/span&gt;&lt;span class="syntax18"&gt;(&lt;/span&gt;f.rel.limit_choices_to&lt;span class="syntax18"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="syntax8"&gt;return&lt;/span&gt; qs&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Just put this function in one of you applications and import it whenever you need to work with forms. And have happy Easter!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9014457088394059918-3087673719979239344?l=djangotricks.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=1p0Tdp5u"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=1p0Tdp5u" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/djangotricks?a=gnR84IP2"&gt;&lt;img src="http://feeds.feedburner.com/~f/djangotricks?i=gnR84IP2" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/djangotricks/~4/4oqknxpn0l0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://djangotricks.blogspot.com/feeds/3087673719979239344/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://djangotricks.blogspot.com/2008/03/dry-while-working-with-choices-for.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3087673719979239344?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9014457088394059918/posts/default/3087673719979239344?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/djangotricks/~3/4oqknxpn0l0/dry-while-working-with-choices-for.html" title="DRY While Working With Choices for Forms" /><author><name>Archatas</name><uri>http://www.blogger.com/profile/18444891139539061590</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://bp0.blogger.com/_nG3SKqiLARM/R8o1eqfKtrI/AAAAAAAAAJY/K2WfbkItxGw/S220/avatar.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://djangotricks.blogspot.com/2008/03/dry-while-working-with-choices-for.html</feedburner:origLink></entry></feed>

