Commit faad6070 authored by Markus Holtermann's avatar Markus Holtermann
Browse files

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.
parent 1521861b
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -436,7 +436,7 @@ class ModelState(object):
            bases = (models.Model,)

        # Constructs all managers on the model
        managers = {}
        managers_mapping = {}

        def reconstruct_manager(mgr):
            as_manager, manager_path, qs_path, args, kwargs = mgr.deconstruct()
@@ -448,16 +448,17 @@ class ModelState(object):
                instance = manager_class(*args, **kwargs)
            # We rely on the ordering of the creation_counter of the original
            # 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"):
            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
            if model._default_manager.use_in_migrations:
                reconstruct_manager(model._default_manager)
            else:
                # 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
            for _, manager, _ in sorted(model._meta.managers):
                if manager.name == "_base_manager" or not manager.use_in_migrations:
@@ -467,7 +468,7 @@ class ModelState(object):
            # instance for further processing
            managers = [
                (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())]:
                managers = []
@@ -513,6 +514,7 @@ class ModelState(object):
        # Sort all managers by their creation counter
        sorted_managers = sorted(self.managers, key=lambda v: v[1].creation_counter)
        for mgr_name, manager in sorted_managers:
            mgr_name = force_text(mgr_name)
            as_manager, manager_path, qs_path, args, kwargs = manager.deconstruct()
            if as_manager:
                qs_class = import_string(qs_path)
+3 −0
Original line number Diff line number Diff line
@@ -66,6 +66,9 @@ Bugfixes
* Fixed a migration crash when adding new relations to models
  (:ticket:`24573`).

* Fixed a migration crash when applying migrations with model managers on
  Python 3 that were generated on Python 2 (:ticket:`24701`).

Optimizations
=============

+6 −1
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from django.db.migrations.state import (
    InvalidBasesError, ModelState, ProjectState, get_related_models_recursive,
)
from django.test import SimpleTestCase, TestCase, override_settings
from django.utils import six

from .models import (
    FoodManager, FoodQuerySet, ModelWithCustomBase, NoMigrationFoodManager,
@@ -144,6 +145,7 @@ class StateTests(TestCase):

        # The default manager is used in migrations
        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))

        # No explicit managers defined. Migrations will fall back to the default
@@ -153,11 +155,13 @@ class StateTests(TestCase):
        # default
        self.assertEqual([name for name, mgr in food_no_default_manager_state.managers],
                         ['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.assertIsInstance(food_no_default_manager_state.managers[1][1], FoodManager)

        self.assertEqual([name for name, mgr in food_order_manager_state.managers],
                         ['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],
                         [('a', 'b', 1, 2), ('x', 'y', 3, 4)])

@@ -220,7 +224,7 @@ class StateTests(TestCase):
                # The ordering we really want is objects, mgr1, mgr2
                ('default', base_mgr),
                ('food_mgr2', mgr2),
                ('food_mgr1', mgr1),
                (b'food_mgr1', mgr1),
            ]
        ))

@@ -234,6 +238,7 @@ class StateTests(TestCase):
        managers = sorted(Food._meta.managers)
        self.assertEqual([mgr.name for _, mgr, _ in managers],
                         ['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],
                         [models.Manager, FoodManager, FoodManager])
        self.assertIs(managers[0][1], Food._default_manager)