Latest entries

It doesn't rain - it pours. Django MPTT 0.2.1

It turns out Django MPTT 0.2 had a dodgy setup.py, which only worked if you had a valid DJANGO_SETTINGS_MODULE environment variable in place.

You can now grab version 0.2.1 instead, which puts in a quick fix to rectify this.

Posted by jonny on January 16, 2008 | 755 comments

Bugfix Release for Django Tagging - 0.2.1

A bugfix release of Django Tagging - version 0.2.1 - is available for download.

This fixes a bug with space-delimited tag input handling - duplicates weren't being removed and the list of tag names wasn't being sorted. Quoth the new regression test:

Failed example:
    parse_tag_input('one one two two')
Expected:
    [u'one', u'two']
Got:
    [u'one', u'one', u'two', u'two']

Grab the updated version if you're running an official distribution, or svn up to revision 128 if you're tracking trunk

Posted by jonny on January 16, 2008 | 0 comments

Django MPTT 0.2 Released

The second public release of Django MPTT is now available for download.

Backwards-incompatible changes:

  • mptt.register replaces mptt.models.treeify for registering models with the mptt application.
  • move_node is now the only "public" method for moving nodes in TreeManager - it will figure out what do do based on its arguments. Other, peviously public, methods have been renamed with a preceding underscore to indicate the internal nature of their intended use.

New and improved features:

  • New model instance methods:

    • insert_at(target, position, commit)
    • is_child_node()
    • is_root_node()
    • is_leaf_node()
    • get_children()
    • get_next_sibling()
    • get_previous_sibling()
    • get_root()
  • New TreeManager methods:

    • insert_node(node, target, position) - insert a new node at any position in the tree, no need to save and then move any more.
    • root_node(tree_id)
    • root_nodes()
  • Tree options are now held in the Options (a.k.a. _meta) for each registered model.
  • Added an order_insertion_by option when registering a model. If a field name is provided, new nodes will automatically be inserted so they're ordered based on the given field at each level in the tree. This ordering is also maintained when automatic reparenting is performed (i.e. when you change a node's parent, then save it).
  • Nodes can now be moved to be siblings of root nodes - this is a special case due to Django MPTT's use of a tree id field, and used to be an invalid move.
  • TreeManager.move_node now performs transaction management in the same manner as model save() and delete() methods.
  • Added a drilldown_tree_for_node template tag.
  • Added a tree_path template filter.
  • The tree_info template filter now accepts an optional argument which can be used to specify that ancestor information be made available for each node in the tree.
  • Added a custom newforms Form, MoveNodeForm, which takes care of moving a given node to any of a given selection of valid target nodes in the tree.

Browse the version 0.2 HTML documentation online for more details.

Posted by jonny on January 16, 2008 | 5 comments

Django Tagging 0.2 Released

The second public release of Django Tagging is available for download.

Version 0.2 brings:

  • Multi-word tags. Anyone who was waiting for this can thank Jeff Croft for poking me at just the right time.
    • Tags can be either space-delimited or comma-delimited.
    • Double quotes may also be used to identify a multi-word tag, if it needs to contain commas or if you just like double quotes a lot.
    • Comma-delimited tags are triggered by having a non-quoted comma anywhere in your tag input.
    • Unclosed double quotes will be ignored.
  • Querying for unions of tags (tag1 OR tag2 OR tag3) in addition to the existing intersection and single tag lookups.
  • Tag names can now be forced to lowercase before they are saved to the database by adding the appropriate FORCE_LOWERCASE_TAGS setting to your project settings module. This feature defaults to being off.
  • A tagged_object_list generic view for displaying paginated lists of objects for a given model which have a given tag, and optionally - related tags for the model.
  • A new template tag, tag_cloud_for_model.
  • Miscellaneous bug fixes.

This release also includes a significant backwards-incompatible change - the Tag and TaggedItem models no longer explicitly set their database table names using the db_table option. If you're upgrading, you'll have to change the names of your database tables to tagging_tag and tagging_taggeditem or, if that's out of the question for some reason, restore the old db_table options in your copy of Django Tagging.

The next release will focus on making tags easier to work with by allowing you to register your Django models with the tagging application for the addition of custom Managers/instance methods, instead of having to work with TagManager and TaggedItemManager all the time through Tag.objects and TaggedItem.objects respectively.

Posted by jonny on January 12, 2008 | 5 comments

Django MPTT 0.1 Released

The first public release of Django MPTT is available for download.

Version 0.1 features:

  • Simple registration of Model classes for MPTT - fields required specifically for the MPTT tree structure will be added dynamically if not present in a given Model.
  • The tree structure is automatically managed when you create or delete MPTT Model instances. New instances are added as the last child of their parent if they have one, otherwise they become the root of a new tree.
  • MPTT Model instances have methods for retrieving their ancestors, siblings and descendants from the database, or determining the number of descendants they have (which does not involve a database hit with MPTT).
  • The tree structure is automatically updated when you change a Model instance's parent - modified instances are moved so they are the last child of their new parent. This allows basic management of the tree using the parent field available to you in the django.contrib.admin application and in newforms forms automatically generated with ModelForm.
  • The TreeManager which is added to all MPTT Model classes provides a tree management API which can be used to move any node (and its descendants) to an arbitrary point in any tree.
  • A template tag and template filter for working with trees of MPTT Model instances.

Posted by jonny on January 4, 2008 | 4 comments

Django MPTT

Django MPTT is an application I've split out from a project I'm working on - it consists of some utilities for implementing Modified Preorder Tree Traversal (MPTT) with your own Django Model classes and helpers for working with trees of Model instances.

I have a few projects where I've used MPTT to be able to efficiently display trees of Model instances - this application takes some of the logic I'd previously copied and pasted directly into model classes and makes it easy to apply it to any model class.

Example of simplest possible usage:

from django.db import models

from mptt.models import treeify

class Genre(models.Model):
    name = models.CharField(max_length=50, unique=True)
    parent = models.ForeignKey('self', null=True, blank=True, related_name='children')

    def __unicode__(self):
        return self.name

treeify(Genre)

Hey, looks like it works:

>>> action = Genre.objects.create(name='Action')
>>> platformer = Genre.objects.create(name='Platformer', parent=action)
>>> platformer_2d = Genre.objects.create(name='2D Platformer', parent=platformer)
>>> platformer_3d = Genre.objects.create(name='3D Platformer', parent=platformer)
>>> from mptt.debug import print_tree
>>> from django.db import connection
>>> initial_query_count = len(connection.queries)
>>> print_tree(Genre.tree.all())
Action
  Platformer
    2D Platformer
    3D Platformer
>>> print len(connection.queries) - initial_query_count
1

Read the application's documentation for information on what's available and implementation details.

Posted by jonny on December 29, 2007 | 54 comments

Django Forum Demo and Screenshots

Progress on my Django forum application has been pretty swift this week. Its TODO file is starting to collect a list of snazzy features I'd actually like to implement, as well as some of the more standard forum features which have been left out until now for simplicity's sake.

All the basic features required to use the forum in standalone mode are now in place, including registration courtesy of django-registration, so here's a demo site (U: guest, P:guest)and a few work-in-progress screenshots (click for Flickr photo pages):

Forum Index:

Topic Listing:

Topic Page:

Posted by jonny on September 7, 2007 | 5 comments

Emoticon Extension for python-markdown

Another day, another component from my forum application which might be useful elsewhere - this time, it's an emoticon extension I wrote for replacing emoticon symbols with images (which have the symbol as their alt text) when formatting text with python-markdown.

Posted by jonny on September 5, 2007 | 1 comment

ImageURLField for Django newforms

While working on validation of avatars linked to by users in my Django forum application, I was able to extract some of the validation code I ended up with out into a reusable form field for Django's newforms library, named ImageURLField.

It allows you to easily validate details about an image's filesize, dimensions and format, and all without having to read the entire image itself into memory.

Posted by jonny on September 4, 2007 | 0 comments

Django Forum

I'm always banging on about how cool it would be to have Metaposts in web forums - some place which is easily accessible in each topic (but not at all prominent) where you can post about the topic ("There's no point discussing this and I'm going to keep posting this over and over if you keep discussing it" etc. - I'm sure you've seen thousands of these kinds of posts) rather than actually discussing whatever the topic's subject is.

Not only would it give people who want to moan about a topic (legitimately or otherwise) somewhere to vent their bilious fluids, thus helping to prevent the topic itself from going off-topic in that direction, it would also give moderators somewhere to move posts of that nature without resulting in those famous forum staples, OMG Mods Deletion Anger and "Where Did My Post Go" Posts In "Announcements And Feedback" type areas.

So I started a simple forum application (darcs repository) last week as a base for playing around with some ideas I haven't seen implemented in popular web forum software, such as Metaposts.

Screenshot of forum application written with Django

It's currently up to the stage where all the basic things you'd expect of a forum work, but not yet at the stage where you would want to use it on a public site - specifically, all moderation is currently performed using the django.contrib.admin application and there aren't any user permission checks in place yet to make sure you shouldn't be allowed to do stuff based on your account (whoops).

For now, it's still just my playground for new forum features - you can currently choose different types of post formatting using a setting (Markdown and BBCode are already implemented courtesy of python-markdown and postmarkup, respectively) and there's already a Metapost model, which will be integrated once I figure out a way to do so usefully.

Posted by jonny on August 22, 2007 | 4 comments

Notable changes in Django's SVN trunk since 0.96

After someone asked about it in #django, I had a quick trawl through the Django Trac's Timeline to see what's happened in trunk since 0.96.

I've been tracking it for a number of weeks now while developing new applications, as it's reached the point where there are just too many useful new features not to, especially if you've been waiting for DecimalField or you need to work with non-ASCII data.

Taken along with the Backwards Incompatible Changes wiki page, this should give you a good idea of what's new in Django's SVN trunk.

Disclaimer: the author takes no responsibility for ensuring he's actually linked to the correct changesets :-p

Updated on 4th September, 2007

Changeset Description
4830Added {% with %} tag, for caching the result of method which will be used more than once.
4857Added django.contrib.webdesign.
4901Added the ability to name URL patterns. Helps with disambiguity reverse matches.
5011Added django.contrib.databrowse.
5072Made values for login, logout and post-login redirect URLs configurable.
5141Added EmailMessage and SMTPConnection classes.
5144Added support for TLS connections to email handling. This means servers like Google's SMTP server can now be used for admin emails.
5146Added BCC support to the EmailMessage class.
5156Added assertFormError, assertTemplateUsed and assertTemplateNotUsed for use during unit testing.
5209Allow callables as initial data in newforms.
5289Added pop() method to QueryDict.
5357Added the ability to display author names without email addresses to RSS 2.0 feeds.
5379Added a context processor that puts MEDIA_URL in the context, and added that context processor to the default set.
5385You can now use extra(select=...) with values().
5409Fixed problem with specifying a 'fields' argument to a JSON serializer.
5443Added list unpacking to for loops in templates.
5516Added caching speed-ups to reverse URL matching.
5530Allow dots in URL pattern names (although the string in that case is first tried as an import path and only then falls back to being treated as a pattern).
5547Added ability to create multipart email messages.
5550Added support for custom email headers.
5555Added support for regex lookups.
5594'View on site' now works for https URLs.
5630Fixed reverse URL creation to work with non-ASCII arguments.
5653Added RequestSite class to sites framework.
5658Added 'raw' argument to save method, to override any pre-save processing.
5725Added a db_type() method to the database Field class. This is a hook for calculating the database column type for a given Field.
5729Added support for the suite() method recommended by the Python unittest docs.
5746Modified the get_object_or_404/get_list_or_404 shortcuts to also accept QuerySets.
5766Added support for database cache table in test database.
5768Fixed bug with using values() and extra(select) in the same QuerySet
5771Added added set_unusable_password() and has_usable_password() methods to the User object
5803Renamed maxlength argument to max_length for oldforms FormFields and db model Fields. This is fully backwards compatible at the moment since the legacy maxlength argument is still supported. Using maxlength will, however, issue a PendingDeprecationWarning when used.
5804Added dynamic save_m2m method() to forms created with form_for_model and form_for_instance on save(commit=False).
5844Added content_type as an alias for mimetype to the HttpResponse constructor.
5867Added documentation for widgets in newforms.
5877In generic views, set the default name for the slug field to 'slug'.
5898Major refactoring of django.core.management -- it's now a package rather than a 1730-line single module. All django-admin/manage.py commands are now stored in separate modules.
5933Fixed model saving so that 0 can be used as a primary key value.
5950Began implementing BaseDatabaseOperations? class for every database backend. This class will be used to hold the database-specific methods that currently live at the module level in each backend.
5990Added Subversion revision number to Django version string.
6002It's now possible again to use Django without a database. This had temporarily gotten buggy after the django.core.management refactoring last week.
6013The 'flush' and 'sqlflush' management commands no longer touch tables that Django is not aware of (tables that are not in INSTALLED_APPS and/or do not have associated models).
6019Changed the unordered_list template filter to use a more simple format, while maintaining backwards compatibility with the old format. unordered_list now works with a simple list of items.
6039In the test client, Added tracking of the client and request that caused a response so that the assertRedirects check can use the correct client when following a redirect.

Posted by jonny on July 28, 2007 | 3 comments

PHP-Free Status Update #1

Right, time to put these time based categories (a.k.a. Events) to some use...

I have one remaining mid-sized application built with PHP which is still actively supported and developed.

A few months back, after having had one too many bad experiences with the brain-damaged undesign which calls itself PHP, I sat down, opened my text editor and created a models.py file, just out of curiosity. After a few hours, I had about 20 Django Models defined, which were more or less equivalent to the PHP application's database structure. After taking a bit of time to customise the django.contrib.admin application, I had quite a pleasant interface to use to play around with the object model.

So, for the past few months, any time PHP has pissed me off, I've added a little bit to this proto-port of a Django application, and kept the model definitions up to date with changes to the database structure and any new features added.

In the last few weeks, a number of things have happened which have brought the light at the end of the PHP tunnel into view:

  1. I started tracking Django's SVN trunk again, as I tend to do a few months after each major release. Among the other excellent new features, trunk contains a new DecimalField field for models which takes care of any concerns I had about using floats for financial information.
  2. I finally got a few hours together to sit down and actually make some PDFs with ReportLab instead of just reading the documentation. It's far, far better than the PHP PDF classes I'm using to generate invoices and quotes.
  3. When I had another chunk of hours free, and armed with Django's newforms, I tackled the core functionality behind one of the most complex screens in the application. It took me less than 100 lines of code to do something which took days the first time round with PHP.

So I've ported a good chunk of the easy bits, I've all but ported the core of one of the hard bits (and there are a few), I have a better means of creating PDFs, financials can be stored with no loss of accuracy - why not go the whole way?

I'm currently working on the next major release of the PHP application, which is scheduled for deployment this weekend. I'm going to make this my start point for getting serious about leaving PHP behind for good.

I have 5 months and 2 weeks, a full-time job, a wife and daughter to distract me with their special brand of awesomeness and a PHP application to support in the meantime - can I do it? Stay tuned.

Posted by jonny on July 18, 2007 | 114 comments

Django Bits and Pieces

A couple of snippets I've written recently which may come in handy, as I couldn't find anything out there when searching to avoid having to write them myself.

Table Sorting

The django.contrib.admin application has nifty, automatic table header sorting which is quite tightly integrated, so I had a look at how it did its sorting and header generation and created the slightly more reusable SortHeader class.

A full, working sample view, template and inclusion tag are provided to get you started.

Form Resubmission

When displaying a preview of some information generated based on a form which was submitted for approval, you often want to submit the same form information back again using <input type="hidden"> elements - the HiddenBaseForm class will add an as_hidden() method to your forms which allows you to do just that.

Usage examples are provided.

Posted by jonny on July 18, 2007 | 1 comment

django-tagging 0.1

After an "interesting" hour or two with distutils, I've just packaged up and released version 0.1 of django-tagging.

For download and installaton instructions, and an almost definitive feature list for 0.1, see the project's front page.

Posted by jonny on May 30, 2007 | 1 comment

django-tagging and django-voting

I've just put django-tagging and django-voting up on Google Code - two applications for use in your Django projects which do what they say on the tin using generic relations.

Posted by jonny on March 6, 2007 | 4 comments