Fixed #27279 -- Fixed a migration performance regression related to RenameModel operations.

Thanks Trac alias mtomiyoshi for the report, Marten Kenbeek for the initial
patch and Tim for the review.
This commit is contained in:
Simon Charette 2016-10-03 19:23:18 -04:00
parent 3ab55c1a8a
commit 040bd7c938
3 changed files with 31 additions and 1 deletions

View File

@ -278,6 +278,11 @@ class RenameModel(ModelOperation):
) )
def state_forwards(self, app_label, state): def state_forwards(self, app_label, state):
# In cases where state doesn't have rendered apps, prevent subsequent
# reload_model() calls from rendering models for performance
# reasons. This method should be refactored to avoid relying on
# state.apps (#27310).
reset_apps = 'apps' not in state.__dict__
apps = state.apps apps = state.apps
model = apps.get_model(app_label, self.old_name) model = apps.get_model(app_label, self.old_name)
model._meta.apps = apps model._meta.apps = apps
@ -286,6 +291,8 @@ class RenameModel(ModelOperation):
f for f in model._meta.get_fields(include_hidden=True) f for f in model._meta.get_fields(include_hidden=True)
if f.auto_created and not f.concrete and (not f.hidden or f.many_to_many) if f.auto_created and not f.concrete and (not f.hidden or f.many_to_many)
) )
if reset_apps:
del state.__dict__['apps']
# Rename the model # Rename the model
state.models[app_label, self.new_name_lower] = state.models[app_label, self.old_name_lower] state.models[app_label, self.new_name_lower] = state.models[app_label, self.old_name_lower]
state.models[app_label, self.new_name_lower].name = self.new_name state.models[app_label, self.new_name_lower].name = self.new_name

View File

@ -11,3 +11,6 @@ Bugfixes
* Allowed ``User.is_authenticated`` and ``User.is_anonymous`` properties to be * Allowed ``User.is_authenticated`` and ``User.is_anonymous`` properties to be
tested for ``set`` membership (:ticket:`27309`). tested for ``set`` membership (:ticket:`27309`).
* Fixed a performance regression when running ``migrate`` in projects
with ``RenameModel`` operations (:ticket:`27279`).

View File

@ -5,7 +5,7 @@ import unittest
from django.db import connection, migrations, models, transaction from django.db import connection, migrations, models, transaction
from django.db.migrations.migration import Migration from django.db.migrations.migration import Migration
from django.db.migrations.operations import CreateModel from django.db.migrations.operations import CreateModel
from django.db.migrations.state import ProjectState from django.db.migrations.state import ModelState, ProjectState
from django.db.models.fields import NOT_PROVIDED from django.db.models.fields import NOT_PROVIDED
from django.db.transaction import atomic from django.db.transaction import atomic
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
@ -590,6 +590,26 @@ class OperationTests(OperationTestBase):
self.assertEqual(definition[1], []) self.assertEqual(definition[1], [])
self.assertEqual(definition[2], {'old_name': "Pony", 'new_name': "Horse"}) self.assertEqual(definition[2], {'old_name': "Pony", 'new_name': "Horse"})
def test_rename_model_state_forwards(self):
"""
RenameModel operations shouldn't trigger the caching of rendered apps
on state without prior apps.
"""
state = ProjectState()
state.add_model(ModelState('migrations', 'Foo', []))
operation = migrations.RenameModel('Foo', 'Bar')
operation.state_forwards('migrations', state)
self.assertNotIn('apps', state.__dict__)
self.assertNotIn(('migrations', 'foo'), state.models)
self.assertIn(('migrations', 'bar'), state.models)
# Now with apps cached.
apps = state.apps
operation = migrations.RenameModel('Bar', 'Foo')
operation.state_forwards('migrations', state)
self.assertIs(state.apps, apps)
self.assertNotIn(('migrations', 'bar'), state.models)
self.assertIn(('migrations', 'foo'), state.models)
def test_rename_model_with_self_referential_fk(self): def test_rename_model_with_self_referential_fk(self):
""" """
Tests the RenameModel operation on model with self referential FK. Tests the RenameModel operation on model with self referential FK.