2008-09-13

Gotchas about Fixtures for Initial Data

One part of Django testing environment is fixtures. 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 initial_data.* are also loaded into the main database when you synchronize it.

I make fixtures for my application like this:
python manage.py dumpdata --format=json --indent=4 myapp > apps/myapp/fixtures/initial_data.json 


The initial data is read out from apps/myapp/fixtures/initial_data.json and written to the main or the test database when I synchronize the database
python manage.py syncdb

or when I run the tests
python manage.py test myapp


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.

When I create new models, it's common practice to me to sync db so that the new database tables are created and the database schema reflects the models. Just after creation of new tables all fixtures called initial_data from all applications will be loaded. N.B. The fixtures from initial_data overwrite all existing data while synchronizing database. 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:
python manage.py sql myapp_with_new_models


You might have pre_save signal handlers 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. 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.

class MyModel(models.Model):
...
def save(self, *args, **kwargs):
is_new = True
pk = self._get_pk_val()
model = type(self)
if pk and model._default_manager.filter(pk=pk):
is_new = False
# something before saving
super(model, self).save(*args, **kwargs)
# something after saving


aka

class MyModel(models.Model):
...
def save(self, *args, **kwargs):
is_new = True
if self.id and MyModel.objects.filter(id=self.id):
is_new = False
# something before saving
super(MyModel, self).save(*args, **kwargs)
# something after saving


Another alternative for storing default data would be custom sql located at apps/myapp/sql/mymodel.sql, but I haven't tried that yet and probably won't.

BTW, happy programmer day!

2008-09-03

A Note on Python Paths

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.

Python modules are either *.py files or directories containing __init__.py. 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.

For example, if you can run the following, then django is under your python path.
python
>>> import django


Stay tuned to get deeper into python paths.

Installing modules



If a module is installable, usually all you need to do is to extract its setup directory, cd to it, and run
python setup.py install

This will copy the module into the site-packages directory of the current python installation. It might be that you have multiple Python versions on your computer. According to django documentation, you can find the currently used site-packages by
python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"

Or you can use PEAK EasyInstall for installing python modules even faster.

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.

Sym-linking



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.

To create a symlink, type the following in a console/terminal:
ln -s <source> <target>

For example, if you want python to access django which is under /Library/Subversion/django_src/trunk/django, you need to write something like this (considering that your site-packages are at /Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/)
ln -s /Library/Subversion/django_src/trunk/django /Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/django

To delete the symlink, simply remove it (this won't delete the original module):
rm /Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/django


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.

*.pth files



Python supports *.pth files which contain the paths to the parent directories of your python modules 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 my-project.pth and write
/Library/Subversion/django_src/trunk
/Library/Subversion/myproject_src/trunk

or
C:\Subversion\django_src\trunk
C:\Subversion\myproject\trunk

into it. Then django and your project files will be importable in python.

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.

PYTHONPATH variable



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 PYTHONPATH. Note again that python paths point not to the modules themselves, but to their parent directories!

The syntax slightly differs among different platforms.

Linux and Mac OS X:
# checking value
echo $PYTHONPATH
# setting value
export PYTHONPATH="/Library/Subversion/django_src/trunk"
# appending to the existing value
export PYTHONPATH="$PYTHONPATH;/Library/Subversion/django_src/trunk"

Windows:
# checking value
echo %PYTHONPATH%
# setting value
set PYTHONPATH="C:\\Subversion\\django_src\\trunk"
# appending to the existing value
set PYTHONPATH="%PYTHONPATH%;C:\\Subversion\\django_src\\trunk"


Multiple paths can be separated by a colon (";").

PYTHONPATH can be used in scripts and webserver configuration files, but it is not very comfortable in daily use.

Adding paths to sys.path



For the projects that you develop and which should run as standalone applications, you can set the required python paths relatively inside your python code.

Note that all python paths which you set in the PYTHONPATH variable or *.pth files as well as the path of default python libraries and the path of site-packages get listed in python variable sys.path. 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.

You can read the list of loaded python paths like this:
>>> import sys
>>> sys.path


You can also freely modify it, for example:
>>> import sys
>>> sys.path.append("/Library/Subversion/django_src/trunk")
>>> import django


And this is an example, how to get and use paths relative to the currently loaded file:
import os, sys

SVN_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
DJANGO_PATH = os.path.join(SVN_PATH, "django_src", "trunk")
PROJECT_PATH = os.path.join(SVN_PATH, "myproject", "trunk")

sys.path += [DJANGO_PATH, PROJECT_PATH]



I hope this introduction was useful for developers and made the big picture about the paths clearer.

Some more related information can be found at the official python documentation.

2008-08-08

MultipleSubmitButton Widget for ChoiceField

Recently I published a snippet with a widget rendering a choice field as a series of submit buttons.

So the {{ form.do }} field from the following form:

SUBMIT_CHOICES = (
('save', _("Save")),
('save-add', _("Save and Add Another")),
)

class TestForm(forms.Form):
do = forms.ChoiceField(
widget=MultipleSubmitButton,
choices=SUBMIT_CHOICES,
)



will be rendered as:

<ul>
<li><button type="submit" name="do" value="save">Save</button></li>
<li><button type="submit" name="do" value="save-add">Save and Add Another</button></li>
</ul>


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!

2008-07-16

EuroPython 2008

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.

EuroPython

Some highlights from the conference follow.
  • During video conference the author of Python programming language Guido van Rossum 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 object for classes, using xrange(), sorted(), and zip() functions, "//" for floor divisions, and dict.iterkeys(). The full porting to 3.0 guide will be probably written and published in the python.org site.

  • Clone Digger 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.

  • 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 simulated terminal.

  • 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: Py2exe, PyInstaller, or BBFreeze.

  • There is a combined installer of Python, SQLite, and Django for Windows and it is called Instant Django.

  • Django DB External Scheme Evolution Branch is a separate application for doing DB scheme changes easily without manual DB field editing.

  • Honza Král showed the essentials of the new-forms admin 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 {{ form.non_field_errors }} variable.

  • Christian Scholtz presented PYOGP 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.

  • Ricardo Quesada and Lucio Torre told us about cocos2d - a framework made in Python for presentations and sprite-based 2D games.

  • 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: xlrd, xlwt, or pyExcelerator. I successfully used the latter one last week.

  • CVS and SVN are not the only version control management systems in the world :D. The others like Mercurial, Git and Bazaar 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.


Some other useful links:


Others blogged about EuroPython 2008 as well:

2008-06-02

Django Meetup Postmortem

Thanks to the organizer Jannis, last Friday German djangoers met successfully, got some beer at Schleusenkrug 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.

Some djangoers on the left side of the table

Some djangoers on the right side of the table

If you want to get in touch with German djangoers, you can join the #django-de IRC channel on irc.freenode.net. Also you can subscribe to their blogs at django-de.org.

2008-05-27

Django Meetup at Linuxtag

Pssst. Don't tell anybody! There is an unofficial Django user meetup happening in Berlin this Friday at five.

datetime.datetime(2008, 5, 27, 17, 0)


The gathering point is at the main entrance of Linuxtag conference (Messe Berlin).

2008-05-23

Decorating the Render Methods of New-Form Widgets

Perhaps all template masters have already faced the problem of styling HTML input fields of different types. The selectors like input[type=checkbox] 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.

There are a few ugly ways to do that which violate the DRY principle:

  • Encompassing the form element in the template with HTML tag which has a class representing specific type of the input field.
    For example:
    <span class="form_checkbox">
    {{ form.is_privacy_policy_confirmed }}
    </span>


  • Defining the specific CSS class for each form field widget in the form.
    For example:
    class FormExample(forms.Form):
    is_privacy_policy_confirmed = forms.BooleanField(
    required=True,
    widget=CheckboxInput(attrs={'class': 'form_checkbox'}),
    )


  • Extending all Fields and all Widgets which use HTML input fields and using the extended versions instead of the originals.



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.

Although "most sane developers consider it a bad idea", I see the Guerrilla patching of the Widget's render method as the nicest solution to solve this problem.

Guerrilla patch is the modification of the runtime code in dynamic languages without changing the original source code.

The method render of the Widget class draws the input field in HTML. As it takes a parameter attrs for additional input field attributes, my idea was to create a decorator which modifies the incoming parameters and adds a CSS class "form_TYPE", where TYPE is the input field type.

With a little help by Dalius Dobravolskas, I succeeded to code a decorator having an optional parameter which defines the CSS class name for the field. If the class name is not defined, the attribute input_type of the Widget class is used for forming the CSS class name (N.B. not all widgets have this attribute).

from django.newforms.widgets import Input, CheckboxInput, RadioSelect, CheckboxSelectMultiple

### adding class="form_*" to all html input fields ###
def add_css_class(css_class=""):
def modify_input_class(function):
_css_class = css_class
def new_function(*args, **kwargs):
arg_names = function.func_code.co_varnames
new_kwargs = dict(zip(arg_names, args))
new_kwargs.update(kwargs)
css_class = _css_class or "form_%s" % getattr(
new_kwargs['self'],
"input_type",
"undefined",
)
self = new_kwargs.pop("self")
attrs = getattr(self, "attrs", None) or {}
if "class" in attrs:
css_classes = attrs["class"].split()
if css_class not in css_classes:
css_classes.append(css_class)
attrs["class"] = " ".join(css_classes)
else:
attrs["class"] = css_class
self.attrs = attrs
return function(self, **new_kwargs)
return new_function
return modify_input_class
Input.render = add_css_class()(Input.render)
CheckboxInput.render = add_css_class("form_checkbox")(CheckboxInput.render)
RadioSelect.render = add_css_class("form_radio")(RadioSelect.render)
CheckboxSelectMultiple.render = add_css_class("form_checkbox")(CheckboxSelectMultiple.render)


To use this code, just place it in some models.py file in your project.

The strange part here was that the variable css_class isn't recognized by the sub-child function new_function directly although the scope of the variable css_class should let it be accessed there. Anyway, the value got easily accessible when I reassigned it to another variable like _css_class in the child function modify_input_class.

The tricky part of this snippet was getting the attrs 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 new_function collects all the incoming arguments to a dictionary new_kwargs. They can be modified and then passed to the original function to decorate.

Ups. I am late to the studio. So see you next time!

2008-04-08

Tips #2

What time is it now? It's time to give you some more tips about Django development!

Use batch scripts to automate manual routines. 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.

Define overwritable constants in your applications. 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.
from django.conf import settings
SOME_SETTING = getattr(settings, "SOME_SETTING", "default value")


Have one view for site-related JavaScript globals. 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.
The following view might be used to display a javascript page directly from a template. Just pass the template to the view in your urls.py file.
from datetime import datetime, timedelta
from django.views.generic.simple import direct_to_template
def direct_to_js_template(request, *args, **kwargs):
response = direct_to_template(request, *args, **kwargs)
response['Content-Type'] = "application/x-javascript"
now = datetime.utcnow()
response['Last-Modified'] = now.strftime('%a, %d %b %Y %H:%M:%S GMT')
expires = now + timedelta(0, 2678400)
response['Expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S GMT')
return response


And now it's time to go home and to get relaxed.

2008-03-24

DRY While Working With Choices for Forms

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.

To get the same choices from the model as in the admin form, you can use the following:
FIELD_CHOICES = SomeModel._meta.get_field("field_name").get_choices()

Then you can modify the text for the null-value choice, like
FIELD_CHOICES[0] = ("" _("- Choose One -"))

or even remove it:
del FIELD_CHOICES[0]

To save the selected object you can simply assign the chosen value to the foreign key, like:
new_instance = SomeModel()
new_instance.field_name_id = form.cleaned_data['field_name']
new_instance.save()

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:
def get_related_queryset(model, field_name):
"""
Get the queryset for the choices of the field in a model
Example:
objects = get_related_queryset(SomeModel, "field_name")
"""
f = model._meta.get_field(field_name)
qs = f.rel.to._default_manager.complex_filter(f.rel.limit_choices_to)
return qs

Just put this function in one of you applications and import it whenever you need to work with forms. And have happy Easter!

2008-03-04

Hacking Contributed Models

Django comes with a bunch of contributed applications like auth for authentication, admin for basic data manipulation, or flatpages for the dynamic content that tends to be rarely changed. Sometimes you want to use them, but their functionality doesn't completely fit your needs.

You have several options in that case:
  • Use custom models with one-to-one or many-to-one relations, creating extensions for existing models. The main drawback of this approach is that those custom models will be editable in the contributed administration separately from the origin or you will need to create custom views to combine everything nicely.
  • Use modified duplicates instead of the contributed applications. The main drawback of this case is that it will be hard to manage the updates of the models.
  • Use signals to modify the models. This can be too complicated for simple changes.
  • Modify the properties of the models on the fly.
Let's dig deeper into the last option. When you start the shell or request for a page on the web server, at first Django loads all modules according the order of the INSTALLED_APPS setting. As Python is an interpreted language and all methods and properties are public, you can access and change them on the fly.

For example, if your task is to rename verbose name of groups to "Roles" and show related users at the list view of contributed administration, then it can be achieved by the following code in some model which application appears somewhere below the "django.contrib.auth" in the INSTALLED_APPS.
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import Group

def modify_group_class():
""" A function modifying the contributed Group model """
# modifying the verbose names
Group._meta.verbose_name = _("Role")
Group._meta.verbose_name_plural = _("Roles")

def display_users(group):
""" A function displaying users for a group """
links = []
for user in group.user_set.all():
links.append(
"""<a href="/admin/auth/user/%d">%s</a>""" % (
user.id,
("%s %s" % ( # show the real name
user.first_name,
user.last_name, # or the username
)).strip() or user.username,
)
)
return "<br />".join(links)
display_users.allow_tags = True
display_users.short_description = _("Users")

# attaching the new function to the Group model
Group.display_users = display_users

# changing the list_display for the Group model
Group._meta.admin.list_display = ("__str__", "display_users")

modify_group_class()


As you might see from this example, Group._meta returns the Meta class and Group._meta.admin returns the Admin class for the Group model. You can prove it to yourself by browsing objects in Django shell aka Python IDE.

Use this kind of hacking whenever you reasonably need to change Django core functionalities or some third party modules and have no other choice. However, don't overuse it, 'cause it might lead to difficulties in maintaining the project.