django - contenttype
Posted by Ian Holsman
while most people know about the django’s admin app which comes bundled inside the framework, there are some other treasures in there as well.
The one I like the most is the undocumented contenttype app. as the documentation says simply: It just holds meta-data about the models. and at first glance sounds kinda boring.
but it allows you to do some quite powerful things like generic tables. while some DB puritans might think of this as heresy, I love them.
what is a generic table? —
it’s a table definition which stores the table-id (or in django speak content type) as well as the primary key in each row.
why would I want to do this? —
some times you want to add a feature across lot’s of different parts of your project, the ‘standard’ way would be to create a separate table for the feature for each model you create.
for example let’s say you have a comment feature. and you want to allow people to add comments on conversations, user reviews, and upcoming events.
the standard way would involve creation of a conversation_comment, user_review_comment and event_comment models in your django area, which all look and act the same way.
which is OK.. you can automate a lot of that. but the problem begins when you have to create forms to show them, and when you want to do things like ‘find me all the posts user XYZ has done’, or add some feature to the comment feature it gets pretty ugly damn fast.
with a generic table you just need to add a foreign key to the content-type in your comment model, and ensure you have a common way of creating primary keys, and you can do cross-model type queries like the one I mentioned. here is a example of this in action which is using a ‘tag’ generic table.
some places I’ve seen it used —
- comments
- tagging
- rating/reviews
- row level authorization
the only hiccup at the moment is how to make these generic tables act like regular foreign keys.. which is something Luke and I are working on.
cool functions on content type —
get_for_model -- get's the content type for the record/class you are referring to
eg.. ct = ContentType.objects.get_for_model( User )
get_object_for_this_type -- get's the referring record
my_record = ct.get_object_for_this_type (pk=object_id )
Nice writeup—I also really like the ContentType stuff. I’ve seen other systems that have tried to add this kind of functionality, e.g. share a ‘metadata’ table between 2 or more ‘main’ tables, and the results generally have not been pretty. But Django’s system seems to be just right, and only requires one extra level of indirection, and one extra piece of information (a content type id, as well as the primary key).
Also, to be more specific about that link to my code, it’s in these files:
http://files.lukeplant.fastmail.fm/public/python/lukeplant_me_uk/django/tagging/models.py http://files.lukeplant.fastmail.fm/public/python/lukeplant_me_uk/django/tagging/fields.py