I'm working on a third party app that needs to integrate with the migrations framework and I'm having trouble finding any built in way to do that.
The app I'm working on allows you to defined "revision logs" that tracks changes to a source model. So you get something like this:
from django.db import models
from my_app.revision import RevisionLog, TrackModel
class Contact(models.Model):
""" The model to be tracked. """
name = models.CharField(max_length=100)
email = models.EmailField()
class ContactRevisionLog(RevisionLog):
""" Revision log tracking Contact model. """
class Meta:
copy_fields = {'name': 'name'}
model = TrackModel(Contact)
name = models.CharField(max_length=100)
The RevisionLog is a normal abstract model with some fields on it. The app works by creating SQL triggers that create a new RevisionLog every time a change is made to the model being tracked, basically an audit trail.
If you don't like SQL triggers please keep it to yourself, I'm aware of the signals framework...
Anyways, I've poured over the migrations autodetector and related code and there doesn't appear to be any possible way to hook into it. So my solution right now is to provide my own makemigrations management command which silently replaces the built in one (I had no idea Django allowed you to do this without patching). The command looks like this:
from django.core.management.commands import makemigrations
from my_app.migration_ops import inspect_changes
class Command(makemigrations.Command):
def write_migration_files(changes):
""" Override here so we can inspect the changes and possibly insert our own operations """
changes = inspect_changes(changes)
super(Command, self).write_migration_files(changes)
So that works well enough and at least I have some reasonable way to hook into makemigrations and insert my own operations. The idea is that a user of my_app can run makemigrations like normal and the appropriate operations will be generated, including operations that manage the SQL triggers that I need.
The last big hurdle to get this working the way I want is that Django doesn't allow custom attributes on Model.Meta. The copy_fields option seen above has an effect on the SQL trigger that is generated, so if a user changes its value and runs makemigrations a new migration should be made. Changes made to the builtin meta options are picked up by the migrations autodetector so I assumed I could take advantage of that and define my own but Django validates all the attributes.
It looks like my options are to either:
- monkeypatch the list of allowed meta options, although I'm wary that this might have strange side effects elsewhere
- use an attribute directly on the model class, but then there's no obvious way to have the migrations autodetector pick this up