Skip to content

Commit aa5ef0d

Browse files
MarkusHtimgraham
authored andcommitted
Fixed #23822 -- Added support for serializing model managers in migration
Thanks to Shai Berger, Loïc Bistuer, Simon Charette, Andrew Godwin, Tim Graham, Carl Meyer, and others for their review and input.
1 parent e37ab31 commit aa5ef0d

File tree

28 files changed

+609
-49
lines changed

28 files changed

+609
-49
lines changed

django/contrib/admin/migrations/0001_initial.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from django.db import models, migrations
55
from django.conf import settings
6+
import django.contrib.admin.models
67

78

89
class Migration(migrations.Migration):
@@ -32,5 +33,8 @@ class Migration(migrations.Migration):
3233
'verbose_name_plural': 'log entries',
3334
},
3435
bases=(models.Model,),
36+
managers=[
37+
('objects', django.contrib.admin.models.LogEntryManager()),
38+
],
3539
),
3640
]

django/contrib/admin/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616

1717
class LogEntryManager(models.Manager):
18+
use_in_migrations = True
19+
1820
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
1921
e = self.model(
2022
None, None, user_id, content_type_id, smart_text(object_id),

django/contrib/auth/migrations/0001_initial.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.core import validators
55
from django.db import models, migrations
66
from django.utils import timezone
7+
import django.contrib.auth.models
78

89

910
class Migration(migrations.Migration):
@@ -27,6 +28,9 @@ class Migration(migrations.Migration):
2728
'verbose_name': 'permission',
2829
'verbose_name_plural': 'permissions',
2930
},
31+
managers=[
32+
('objects', django.contrib.auth.models.PermissionManager()),
33+
],
3034
),
3135
migrations.CreateModel(
3236
name='Group',
@@ -39,6 +43,9 @@ class Migration(migrations.Migration):
3943
'verbose_name': 'group',
4044
'verbose_name_plural': 'groups',
4145
},
46+
managers=[
47+
('objects', django.contrib.auth.models.GroupManager()),
48+
],
4249
),
4350
migrations.CreateModel(
4451
name='User',
@@ -62,5 +69,8 @@ class Migration(migrations.Migration):
6269
'verbose_name': 'user',
6370
'verbose_name_plural': 'users',
6471
},
72+
managers=[
73+
('objects', django.contrib.auth.models.UserManager()),
74+
],
6575
),
6676
]

django/contrib/auth/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ def update_last_login(sender, user, **kwargs):
2929

3030

3131
class PermissionManager(models.Manager):
32+
use_in_migrations = True
33+
3234
def get_by_natural_key(self, codename, app_label, model):
3335
return self.get(
3436
codename=codename,
@@ -87,6 +89,8 @@ class GroupManager(models.Manager):
8789
"""
8890
The manager for the auth's Group model.
8991
"""
92+
use_in_migrations = True
93+
9094
def get_by_natural_key(self, name):
9195
return self.get(name=name)
9296

@@ -160,6 +164,7 @@ def get_by_natural_key(self, username):
160164

161165

162166
class UserManager(BaseUserManager):
167+
use_in_migrations = True
163168

164169
def _create_user(self, username, email, password,
165170
is_staff, is_superuser, **extra_fields):

django/contrib/contenttypes/migrations/0001_initial.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import unicode_literals
33

44
from django.db import models, migrations
5+
import django.contrib.contenttypes.models
56

67

78
class Migration(migrations.Migration):
@@ -25,6 +26,9 @@ class Migration(migrations.Migration):
2526
'verbose_name_plural': 'content types',
2627
},
2728
bases=(models.Model,),
29+
managers=[
30+
('objects', django.contrib.contenttypes.models.ContentTypeManager()),
31+
],
2832
),
2933
migrations.AlterUniqueTogether(
3034
name='contenttype',

django/contrib/contenttypes/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010

1111
class ContentTypeManager(models.Manager):
12+
use_in_migrations = True
1213

1314
# Cache to avoid re-looking up ContentType objects all over the place.
1415
# This cache is shared by all the get_for_* methods.

django/contrib/sessions/migrations/0001_initial.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import unicode_literals
33

44
from django.db import models, migrations
5+
import django.contrib.sessions.models
56

67

78
class Migration(migrations.Migration):
@@ -23,5 +24,8 @@ class Migration(migrations.Migration):
2324
'verbose_name_plural': 'sessions',
2425
},
2526
bases=(models.Model,),
27+
managers=[
28+
('objects', django.contrib.sessions.models.SessionManager()),
29+
],
2630
),
2731
]

django/contrib/sessions/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77

88
class SessionManager(models.Manager):
9+
use_in_migrations = True
10+
911
def encode(self, session_dict):
1012
"""
1113
Returns the given session dictionary serialized and encoded as a string.

django/contrib/sites/managers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
class CurrentSiteManager(models.Manager):
1111
"Use this to limit objects to those associated with the current site."
1212

13+
use_in_migrations = True
14+
1315
def __init__(self, field_name=None):
1416
super(CurrentSiteManager, self).__init__()
1517
self.__field_name = field_name

django/contrib/sites/migrations/0001_initial.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from django.db import models, migrations
55
from django.contrib.sites.models import _simple_domain_name_validator
6+
import django.contrib.sites.models
67

78

89
class Migration(migrations.Migration):
@@ -24,5 +25,8 @@ class Migration(migrations.Migration):
2425
'verbose_name_plural': 'sites',
2526
},
2627
bases=(models.Model,),
28+
managers=[
29+
('objects', django.contrib.sites.models.SiteManager()),
30+
],
2731
),
2832
]

django/contrib/sites/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def _simple_domain_name_validator(value):
3333

3434

3535
class SiteManager(models.Manager):
36+
use_in_migrations = True
3637

3738
def _get_site_by_id(self, site_id):
3839
if site_id not in SITE_CACHE:

django/db/migrations/autodetector.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ def _detect_changes(self, convert_apps=None, graph=None):
178178
self.generate_deleted_proxies()
179179
self.generate_created_proxies()
180180
self.generate_altered_options()
181+
self.generate_altered_managers()
181182

182183
# Generate field operations
183184
self.generate_renamed_fields()
@@ -503,6 +504,7 @@ def generate_created_models(self):
503504
fields=[d for d in model_state.fields if d[0] not in related_fields],
504505
options=model_state.options,
505506
bases=model_state.bases,
507+
managers=model_state.managers,
506508
),
507509
dependencies=dependencies,
508510
beginning=True,
@@ -607,6 +609,7 @@ def generate_created_proxies(self):
607609
fields=[],
608610
options=model_state.options,
609611
bases=model_state.bases,
612+
managers=model_state.managers,
610613
),
611614
# Depend on the deletion of any possible non-proxy version of us
612615
dependencies=dependencies,
@@ -990,6 +993,20 @@ def generate_altered_order_with_respect_to(self):
990993
dependencies=dependencies,
991994
)
992995

996+
def generate_altered_managers(self):
997+
for app_label, model_name in sorted(self.kept_model_keys):
998+
old_model_name = self.renamed_models.get((app_label, model_name), model_name)
999+
old_model_state = self.from_state.models[app_label, old_model_name]
1000+
new_model_state = self.to_state.models[app_label, model_name]
1001+
if old_model_state.managers != new_model_state.managers:
1002+
self.add_operation(
1003+
app_label,
1004+
operations.AlterModelManagers(
1005+
name=model_name,
1006+
managers=new_model_state.managers,
1007+
)
1008+
)
1009+
9931010
def arrange_for_graph(self, changes, graph, migration_name=None):
9941011
"""
9951012
Takes in a result from changes() and a MigrationGraph,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .models import (CreateModel, DeleteModel, AlterModelTable,
22
AlterUniqueTogether, AlterIndexTogether, RenameModel, AlterModelOptions,
3-
AlterOrderWithRespectTo)
3+
AlterOrderWithRespectTo, AlterModelManagers)
44
from .fields import AddField, RemoveField, AlterField, RenameField
55
from .special import SeparateDatabaseAndState, RunSQL, RunPython
66

@@ -9,5 +9,5 @@
99
'RenameModel', 'AlterIndexTogether', 'AlterModelOptions',
1010
'AddField', 'RemoveField', 'AlterField', 'RenameField',
1111
'SeparateDatabaseAndState', 'RunSQL', 'RunPython',
12-
'AlterOrderWithRespectTo',
12+
'AlterOrderWithRespectTo', 'AlterModelManagers',
1313
]

django/db/migrations/operations/models.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ class CreateModel(Operation):
1212
Create a model's table.
1313
"""
1414

15-
serialization_expand_args = ['fields', 'options']
15+
serialization_expand_args = ['fields', 'options', 'managers']
1616

17-
def __init__(self, name, fields, options=None, bases=None):
17+
def __init__(self, name, fields, options=None, bases=None, managers=None):
1818
self.name = name
1919
self.fields = fields
2020
self.options = options or {}
2121
self.bases = bases or (models.Model,)
22+
self.managers = managers or []
2223

2324
def deconstruct(self):
2425
kwargs = {
@@ -29,6 +30,8 @@ def deconstruct(self):
2930
kwargs['options'] = self.options
3031
if self.bases and self.bases != (models.Model,):
3132
kwargs['bases'] = self.bases
33+
if self.managers and self.managers != [('objects', models.Manager())]:
34+
kwargs['managers'] = self.managers
3235
return (
3336
self.__class__.__name__,
3437
[],
@@ -42,6 +45,7 @@ def state_forwards(self, app_label, state):
4245
list(self.fields),
4346
dict(self.options),
4447
tuple(self.bases),
48+
list(self.managers),
4549
)
4650

4751
def database_forwards(self, app_label, schema_editor, from_state, to_state):
@@ -467,3 +471,38 @@ def references_model(self, name, app_label=None):
467471

468472
def describe(self):
469473
return "Change Meta options on %s" % (self.name, )
474+
475+
476+
class AlterModelManagers(Operation):
477+
"""
478+
Alters the model's managers
479+
"""
480+
481+
serialization_expand_args = ['managers']
482+
483+
def __init__(self, name, managers):
484+
self.name = name
485+
self.managers = managers
486+
487+
def deconstruct(self):
488+
return (
489+
self.__class__.__name__,
490+
[self.name, self.managers],
491+
{}
492+
)
493+
494+
def state_forwards(self, app_label, state):
495+
model_state = state.models[app_label, self.name.lower()]
496+
model_state.managers = list(self.managers)
497+
498+
def database_forwards(self, app_label, schema_editor, from_state, to_state):
499+
pass
500+
501+
def database_backwards(self, app_label, schema_editor, from_state, to_state):
502+
pass
503+
504+
def references_model(self, name, app_label=None):
505+
return name.lower() == self.name.lower()
506+
507+
def describe(self):
508+
return "Change managers on %s" % (self.name, )

0 commit comments

Comments
 (0)