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:
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.