A while back, I wrote up an article on creating models dynamically at run-time, and Malcolm Tredinnick rightly noted that there are potential uses, but it’s high time I provide an actual example.
Malcolm had it right in his analysis, reading between the lines in my example code to realize that I do indeed have plans for web-based model design. On a more practicle (and already available) note, however, a recent discussion on django-developers allowed me a chance to illustrate how real people can use it for real things.
When looking to create an audit trail, one possible approach is to use a single structure to store changes to all models, which is what the full-history branch is aiming to do. However, this tends to be slower than using a separate structure for each model, since then each structure can be tailor-made for the model at hand.
This second approach is what what George Vilches was looking into, when he ran into a wall. Creating separate models for each audit trail would not only take extra time, it would violate DRY principles. So he decided to extend my dynamic model example and have Django do the heavy lifting.
The following is my approach to the problem he described, which also fixes some issues he was having with his implementation.
The supporting code for this is actually fairly simple. It uses a few of Django’s convenient hooks to tie in where it needs to. Of note, it uses
contribute_to_class to activate itself, rather than hooking into Django directly. This has the added benefit of allowing the developer to choose what name is attached to the model.
It also makes use of a couple signals,
class_prepared is used to make sure that the audit model isn’t created until all of the original modle’s fields have been processed. This way, the audit model can be made to match the original, along with some added fields. It listens to
post_save on the original model so it can store the new state of the object after every save.
The real “magic” of it is that it copies the original model’s
__module__ attribute to the new model, so the audit model is perceived by Django as if it was written in the real models file. This allows it to be picked up by all the standard
management.py tools, such as
Usage details, code and other information can be found at the Wiki article on the subject. And remember, althrough this code works for some models, it will break on others, and is intended primarily as a learning tool. It’s also a great example of what can be achieved in three hours, with a solid understanding of metaclasses and Django’s internals.