#23822 closed New feature (fixed)
Serialize model managers in migrations
Reported by: | Markus Holtermann | Owned by: | Markus Holtermann |
---|---|---|---|
Component: | Migrations | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Right now it's impossible to use e.g. the UserManager
in a RunPython
operation, thus User.objects.create_superuser()
isn't available. We should serialize the model managers into migrations too. I suggest to do it the same way as with callable argument values on fields like upload_to
: the manager need to stay around as long as there is a migration referencing it.
Change History (16)
comment:1 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 10 years ago
I don't understand how this is supposed to work. As a trivial example -- suppose your (custom) user model changes over time, and grows a new mandatory field. This requires you to change your UserManager.create_superuser()
accordingly. For the migration to still work, you need to serialize the actual manager code -- but you clearly do not intend this to be the case, and rightly so.
No, I think that if you need to call some code in a migration, then this code needs to be made available to the migration, but requiring the manager to be compatible with all past migrations still around makes very little sense to me.
comment:3 by , 10 years ago
Hi Shai,
the managers will be serialized in the same way as e.g. functions for field arguments are serialized: imported and referenced. A migration for a custom user model will thus look like
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import myapp.models class Migration(migrations.Migration): operations = [ migrations.CreateModel( name='MyUser', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('password', models.CharField(max_length=128, verbose_name='password')), ('email', models.EmailField(max_length=255, verbose_name='email address', blank=True, unique=True)), ], options={ 'verbose_name': 'user', 'verbose_name_plural': 'users', }, managers=[ ('objects', myapp.models.MyUserManager()), ], ), ]
The managers
argument keeps track of all managers of a model unless only the default manager (objects = models.Manager()
) is available. The argument will be empty / not given in those situations.
A current work-in-progress implementation is available in my migration-managers branch.
comment:4 by , 10 years ago
I had the same concern as Shai with this ticket. Anything that is imported in migration files adopts some new, rather stringent, backwards-compatibility requirements. It can never move, go away, or change in backwards-incompatible ways, as long as the migration(s) referencing it are still around. And getting rid of old migrations is not easy, especially for reusable third-party projects (see the lengthy discussion over on #23861).
But I can also see some cases where a simple custom manager is fundamental to a model's behavior, and less likely to change. (For one example, I think of custom managers I've implemented in the past for soft-delete). I can certainly see the attractiveness of automatically having this manager in migrations as well.
One option would be to make this behavior available, but opt-in via something like a use_in_migrations = True
attribute on the manager, roughly parallel to the existing use_for_related_fields
. That way we wouldn't be forcing anyone into these new back-compat restrictions on custom managers, but for those who understand the tradeoff (the downsides should be well documented) and want to make it, they have that choice.
I like this idea, although I still wonder whether even having that option is a bad temptation to have lying around -- something that'll be awfully tempting in the moment, but you may regret it in the morning :-)
comment:6 by , 10 years ago
Hi Markus,
Since we discussed this topic a bit in Amsterdam I also wanted to chime in order to let you know the use_in_migration
flag also removes the objections I raised.
comment:7 by , 10 years ago
Has patch: | set |
---|---|
Needs tests: | set |
I finally had some time to write some docs and tests. Here's the pull request: https://github.com/django/django/pull/3687
comment:8 by , 10 years ago
Needs tests: | unset |
---|
comment:9 by , 10 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:11 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
My understanding is that this has been discussed offline with Andrew and accepted.