Fixed #26475 -- Added functools.partial() support to migrations autodetector.
This commit is contained in:
parent
2a9bcb503f
commit
5402f3ab09
|
@ -1,6 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
import functools
|
||||
import re
|
||||
from itertools import chain
|
||||
|
||||
|
@ -63,6 +64,8 @@ class MigrationAutodetector(object):
|
|||
key: self.deep_deconstruct(value)
|
||||
for key, value in obj.items()
|
||||
}
|
||||
elif isinstance(obj, functools.partial):
|
||||
return (obj.func, self.deep_deconstruct(obj.args), self.deep_deconstruct(obj.keywords))
|
||||
elif isinstance(obj, COMPILED_REGEX_TYPE):
|
||||
return RegexObject(obj)
|
||||
elif isinstance(obj, type):
|
||||
|
|
|
@ -15,3 +15,6 @@ Bugfixes
|
|||
|
||||
* Fixed ``TimeField`` microseconds round-tripping on MySQL and SQLite
|
||||
(:ticket:`26498`).
|
||||
|
||||
* Prevented ``makemigrations`` from generating infinite migrations for a model
|
||||
field that references a ``functools.partial`` (:ticket:`26475`).
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import functools
|
||||
import re
|
||||
|
||||
from django.apps import apps
|
||||
|
@ -657,6 +658,59 @@ class AutodetectorTests(TestCase):
|
|||
self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])
|
||||
self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=True)
|
||||
|
||||
def test_supports_functools_partial(self):
|
||||
def _content_file_name(instance, filename, key, **kwargs):
|
||||
return '{}/{}'.format(instance, filename)
|
||||
|
||||
def content_file_name(key, **kwargs):
|
||||
return functools.partial(_content_file_name, key, **kwargs)
|
||||
|
||||
# An unchanged partial reference.
|
||||
before = self.make_project_state([ModelState("testapp", "Author", [
|
||||
("id", models.AutoField(primary_key=True)),
|
||||
("file", models.FileField(max_length=200, upload_to=content_file_name('file'))),
|
||||
])])
|
||||
after = self.make_project_state([ModelState("testapp", "Author", [
|
||||
("id", models.AutoField(primary_key=True)),
|
||||
("file", models.FileField(max_length=200, upload_to=content_file_name('file'))),
|
||||
])])
|
||||
autodetector = MigrationAutodetector(before, after)
|
||||
changes = autodetector._detect_changes()
|
||||
self.assertNumberMigrations(changes, 'testapp', 0)
|
||||
|
||||
# A changed partial reference.
|
||||
args_changed = self.make_project_state([ModelState("testapp", "Author", [
|
||||
("id", models.AutoField(primary_key=True)),
|
||||
("file", models.FileField(max_length=200, upload_to=content_file_name('other-file'))),
|
||||
])])
|
||||
autodetector = MigrationAutodetector(before, args_changed)
|
||||
changes = autodetector._detect_changes()
|
||||
self.assertNumberMigrations(changes, 'testapp', 1)
|
||||
self.assertOperationTypes(changes, 'testapp', 0, ['AlterField'])
|
||||
# Can't use assertOperationFieldAttributes because we need the
|
||||
# deconstructed version, i.e., the exploded func/args/keywords rather
|
||||
# than the partial: we don't care if it's not the same instance of the
|
||||
# partial, only if it's the same source function, args, and keywords.
|
||||
value = changes['testapp'][0].operations[0].field.upload_to
|
||||
self.assertEqual(
|
||||
(_content_file_name, ('other-file',), {}),
|
||||
(value.func, value.args, value.keywords)
|
||||
)
|
||||
|
||||
kwargs_changed = self.make_project_state([ModelState("testapp", "Author", [
|
||||
("id", models.AutoField(primary_key=True)),
|
||||
("file", models.FileField(max_length=200, upload_to=content_file_name('file', spam='eggs'))),
|
||||
])])
|
||||
autodetector = MigrationAutodetector(before, kwargs_changed)
|
||||
changes = autodetector._detect_changes()
|
||||
self.assertNumberMigrations(changes, 'testapp', 1)
|
||||
self.assertOperationTypes(changes, 'testapp', 0, ['AlterField'])
|
||||
value = changes['testapp'][0].operations[0].field.upload_to
|
||||
self.assertEqual(
|
||||
(_content_file_name, ('file',), {'spam': 'eggs'}),
|
||||
(value.func, value.args, value.keywords)
|
||||
)
|
||||
|
||||
@mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_alteration',
|
||||
side_effect=AssertionError("Should not have prompted for not null addition"))
|
||||
def test_alter_field_to_not_null_with_default(self, mocked_ask_method):
|
||||
|
|
Loading…
Reference in New Issue