Month: November 2009

Recent Posts
Uncategorized

Django On-Demand Model Fields

I’m writing an Inventory system for work in my downtime, part to brush up my rusting Django skills, part because we really need one.

We started with a few requirements for what would make a good inventory system to meet our needs, and one of those was that it would be painless to add new types of items and have the ability to customize them. If you know much about Django models, you probably know enough to know that models are typically hard-baked. If you’re writing a blog system, you might create a model with fields for Title, Author, Date, Text. Well for us, what if I want Widget X to have a field for Serial Number, and Widget Y to have a field for Firmware?

Well, one option is to write a model for each item. This unfortunately breaks both requirements- new item types must be added by the coder (me), and any changes to item properties also have to go through me. I don’t want to have to run constant maintenance here, so no thanks, I’ll pass on that.

What I’ve come up with instead are two models. One called ItemType, which consists of 3 fields: name, display, and custom_fields. The first two are self-explanatory. The last field is typically shown like this:

firmware=DDWRT,Stock|model=Linksys,Cisco|mac_address

The second model is just called Item and contains common properties all items have: ID, Condition, Notes, etc; ItemType is a foreign key here.

A rather customized (but short and sweet!) ModelForm parses the custom_fields for the item type of a particular Item, and adds the fields (combobox or textfield depending on if you supplied choices), and the backend is managed by [django-expando](http://github.com/tuttle/django-expando) so we can even have these custom properties in the first place.

I ran into a stupid Django bug when coding this, which prevented runtime fields from displaying in the ModelAdmin. The patch I’ve [documented and described](http://code.djangoproject.com/ticket/12238), but I’ve really little hope upstream will notice or even fix it. But should you want to do this in the future and it doesn’t work (e.g., it was never fixed), that’s what you’ll need to do.

There’s also this small, nasty recursion error expando is spawning off for reasons I’ve yet to figure out. I’ll nail it eventually. (UPDATE: upstream django-expando has fixed this, hoo-rah!)

But regardless, the dynamic typing system for this works really well and I’m kind of surprised and happy it turned out to be a success. Should it get to be proper production-level code, I’ll look into opensourcing it. For now, just get a hold of me if you’ve any questions about how to make this solution work.

So, Passenger is actually pretty useful

I have to admit, I’d heard of [Passenger/mod_rails](http://www.modrails.com/) before, but I only found out yesterday by chance that it also handles WSGI. Since most of any serious work I do is with Python, this caught my attention. Passenger turned out to be simple to setup, and braindead-simple to get running.

I’ve gone through several iterations of how to run my WSGI, from Apache mod_wsgi to Nginx mod_wsgi to Nginx proxy + CherryPy, and now back to Apache “mod_rails”. As an aside, Nginx mod_wsgi sounds like a good idea, but it isn’t. The author has written about this as well, and I encourage you to Google for more information.

One thing to remember when you setup your WSGI app with Passenger is that your passenger_wsgi.py file should be in the directory _above_ your DocumentRoot. So if your docroot is /var/www/my.site/public, put the .py in /var/www/my.site/

I also run two redmines, so this simplifies that as well, since Passenger does rails primarily.

Goodbye, Nginx TCP proxying.