Three weeks ago we had a discussion about Ajax support in Django, which resulted in "Ajax and Django"
wiki page. A short recap: it lists a vague goal, some general considerations, and possible strategies; it scratches the surface of existing implementations (mostly RoR), existing third-party toolkits (Mochikit/Dojo), and related RPC-style and REST-style services. No code was produced, no consensus was reached, but now it is a part of Django’s Version One roadmap
.
It is practically impossible to travel to Muslim countries without hearing some anecdotes about Nasreddin Hodja
. Here is one (the source
):
Hodja was once a judge. One day a man came to his house to complain about his neighbor. Hodja listened carefully and then said to him,
— My good man, you are right.
The man went away happily. In a little while the first man’s neighbor came to see Hodja. He complained about the first man. Hodja listened carefully to him, too, and then said,
I found a blog post
describing London Web Frameworks Night
, which took place on 11/17/2005 at the University of Westminster. Jabbering Giraffe
reports on presentation of three web frameworks: Catalyst
, Django
, And Ruby on Rails
.
It is an interesting read. Author remarks that Django’s Admin ("with lots of Ajax and JavaScript goodness") was the main object of envy of other frameworks. Another interesting remark was how good Rails people are at marketing.
As you all know Django has new shiny RSS framework
. This change breaks my simple RSS tutorial
and I am glad that it happened! To tell you the whole truth, it breaks only "The Simple Way" to do RSS in Django replacing it with even simpler one. "The Smart Way" RSS still works as you can see on my web site. Like I predicted the feedgenerator is still around and it learned some new tricks: it can produce Atom feed now! I am going to update the tutorial recreating old examples with new RSS framework. It will be fun!
Recently I looked at the stats of my web site. DreamHost
provides Analog 6.0
. I supplemented it with awstats
. Plus there are some other means to analyze the traffic. Let’s put it this way: I know my average reader. I thought I did. Anyway I found a few surprises.
The country list includes 77 countries. Out of 193. Not bad for a personal blog. Practically all Eurasian countries, and countries of both Americas are in the list. Australia, New Zealand, and many island countries are there as well. Only 2 countries from Africa: South Africa and Senegal. Pity.
As of today i18n branch of Django is merged back to trunk. What is i18n? It is an abbreviation of the big word "internationalization". l10n ("localization") is a sibling of i18n. In practice it means that now you and I can do truly international multi-language web sites without much hassle. While this is more important for big corporations and international organizations, it is a big step for Django’s truly international community. Let’s thank Hugo
(the engine behind this effort), all participating developers, and all translators for their monumental effort to make it a reality.
I’ve migrated my sites to new Django admin. It is easy to do but make sure that you followed instructions closely. Small mistakes can cost you a lot of head-scratching time. You can find the instructions at the bottom of Backwards-incompatible changes
.
New refactoring has totally made sense. Meanwhile Robert Wittams & Co is working on new-admin (note the hyphen). Django’s admin is a very nice app (now it is an app!). I hope I could reuse some pieces of upcoming new-admin in my apps. If I remember correctly it was the main driving force behind branching.
In his blog post Ian Maurer shares a very slick way to run unit tests with SQLite’s in-memory database. Check it out: http://itmaurer.com/blog/?p=2
Simple, elegant, practical.
I am running new Django cache code now. If you notice any problems with it (e.g., error messages), please drop me a line at once.
My email is at the bottom of the page.
For a long time I wanted to have two TinyMCE toolbars for Django Admin:
- One-line toolbar with the most essential tools, which gives a lot of space for inline editing of text.
- Full-featured toolbar with all tools for full-screen mode.
It finally happened. Spocke (the main developer of TinyMCE) helped me to figure out how to do it. I updated my article in Django Wiki
with new config file, and removed my notes about space problem (new config file solves it) and flickering (new TinyMCE 2.0 RC2 works perfectly).
Update: "The Simple Way" part of this tutorial is obsolete
now. I am going to recreate examples using new improved RSS framework. Stay tuned!
I was asked several times to explain how I did RSS for my site. Django has RSS framework, which is not documented. Most probably I am not the right guy to explain it but I’ll try.
There are three ways to implement RSS with Django:
- The Simple: using Django’s RSS framework.
- The Smart: using django.utils.feedgenerator.
- The Hard: write a view and output XML manually or using standard xml.sax.saxutils.
If you want Django to do everything for you, then you should use "The Simple Way". If you want some custom object selection, you should use "The Smart Way". For obsessed workaholics, S&M adepts, and guys-with-really-convoluted-needs the only way is "The Hard Way". Being lazy I prefer #1 and #2. If you want #3, I suggest you to study files mentioned in "The Simple Way" subsection below.
The Simple Way
If you look into Django’s code, you will find several files related to RSS:
After some requests I decided to publish my code for categories. It’s very simple. It was inspired by following articles: A "category" Data Model
(note: this article uses old-style model format, it doesn’t work anymore) and Relating an object to itself, many-to-one
.
from django.core import meta
class Category(meta.Model):
"""
Category defines following fields:
name - simple name of category, e.g., 'C++'
full_name - full name of category, which includes names of all parents,
e.g. 'Development::C++'
parent - reference to parent category or null
description - HTML description of category, or null
Notes:
1) full_name is not editable. It is calculated automatically
by calculate_full_name() method during save phase (hook _pre_save).
2) Separator specified by get_separator() method. It can be
overridden in subclasses.
"""
name = meta.CharField(maxlength=50)
full_name = meta.CharField(maxlength=250, unique=True,
editable=False)
parent = meta.ForeignKey('self', blank=True, null=True,
related_name='child')
description = meta.TextField(blank=True, null=True,
help_text="Use raw HTML.")
class META:
verbose_name_plural = 'Categories'
module_name = verbose_name_plural.lower()
admin = meta.Admin(
list_display = ('full_name',),
search_fields = ['full_name',],
js = (
'/tiny_mce/tiny_mce.js',
'/appmedia/admin/js/textareas.js',
),
)
ordering = ('full_name',)
def __repr__(self):
return self.full_name
def _pre_save(self):
self.full_name = self.calculate_full_name()
def get_separator(self):
return '::'
# note: used in templates
def get_all_children(self):
"get list of all children of the category"
output = []
children = self.get_child_list()
for c in children:
output.append(c)
output.extend(c.get_all_children())
return output
# note: the last object of the list is the category itself
# note: used in templates
def get_all_parents(self):
"get list of all parents of the category from top level parent down"
id_list = []
object = self
while True:
id_list.append(object)
if not object.parent_id:
break
object = object.get_parent()
id_list.reverse()
return id_list
# note: used in templates
def calculate_full_name(self):
"calculate full name from parent list"
return self.get_separator().join([x.name for x in self.get_all_parents()])
# note: used in templates
def get_path(self):
"get relative path of the category"
return 'categories/%d/' % self.id
As you can see it is very simple. This model defines full_name field, which is auto-populated. I was toying with an idea to keep parent_name and produce full_name by concatenating parent_name with name. But after some trials I decided against it: it introduced a lot of complexities and runtime overhead by saving a few bytes in database. In my opinion it doesn’t worth it. This model defines some convenience methods as well. They are meant to be used in templates.
Finally we have official Django badges
. Wilson Miner did a great job putting them together. I decided to use this one:
How do you like it?
If you are a fan of 80 by 15 pixels badges, don’t fret — Dave Hodder has you covered with his rendition of "Made with Django" theme. Collection of his small badges are attached to ticket #509
. Check it out too.
Finally I added RSS 2.0 using Django’s facilities.
Default RSS feed is for my blog. Categories have two versions of RSS feed: exclusively for documents of the category, and documents for the category and its children. (I hope it is not news anymore: I have hierarchical categories.)
Now this blog runs on sql:. I use MySQL as my backend. Before that I tested it locally with SQLite
(so easy to install!) and MySQL.
I learned today that MySQL, which is provided by my host (DreamHost
), uses MyISAM for all tables. MyISAM doesn’t support transactions. AT home I use InnoDB, which supports transactions. However the hope is not lost — DreamHost mulls over InnoDB support. But as far as I know, they don’t have an implementation schedule yet.
This site was running using locmem: caching option, which is fine for small web site. Now it runs using file:. Please let me know, if you spot any issues.
I am planning to implement sql: next.
I am not talking about multithreading problems. locmem: doesn’t have them. But it does have problems with gzip. Sometimes I see binary stuff on my screen. It looks like a gzipped web page without proper gzip encoding flag. I don’t do anything related to compression in my cache.
Is it a bug in Django’s caching? Hmm… Apparently it works properly with memcache. Can it be some bug in simple: implementation, which was a base for locmem:? Weird.
Being a little bit frustrated by the lack of caching options for my web site I’ve added locmem: cache to Django. It is fully multithreaded local memory-based cache, which uses reader-writer lock to do its magic. It seems to work well but I need some time to test it more.
I think file: is going to be next.