Refs #27666 -- Added ProjectState.reload_models().

This commit is contained in:
Markus Holtermann 2016-11-06 13:20:08 +01:00 committed by Tim Graham
parent 45ded053b1
commit 46e0335583
2 changed files with 76 additions and 57 deletions

View File

@ -306,6 +306,7 @@ class RenameModel(ModelOperation):
# Repoint all fields pointing to the old model to the new one. # Repoint all fields pointing to the old model to the new one.
old_model_tuple = app_label, self.old_name_lower old_model_tuple = app_label, self.old_name_lower
new_remote_model = '%s.%s' % (app_label, self.new_name) new_remote_model = '%s.%s' % (app_label, self.new_name)
to_reload = []
for (model_app_label, model_name), model_state in state.models.items(): for (model_app_label, model_name), model_state in state.models.items():
model_changed = False model_changed = False
for index, (name, field) in enumerate(model_state.fields): for index, (name, field) in enumerate(model_state.fields):
@ -331,7 +332,9 @@ class RenameModel(ModelOperation):
model_state.fields[index] = name, changed_field model_state.fields[index] = name, changed_field
model_changed = True model_changed = True
if model_changed: if model_changed:
state.reload_model(model_app_label, model_name, delay=True) to_reload.append((model_app_label, model_name))
# Reload models related to old model before removing the old model.
state.reload_models(to_reload, delay=True)
# Remove the old model. # Remove the old model.
state.remove_model(app_label, self.old_name_lower) state.remove_model(app_label, self.old_name_lower)
state.reload_model(app_label, self.new_name_lower, delay=True) state.reload_model(app_label, self.new_name_lower, delay=True)

View File

@ -112,75 +112,91 @@ class ProjectState(object):
# the cache automatically (#24513) # the cache automatically (#24513)
self.apps.clear_cache() self.apps.clear_cache()
def reload_model(self, app_label, model_name, delay=False): def _find_reload_model(self, app_label, model_name, delay=False):
if delay: if delay:
self.is_delayed = True self.is_delayed = True
if 'apps' in self.__dict__: # hasattr would cache the property related_models = set()
try:
old_model = self.apps.get_model(app_label, model_name) try:
except LookupError: old_model = self.apps.get_model(app_label, model_name)
related_models = set() except LookupError:
pass
else:
# Get all relations to and from the old model before reloading,
# as _meta.apps may change
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)]
# Directly related models are the models pointed to by ForeignKeys,
# OneToOneFields, and ManyToManyFields.
direct_related_models = set()
for name, field in model_state.fields:
if field.is_relation:
if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT:
continue
rel_app_label, rel_model_name = _get_app_label_and_model_name(field.related_model, app_label)
direct_related_models.add((rel_app_label, rel_model_name.lower()))
# For all direct related models recursively get all related models.
related_models.update(direct_related_models)
for rel_app_label, rel_model_name in direct_related_models:
try:
rel_model = self.apps.get_model(rel_app_label, rel_model_name)
except LookupError:
pass
else: else:
# Get all relations to and from the old model before reloading,
# as _meta.apps may change
if delay: if delay:
related_models = get_related_models_tuples(old_model) related_models.update(get_related_models_tuples(rel_model))
else: else:
related_models = get_related_models_recursive(old_model) related_models.update(get_related_models_recursive(rel_model))
# Get all outgoing references from the model to be rendered # Include the model itself
model_state = self.models[(app_label, model_name)] related_models.add((app_label, model_name))
# Directly related models are the models pointed to by ForeignKeys,
# OneToOneFields, and ManyToManyFields.
direct_related_models = set()
for name, field in model_state.fields:
if field.is_relation:
if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT:
continue
rel_app_label, rel_model_name = _get_app_label_and_model_name(field.related_model, app_label)
direct_related_models.add((rel_app_label, rel_model_name.lower()))
# For all direct related models recursively get all related models. return related_models
related_models.update(direct_related_models)
for rel_app_label, rel_model_name in direct_related_models:
try:
rel_model = self.apps.get_model(rel_app_label, rel_model_name)
except LookupError:
pass
else:
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 def reload_model(self, app_label, model_name, delay=False):
related_models.add((app_label, model_name)) if 'apps' in self.__dict__: # hasattr would cache the property
related_models = self._find_reload_model(app_label, model_name, delay)
self._reload(related_models)
# Unregister all related models def reload_models(self, models, delay=True):
with self.apps.bulk_update(): if 'apps' in self.__dict__: # hasattr would cache the property
for rel_app_label, rel_model_name in related_models: related_models = set()
self.apps.unregister_model(rel_app_label, rel_model_name) for app_label, model_name in models:
related_models.update(self._find_reload_model(app_label, model_name, delay))
self._reload(related_models)
states_to_be_rendered = [] def _reload(self, related_models):
# Gather all models states of those models that will be rerendered. # Unregister all related models
# This includes: with self.apps.bulk_update():
# 1. All related models of unmigrated apps
for model_state in self.apps.real_models:
if (model_state.app_label, model_state.name_lower) in related_models:
states_to_be_rendered.append(model_state)
# 2. All related models of migrated apps
for rel_app_label, rel_model_name in related_models: for rel_app_label, rel_model_name in related_models:
try: self.apps.unregister_model(rel_app_label, rel_model_name)
model_state = self.models[rel_app_label, rel_model_name]
except KeyError:
pass
else:
states_to_be_rendered.append(model_state)
# Render all models states_to_be_rendered = []
self.apps.render_multiple(states_to_be_rendered) # Gather all models states of those models that will be rerendered.
# This includes:
# 1. All related models of unmigrated apps
for model_state in self.apps.real_models:
if (model_state.app_label, model_state.name_lower) in related_models:
states_to_be_rendered.append(model_state)
# 2. All related models of migrated apps
for rel_app_label, rel_model_name in related_models:
try:
model_state = self.models[rel_app_label, rel_model_name]
except KeyError:
pass
else:
states_to_be_rendered.append(model_state)
# Render all models
self.apps.render_multiple(states_to_be_rendered)
def clone(self): def clone(self):
"Returns an exact copy of this ProjectState" "Returns an exact copy of this ProjectState"