If you have custom code for models (custom validators and custom fields are examples), Django Migrations will import them directly into the migration files. An example:
file web/models.py
def validate_power(self, power):
if power == 'fly':
raise ValidationError('Heroes cannot fly!')
class Hero(Model):
power = CharField(max_length=30, validators=[validate_power])
In the migration file, there will be:
file web/migrations/0001_initial.py
class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='Hero',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, primary_key=True, auto_created=True)),
('power', models.CharField(max_length=30, validators=[web.models.validate_power])),
],
),
]
The validator is imported directly from the codebase. What this means is that if I ever change a validator, I have to make sure it is backwards-compatible, for as long as I keep that migration (which could be forever). Same happens to custom fields, which is worse to keep backwards-compatibility because sometimes you need to change (or just want to change) how you store data, handle it, etc. If you change how you store data, for example, it could make sense the change the validators and then all the previous migrations that relied on the validator will not work anymore (unless you change the migration code, and that could be many files, and also changing migration code should not be needed).
So, this idea of directly importing from the codebase binds data to code. Shouldn't we strive to keep them separate? Maybe we should stick to raw SQL migrations? Is this problem possible to be solved without using raw SQL migrations? Is binding data to code a price worth paying to keep SQL at a distance?