[1.8.x] Fixed #24701 -- Converted model manager names to unicode in migrations

Thanks to Reto Aebersold for reporting the issue and Tim Graham and
Claude Paroz for the review.

Backport of faad6070ee from master
This commit is contained in:
Markus Holtermann 2015-04-25 03:08:23 +02:00
parent fc342bff94
commit 419f296259
3 changed files with 16 additions and 6 deletions

View File

@ -419,7 +419,7 @@ class ModelState(object):
bases = (models.Model,) bases = (models.Model,)
# Constructs all managers on the model # Constructs all managers on the model
managers = {} managers_mapping = {}
def reconstruct_manager(mgr): def reconstruct_manager(mgr):
as_manager, manager_path, qs_path, args, kwargs = mgr.deconstruct() as_manager, manager_path, qs_path, args, kwargs = mgr.deconstruct()
@ -431,16 +431,17 @@ class ModelState(object):
instance = manager_class(*args, **kwargs) instance = manager_class(*args, **kwargs)
# We rely on the ordering of the creation_counter of the original # We rely on the ordering of the creation_counter of the original
# instance # instance
managers[mgr.name] = (mgr.creation_counter, instance) name = force_text(mgr.name)
managers_mapping[name] = (mgr.creation_counter, instance)
if hasattr(model, "_default_manager"): if hasattr(model, "_default_manager"):
default_manager_name = model._default_manager.name default_manager_name = force_text(model._default_manager.name)
# Make sure the default manager is always the first # Make sure the default manager is always the first
if model._default_manager.use_in_migrations: if model._default_manager.use_in_migrations:
reconstruct_manager(model._default_manager) reconstruct_manager(model._default_manager)
else: else:
# Force this manager to be the first and thus default # Force this manager to be the first and thus default
managers[default_manager_name] = (0, models.Manager()) managers_mapping[default_manager_name] = (0, models.Manager())
# Sort all managers by their creation counter # Sort all managers by their creation counter
for _, manager, _ in sorted(model._meta.managers): for _, manager, _ in sorted(model._meta.managers):
if manager.name == "_base_manager" or not manager.use_in_migrations: if manager.name == "_base_manager" or not manager.use_in_migrations:
@ -450,7 +451,7 @@ class ModelState(object):
# instance for further processing # instance for further processing
managers = [ managers = [
(name, instance) for name, (cc, instance) in (name, instance) for name, (cc, instance) in
sorted(managers.items(), key=lambda v: v[1]) sorted(managers_mapping.items(), key=lambda v: v[1])
] ]
if managers == [(default_manager_name, models.Manager())]: if managers == [(default_manager_name, models.Manager())]:
managers = [] managers = []
@ -496,6 +497,7 @@ class ModelState(object):
# Sort all managers by their creation counter # Sort all managers by their creation counter
sorted_managers = sorted(self.managers, key=lambda v: v[1].creation_counter) sorted_managers = sorted(self.managers, key=lambda v: v[1].creation_counter)
for mgr_name, manager in sorted_managers: for mgr_name, manager in sorted_managers:
mgr_name = force_text(mgr_name)
as_manager, manager_path, qs_path, args, kwargs = manager.deconstruct() as_manager, manager_path, qs_path, args, kwargs = manager.deconstruct()
if as_manager: if as_manager:
qs_class = import_string(qs_path) qs_class = import_string(qs_path)

View File

@ -66,6 +66,9 @@ Bugfixes
* Fixed a migration crash when adding new relations to models * Fixed a migration crash when adding new relations to models
(:ticket:`24573`). (:ticket:`24573`).
* Fixed a migration crash when applying migrations with model managers on
Python 3 that were generated on Python 2 (:ticket:`24701`).
Optimizations Optimizations
============= =============

View File

@ -7,6 +7,7 @@ from django.db.migrations.state import (
InvalidBasesError, ModelState, ProjectState, get_related_models_recursive, InvalidBasesError, ModelState, ProjectState, get_related_models_recursive,
) )
from django.test import SimpleTestCase, TestCase, override_settings from django.test import SimpleTestCase, TestCase, override_settings
from django.utils import six
from .models import ( from .models import (
FoodManager, FoodQuerySet, ModelWithCustomBase, NoMigrationFoodManager, FoodManager, FoodQuerySet, ModelWithCustomBase, NoMigrationFoodManager,
@ -143,6 +144,7 @@ class StateTests(TestCase):
# The default manager is used in migrations # The default manager is used in migrations
self.assertEqual([name for name, mgr in food_state.managers], ['food_mgr']) self.assertEqual([name for name, mgr in food_state.managers], ['food_mgr'])
self.assertTrue(all(isinstance(name, six.text_type) for name, mgr in food_state.managers))
self.assertEqual(food_state.managers[0][1].args, ('a', 'b', 1, 2)) self.assertEqual(food_state.managers[0][1].args, ('a', 'b', 1, 2))
# No explicit managers defined. Migrations will fall back to the default # No explicit managers defined. Migrations will fall back to the default
@ -152,11 +154,13 @@ class StateTests(TestCase):
# default # default
self.assertEqual([name for name, mgr in food_no_default_manager_state.managers], self.assertEqual([name for name, mgr in food_no_default_manager_state.managers],
['food_no_mgr', 'food_mgr']) ['food_no_mgr', 'food_mgr'])
self.assertTrue(all(isinstance(name, six.text_type) for name, mgr in food_no_default_manager_state.managers))
self.assertEqual(food_no_default_manager_state.managers[0][1].__class__, models.Manager) self.assertEqual(food_no_default_manager_state.managers[0][1].__class__, models.Manager)
self.assertIsInstance(food_no_default_manager_state.managers[1][1], FoodManager) self.assertIsInstance(food_no_default_manager_state.managers[1][1], FoodManager)
self.assertEqual([name for name, mgr in food_order_manager_state.managers], self.assertEqual([name for name, mgr in food_order_manager_state.managers],
['food_mgr1', 'food_mgr2']) ['food_mgr1', 'food_mgr2'])
self.assertTrue(all(isinstance(name, six.text_type) for name, mgr in food_order_manager_state.managers))
self.assertEqual([mgr.args for name, mgr in food_order_manager_state.managers], self.assertEqual([mgr.args for name, mgr in food_order_manager_state.managers],
[('a', 'b', 1, 2), ('x', 'y', 3, 4)]) [('a', 'b', 1, 2), ('x', 'y', 3, 4)])
@ -203,7 +207,7 @@ class StateTests(TestCase):
# The ordering we really want is objects, mgr1, mgr2 # The ordering we really want is objects, mgr1, mgr2
('default', base_mgr), ('default', base_mgr),
('food_mgr2', mgr2), ('food_mgr2', mgr2),
('food_mgr1', mgr1), (b'food_mgr1', mgr1),
] ]
)) ))
@ -217,6 +221,7 @@ class StateTests(TestCase):
managers = sorted(Food._meta.managers) managers = sorted(Food._meta.managers)
self.assertEqual([mgr.name for _, mgr, _ in managers], self.assertEqual([mgr.name for _, mgr, _ in managers],
['default', 'food_mgr1', 'food_mgr2']) ['default', 'food_mgr1', 'food_mgr2'])
self.assertTrue(all(isinstance(mgr.name, six.text_type) for _, mgr, _ in managers))
self.assertEqual([mgr.__class__ for _, mgr, _ in managers], self.assertEqual([mgr.__class__ for _, mgr, _ in managers],
[models.Manager, FoodManager, FoodManager]) [models.Manager, FoodManager, FoodManager])
self.assertIs(managers[0][1], Food._default_manager) self.assertIs(managers[0][1], Food._default_manager)