Posts Tagged ‘django’

Developing Reusable Django Apps: App Settings

Tuesday, January 26th, 2010

Eventually your app will need some sort of configuration. Supplying many parameters to customise your views, template tags and filters to allow template authors easily harness the power of your app and implementing registration pattern are all sensible things to do. But at some point you will need configuration for your app at project level. Because app level configuration is stupidnot reusable. Consumers of your app should never have to change its source code. And what better place to put our configuration statements than settings.py? Remember; we want to make things easier for our app’s consumers, not harder. There is no need to add a new file to the project for a few lines of settings1.

We already know we shouldn’t import settings.py directly. Instead we import the settings object (much like a singleton) of django.conf module:

from django.conf import settings

Now you can access different configuration options as attributes on this object. But I suggest you to use getattr() in order to avoid getting AttributeErrors. Also, notice how we didn’t hardcode the name of the attribute in the second method below:

# This is too verbose
try:
    some_setting = settings.SOME_SETTING
except AttributeError:
    some_setting = DEFAULT_VALUE

# Plain and simple
some_setting = getattr(settings, 'SOME_SETTING', DEFAULT_VALUE)

Instead of requiring consumers to define all your app settings it is better to supply sensible defaults. Also I find it useful to prefix names of app settings within settings.py.

# in settings.py
MYAPP_FOO_CHOICES = [('bar', u'Bar'), ('baz', u'Baz')]


# in myapp/models.py
from django.db import models
from django.conf import settings


FOO_CHOICES = getattr(settings, 'MYAPP_FOO_CHOICES', [('quux', u'Quux')])


class FooRecord(models.Model):
    foo = models.CharField(max_length=10, choices=FOO_CHOICES)

This works fine for simple apps with fewer settings. But it can easily get out of hand when your app grows. An app_settings.py module would help keeping track of configuration by keeping all configuration options in one place:

# in myapp/app_settings.py
from django.conf import settings


FOO_CHOICES = getattr(settings, 'MYAPP_FOO_CHOICES', [('quux', u'Quux')])


# in myapp/models.py
from django.db import models
from app_settings import FOO_CHOICES


class FooRecord(models.Model):
    foo = models.CharField(max_length=10, choices=FOO_CHOICES)

To summarize the points above:

  • Import settings from django.conf
  • Use getattr()
  • Always supply a default value.
  • Prefix settings you made up in settings.py
  • Use app_settings.py if you have many

1: If your configuration is long, say more than 100 lines, you should step back and reconsider. Perhaps you should prefer a strategy similar to django.contrib.sitemaps or django.contrib.syndication.

Bookmark and Share

Developing Reusable Django Apps

Wednesday, January 13th, 2010

Django app structure is an implementation of seperation of concerns. It is slightly different than what you can find in other MVC frameworks. The stack is split vertically, not horizontally. And then the app is split horizontally within, i.e. models, views, templates etc are in their seperate modules/packages/directories. This vertical splitting allows you to collect all ingredients of one functionality in your project in one place.

Framework Structure

I think apps1 are one of the strong points of Django. A selling point if you like. There is a great ecosystem of apps, you can find an app for almost anything posssible with Python. And Python is kick-ass when it comes to library wealth. But there is another major advantage of apps when they’re done right; a sane code base. Here is a slide from the Django in the Real World presentation by Jacob Kaplan-Moss:

The fivefold path

  • Do one thing, and do it well.
  • Don’t be afraid of multiple apps.
  • Write for flexibility.
  • Build to distribute.
  • Extend carefully.

I will focus on flexibility and interoperability of apps in this post. But before we proceed I would like to emphasize the first bullet point in the slide. Because the scope of your app plays a big role in its flexibility and interoperability. Apps should be small enough to easily understand and integrate (into a project). Many times I have moved away from an otherwise good app because of its many dependencies and/or excessive features. On the other hand apps should be big enough to allow for different configurations and allow extension without modifying their code. Do one thing, and do it well.

Scope of an app

Take django-tagging for example; it’s 1.3 KLOC but it does tagging and nothing else. There are no dependencies other than Django, you can add tags to any model without modifying the model source, a tag can be associated with any type of model and tagging hides the gory details from you… In short; finding the right size is important. This is why tagging is the tagging app for Django.

Building For Reuse

General advice is “even project specific apps should be reusable“. Slapping the same app onto another project is not the only advantage. In fact it may not be possible if you are not in the habit of upgrading your whole project to recent versions of Django. The main advantage as I have said before is sanity. I prefer Django to other web frameworks/environments because it provides a civilized way of development. Let’s accept it; web programming is not a particularly interesting, exciting or intellectually rewarding field. You write the same piece of code over and over. And worst of all the challanges you face are actually a result of either the underlying system was designed by morons or you are trying to use it for something it’s not intended to be used. So it is only natural that web programmers feel they’re rusting. Django eases the pain. If you stick to certain conventions serenity will follow as well.

Naturally the framework does most of the work regarding app flexibility and interoperability. Take URLs for instance include('myapp.urls') and you are good to go. You don’t have to bind views one by one. Is it inflexible? Who said urls.py can only contain a hardcoded list of URLs. You can do anything that is possible with Python. You can generate different urlpatterns based on a setting for instance.

It is relatively easy and straightforward to reuse and extend forms and views (both function based and class based). Models are a little harder to get right though. You should always think of the most difficult situation which is you can’t touch either app’s code. Registration pattern of admin app provides a good solution here. You can register a third party model to another third party app in just a few lines.

You don’t need to write lots of code to get the flexibility and interoperability. Well designed apps make good use of settings.py for example. Why should the project developer wrap a view when a single line assignment would do the job? Supplying good templatetags and template snipplets (includes) is another way to make things easy for app consumers.

Signals provide a great way to propagate the events generated from your app. Even though they are one way2, signals are extremely powerful. Any number of observers can connect to a signal and you can send a signal anywhere in your code. Literally. It is even possible your app suppying a signal and then another app sending it3.

There are many more ways to tame your app to be reusable. It all starts with your determination and discipline. Just like documentation, testing and maintaining a software generally. I will write more about reusable apps.


1: The word application is used both for a web application and a Django application. To avoid confusion I always use app to indicate the latter.

2: Signals don’t have return values. But you can use a callback AFAIK.

3: I can’t think of an example this would be useful, but still…

Bookmark and Share

Dynamic Translation Apps for Django

Wednesday, January 6th, 2010

When I needed multi-language flatpages and flatblocks for telvee I searched for available Django apps that do dynamic translation. By dynamic translation, I mean translations are entered and stored in the database. As I said I needed to be able to translate both full pages and chunks of text that I can include into another page. I ended up rolling my own, which is without a doubt lesser to some apps below. I will try to imrove it with the good ideas from existing projects and then open source. Meanwhile I would like to share my review of 6 dynamic translation apps with you. I hope someone out there finds it useful.

Django-multilingual

One of the few active projects I have reviewed is django-multilingual. To enable a model for translations you need to create a Translation inner-class and move translatable fields inside. Then a seperate model for those fields is created behind the scenes. Django-multilingual has admin integration and a multi-language flatpages app. Another nice thing about this app is that it’s using signals internally. The API is based on getter and setter methods. For instance you call get_<fieldname>(language_id=None) on your model to get the field value for the active translation. This app has no releases and less than satisfactory API documentation and has tests only on the example project.

Django-pluggable-model-i18n

Django-pluggable-model-i18n uses registration pattern of admin app. It creates an extra model for translated fields and stores translations of non-default languages in it. This app has no releases, no tests, no API documentation and it is clearly stated to be experimental. I would also like to note the last commit date is May 25, 2009.

Django-modeltranslation

Another app that implements registration pattern is django-modeltranslation. The advantage of registration pattern is you don’t have to modify the code of the 3rd party apps you want to translate dynamically. But the database schema still needs to be modified if you are using django-modeltranslation. Also there is one translations.py for the project. I think one translation definition file per app would be better (just like admin does). Django-modeltranslation has intuitive underscore-language_code API but it is somewhat inconsistent; when you read the unsuffixed field you read the currently active language, when you write you write the default language specified in settings.py. Also the modified model stores some redundant data. For instance if your DEFAULT_LANGUAGE is "ES" both <fieldname> and <fieldname>_es columns will store the same value. Django-modeltranslation has admin integration and a management command to update database schema. Most importantly this app is the only one which has both tests and a release. (Unfortunately tests require specific settings to run)

Transdb

Transdb takes a completely different approach to dynamic translation problem. It provides two new field types; TransCharField and TransTextField. Then it serializes all your translations within a single column for each field of those. This means no JOINs and no extra queries. Unfortunately transdb doesn’t implement underscore-language_code API, you need to use get_in_language() and set_in_language() methods. Transdb has default widgets that render one form field for each language. Last commit date is Nov 07, 2008 and there is a release.

Django-multilingual-model

This one is not actually an app but just one module with 33 42 lines of code. You need to define the model that holds translations manually. This introduces some code redundancy, since you also define which fields get translated in the original model. Django-multilingual-model doesn’t implement any translation API, so it’s rather verbose to do anything with it. There are no tests and no releases. I simply don’t recommend django-multilingual-model for anything serious.

Django-transmeta

Django-transmeta stores translations in extra columns it creates in the original field’s table similar to django-modeltranslation. But you enable translation assigning a metaclass for your model and then add a Meta attribute; this means you can’t make models in existing apps translatable without modifying their code. Django-transmeta implements underscore-language_code API, has admin integration and a management command to sync database when you add new languages or translatable fields. There are documentation and code examples but no tests or releases. Last commit date it Nov 24, 2009.

Comparison of Dynamic Translation Apps

Comparison of Dynamic Translation Apps

Software development is making choices. Would you rather have a clean and stable schema with an extra translations model or avoid extra JOINs and denormalize translations onto your original model’s table? Both have advantages and disadvantages. But some choices are not based on trade-offs. Documentation, examples, tests and releases for instance. Also in my opinion underscore-language_code API is way better than any of the alternatives.

Django platform is a very powerful and intuitive one. Many people have moved in last year. This popularity affected app ecosystem as well. But unfortunately a significant number of those apps are half baked fire-and-forget type. I wish 2010 to be the year of a significant increase in software quality of Django apps. I’ll try to do my part.

Bookmark and Share

New Project: Telvee

Wednesday, October 28th, 2009

I have been working on telvee for a few months. To be precise gitk tells the initial commit was at 2009-06-24 16:20:38. I have been meaning to write about it but just couldn’t find the time (or should I say couldn’t drag my lazy ass to do it). Anyway. I have marked version 0.3 for telvee today and here it is.

Telvee is a social toy built around the idea of coffee grounds reading. No, it is not a fortunetelling application. Telvee is intentionally dumb in that sense. It generates an image of the remains inside a coffee cup. Looking at this image, users write fortunes for other users’ cups. This is the basic idea.

What if you don’t know anything about coffee reading? Well, we have a 12 step program that… No, no. If you don’t have proper coffee reading skills just make something up. Telvee is after all nothing more than a social toy. I am very passionate about coffee reading. I can’t tell a fortune. But I can drink the coffee, turn the cup and listen to my fortunes. This has worked pretty well for me in real life. We’ll see how it works out on the Internet when telvee is launched.

As you might have guessed telvee is a Django project. I am happy to work on a well organized, clean and well tested (91% coverage) codebase.

What Works?

Basically all the main functions are in place:

  • Cup image generated
  • You can write fortunes
  • You can moderate fortunes (but blocking is broken)
    • Deleting, recovering, liking, unliking…
  • You can write comments
  • You can delete comments (but blocking is broken)
  • Friending works
  • You can block users (but it doesn’t have any effect for now, hence broken)
  • Invitations work (but, currently hidden from testers)
  • Account management works (but, incomplete)

What Needs To Be Done?

Aside from critical functions I haven’t thought of:

  • A blog
  • Groups
  • Improvements on many pages including homepage
  • Django goodies that has little to do with frontend experience.

When Will It Be Finished?

Probably never. But I would like to initiate a proper beta this year. And then launch as soon as possible, maybe before summer. It is currently tested by a tiny group of people. I will start sending invites as soon as I decide it’s reasonably embarrasing.

If you would like to try telvee; please leave your e-mail at http://www.telvee.com.

Bookmark and Share

Django Fixtures

Thursday, September 24th, 2009

A fixture is basically a data dump in a specific format. There is no restriction of which models or how much data a fixture can contain. Fixtures are portable. They work the same with all the database backends and operating systems supported by Django. This means that you can use fixtures as a simple data migration tool1. Django supports a number of formats by default. JSON format is widely used in the community. But you can easily create serializers for other formats. Although it is not a requirement, all built-in serializers produce human-readable output. Therefore fixtures are an ideal way to keep your data and code together without tightly coupling them.

One limitation you should be aware of is that fixtures are deserialized with the absolute values of primary keys. This makes it difficult to store permissions in fixtures. Also, say, you have two different fixtures for the same model with overlapping pk values. If you load both, instances with overlapping pk‘s will be overwritten by the second load.

Test Fixtures

Fixtures are most useful during testing. They are an easy, reliable way to test your app with some data. If you prefer unittests like I do, here’s how to attach fixtures to your TestCase:

   1 from django.test import TestCase
   2 
   3 
   4 class MyTestCase(TestCase):
   5     fixtures = ['test_users', 'test_foos.json', 'test_bars.xml']
   6 
   7     # rest of your class

Here is a tip for preparing fixtures quickly:

  1. Enable admin for your app. Just registering your models should be enough.
  2. Create and/or edit test data via admin.
  3. You can also use Django shell2 for test data creation.
  4. Dump your data as intermediary fixtures3.
  5. Manually perform any final editing if necessary and save.

This method might look like a lot of work. But it is actually very practical to prepare your test fixtures, or fixtures of any kind this way. Most of the work is removing unneeded data and tweaking the primary keys in the final step. But it is still faster than manually writing the whole thing.

Initial Data

If you want to load some data right after creating your database tables, you provide initial_data fixtures. At the end of syncdb (and flush), Django automatically loads fixtures in your installed apps4 named initial_data regardless of their format.

This is good if your app require some data to function correctly. But there is a pitfall; as stated in Django documentation your initial_data fixture will be loaded every time you run syncdb. And if you edit these entries they will be overwritten next time you run syncdb. Therefore initial_data is only good for data immutable in nature, such as units of length.

If you want to supply initial data for your project it is better to create one or more bootstrap fixtures and load them manually. Here is my minimal list of things I put in my bootstrap fixture:

  • An auth.User as my superuser. It is much easier to load a superuser from fixtures than to create it interactively. I always run syncdb with --noinput and just change my superuser’s password once when I deploy.
  • A profile for my superuser.
  • A sites.site object.

And of course if all of the following fixtures will be loaded when you issue manage.py loaddata bootstrap command:

  • project_dir/myapp1/fixtures/bootstrap.json
  • project_dir/myapp1/fixtures/bootstrap.xml
  • project_dir/myapp2/fixtures/bootstrap.json
  • project_dir/myapp3/fixtures/bootstrap.yml

I think it would be nice if re-usable app authors agreed upon a convention like naming initial data fixtures bootstrap.


1: For very little amount of data this works fine. But serialization/deserialization becomes unreasonably slow for larger data sets. Then it’s better to use native tools for your database, modifying the input if necessarily.

2: Use manage.py shell command to enter Django shell.

3: Use manage.py dumpdata <app_name> > <out_file> command to dump your app data as a fixture. By default Django uses JSON format. If you add --indent=2 it will make the output much easier to read and edit.

4: Apps that are in your settings.INSTALLED_APPS.

Bookmark and Share