mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #27666 -- Delayed rendering of recursivly related models in migration operations.
This commit is contained in:
committed by
Tim Graham
parent
7d2db2a7b8
commit
45ded053b1
@@ -70,7 +70,9 @@ class AddField(FieldOperation):
|
||||
else:
|
||||
field = self.field
|
||||
state.models[app_label, self.model_name_lower].fields.append((self.name, field))
|
||||
state.reload_model(app_label, self.model_name_lower)
|
||||
# Delay rendering of relationships if it's not a relational field
|
||||
delay = not field.is_relation
|
||||
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
@@ -135,11 +137,16 @@ class RemoveField(FieldOperation):
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
new_fields = []
|
||||
old_field = None
|
||||
for name, instance in state.models[app_label, self.model_name_lower].fields:
|
||||
if name != self.name:
|
||||
new_fields.append((name, instance))
|
||||
else:
|
||||
old_field = instance
|
||||
state.models[app_label, self.model_name_lower].fields = new_fields
|
||||
state.reload_model(app_label, self.model_name_lower)
|
||||
# Delay rendering of relationships if it's not a relational field
|
||||
delay = not old_field.is_relation
|
||||
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
@@ -191,7 +198,11 @@ class AlterField(FieldOperation):
|
||||
for n, f in
|
||||
state.models[app_label, self.model_name_lower].fields
|
||||
]
|
||||
state.reload_model(app_label, self.model_name_lower)
|
||||
# TODO: investigate if old relational fields must be reloaded or if it's
|
||||
# sufficient if the new field is (#27737).
|
||||
# Delay rendering of relationships if it's not a relational field
|
||||
delay = not field.is_relation
|
||||
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
@@ -270,7 +281,13 @@ class RenameField(FieldOperation):
|
||||
[self.new_name if n == self.old_name else n for n in together]
|
||||
for together in options[option]
|
||||
]
|
||||
state.reload_model(app_label, self.model_name_lower)
|
||||
for n, f in state.models[app_label, self.model_name_lower].fields:
|
||||
if n == self.new_name:
|
||||
field = f
|
||||
break
|
||||
# Delay rendering of relationships if it's not a relational field
|
||||
delay = not field.is_relation
|
||||
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
|
||||
@@ -331,10 +331,10 @@ class RenameModel(ModelOperation):
|
||||
model_state.fields[index] = name, changed_field
|
||||
model_changed = True
|
||||
if model_changed:
|
||||
state.reload_model(model_app_label, model_name)
|
||||
state.reload_model(model_app_label, model_name, delay=True)
|
||||
# Remove the old model.
|
||||
state.remove_model(app_label, self.old_name_lower)
|
||||
state.reload_model(app_label, self.new_name_lower)
|
||||
state.reload_model(app_label, self.new_name_lower, delay=True)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
new_model = to_state.apps.get_model(app_label, self.new_name)
|
||||
@@ -444,7 +444,7 @@ class AlterModelTable(ModelOperation):
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.models[app_label, self.name_lower].options["db_table"] = self.table
|
||||
state.reload_model(app_label, self.name_lower)
|
||||
state.reload_model(app_label, self.name_lower, delay=True)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
new_model = to_state.apps.get_model(app_label, self.name)
|
||||
@@ -521,7 +521,7 @@ class AlterUniqueTogether(FieldRelatedOptionOperation):
|
||||
def state_forwards(self, app_label, state):
|
||||
model_state = state.models[app_label, self.name_lower]
|
||||
model_state.options[self.option_name] = self.unique_together
|
||||
state.reload_model(app_label, self.name_lower)
|
||||
state.reload_model(app_label, self.name_lower, delay=True)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
new_model = to_state.apps.get_model(app_label, self.name)
|
||||
@@ -575,7 +575,7 @@ class AlterIndexTogether(FieldRelatedOptionOperation):
|
||||
def state_forwards(self, app_label, state):
|
||||
model_state = state.models[app_label, self.name_lower]
|
||||
model_state.options[self.option_name] = self.index_together
|
||||
state.reload_model(app_label, self.name_lower)
|
||||
state.reload_model(app_label, self.name_lower, delay=True)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
new_model = to_state.apps.get_model(app_label, self.name)
|
||||
@@ -626,7 +626,7 @@ class AlterOrderWithRespectTo(FieldRelatedOptionOperation):
|
||||
def state_forwards(self, app_label, state):
|
||||
model_state = state.models[app_label, self.name_lower]
|
||||
model_state.options['order_with_respect_to'] = self.order_with_respect_to
|
||||
state.reload_model(app_label, self.name_lower)
|
||||
state.reload_model(app_label, self.name_lower, delay=True)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.name)
|
||||
@@ -705,7 +705,7 @@ class AlterModelOptions(ModelOptionOperation):
|
||||
for key in self.ALTER_OPTION_KEYS:
|
||||
if key not in self.options and key in model_state.options:
|
||||
del model_state.options[key]
|
||||
state.reload_model(app_label, self.name_lower)
|
||||
state.reload_model(app_label, self.name_lower, delay=True)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
pass
|
||||
@@ -738,7 +738,7 @@ class AlterModelManagers(ModelOptionOperation):
|
||||
def state_forwards(self, app_label, state):
|
||||
model_state = state.models[app_label, self.name_lower]
|
||||
model_state.managers = list(self.managers)
|
||||
state.reload_model(app_label, self.name_lower)
|
||||
state.reload_model(app_label, self.name_lower, delay=True)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
pass
|
||||
|
||||
@@ -181,6 +181,9 @@ class RunPython(Operation):
|
||||
pass
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
# RunPython has access to all models. Ensure that all models are
|
||||
# reloaded in case any are delayed.
|
||||
from_state.clear_delayed_apps_cache()
|
||||
if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints):
|
||||
# We now execute the Python code in a context that contains a 'models'
|
||||
# object, representing the versioned models as an app registry.
|
||||
|
||||
@@ -52,6 +52,17 @@ def _get_related_models(m):
|
||||
return related_models
|
||||
|
||||
|
||||
def get_related_models_tuples(model):
|
||||
"""
|
||||
Return a list of typical (app_label, model_name) tuples for all related
|
||||
models for the given model.
|
||||
"""
|
||||
return {
|
||||
(rel_mod._meta.app_label, rel_mod._meta.model_name)
|
||||
for rel_mod in _get_related_models(model)
|
||||
}
|
||||
|
||||
|
||||
def get_related_models_recursive(model):
|
||||
"""
|
||||
Return all models that have a direct or indirect relationship
|
||||
@@ -85,6 +96,7 @@ class ProjectState(object):
|
||||
self.models = models or {}
|
||||
# Apps to include from main registry, usually unmigrated ones
|
||||
self.real_apps = real_apps or []
|
||||
self.is_delayed = False
|
||||
|
||||
def add_model(self, model_state):
|
||||
app_label, model_name = model_state.app_label, model_state.name_lower
|
||||
@@ -100,7 +112,10 @@ class ProjectState(object):
|
||||
# the cache automatically (#24513)
|
||||
self.apps.clear_cache()
|
||||
|
||||
def reload_model(self, app_label, model_name):
|
||||
def reload_model(self, app_label, model_name, delay=False):
|
||||
if delay:
|
||||
self.is_delayed = True
|
||||
|
||||
if 'apps' in self.__dict__: # hasattr would cache the property
|
||||
try:
|
||||
old_model = self.apps.get_model(app_label, model_name)
|
||||
@@ -109,7 +124,10 @@ class ProjectState(object):
|
||||
else:
|
||||
# Get all relations to and from the old model before reloading,
|
||||
# as _meta.apps may change
|
||||
related_models = get_related_models_recursive(old_model)
|
||||
if delay:
|
||||
related_models = get_related_models_tuples(old_model)
|
||||
else:
|
||||
related_models = get_related_models_recursive(old_model)
|
||||
|
||||
# Get all outgoing references from the model to be rendered
|
||||
model_state = self.models[(app_label, model_name)]
|
||||
@@ -131,7 +149,10 @@ class ProjectState(object):
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
related_models.update(get_related_models_recursive(rel_model))
|
||||
if delay:
|
||||
related_models.update(get_related_models_tuples(rel_model))
|
||||
else:
|
||||
related_models.update(get_related_models_recursive(rel_model))
|
||||
|
||||
# Include the model itself
|
||||
related_models.add((app_label, model_name))
|
||||
@@ -169,8 +190,13 @@ class ProjectState(object):
|
||||
)
|
||||
if 'apps' in self.__dict__:
|
||||
new_state.apps = self.apps.clone()
|
||||
new_state.is_delayed = self.is_delayed
|
||||
return new_state
|
||||
|
||||
def clear_delayed_apps_cache(self):
|
||||
if self.is_delayed and 'apps' in self.__dict__:
|
||||
del self.__dict__['apps']
|
||||
|
||||
@cached_property
|
||||
def apps(self):
|
||||
return StateApps(self.real_apps, self.models)
|
||||
|
||||
Reference in New Issue
Block a user