mirror of https://github.com/django/django.git
Fixed #25280 -- Properly checked regex objects for equality to prevent infinite migrations
Thanks Sayid Munawar and Tim Graham for the report, investigation and review.
This commit is contained in:
parent
1175027641
commit
91f701f4fc
|
@ -11,6 +11,7 @@ from django.db.migrations.migration import Migration
|
|||
from django.db.migrations.operations.models import AlterModelOptions
|
||||
from django.db.migrations.optimizer import MigrationOptimizer
|
||||
from django.db.migrations.questioner import MigrationQuestioner
|
||||
from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
|
||||
from django.utils import six
|
||||
|
||||
from .topological_sort import stable_topological_sort
|
||||
|
@ -62,6 +63,8 @@ class MigrationAutodetector(object):
|
|||
key: self.deep_deconstruct(value)
|
||||
for key, value in obj.items()
|
||||
}
|
||||
elif isinstance(obj, COMPILED_REGEX_TYPE):
|
||||
return RegexObject(obj)
|
||||
elif isinstance(obj, type):
|
||||
# If this is a type that implements 'deconstruct' as an instance method,
|
||||
# avoid treating this as being deconstructible itself - see #22951
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import re
|
||||
|
||||
COMPILED_REGEX_TYPE = type(re.compile(''))
|
||||
|
||||
|
||||
class RegexObject(object):
|
||||
def __init__(self, obj):
|
||||
self.pattern = obj.pattern
|
||||
self.flags = obj.flags
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.pattern == other.pattern and self.flags == other.flags
|
|
@ -14,6 +14,7 @@ from django.apps import apps
|
|||
from django.db import migrations, models
|
||||
from django.db.migrations.loader import MigrationLoader
|
||||
from django.db.migrations.operations.base import Operation
|
||||
from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
|
||||
from django.utils import datetime_safe, six
|
||||
from django.utils._os import upath
|
||||
from django.utils.encoding import force_text
|
||||
|
@ -23,8 +24,6 @@ from django.utils.module_loading import module_dir
|
|||
from django.utils.timezone import utc
|
||||
from django.utils.version import get_docs_version
|
||||
|
||||
COMPILED_REGEX_TYPE = type(re.compile(''))
|
||||
|
||||
|
||||
class SettingsReference(str):
|
||||
"""
|
||||
|
@ -506,7 +505,7 @@ class MigrationWriter(object):
|
|||
format = "(%s)" if len(strings) != 1 else "(%s,)"
|
||||
return format % (", ".join(strings)), imports
|
||||
# Compiled regex
|
||||
elif isinstance(value, COMPILED_REGEX_TYPE):
|
||||
elif isinstance(value, (COMPILED_REGEX_TYPE, RegexObject)):
|
||||
imports = {"import re"}
|
||||
regex_pattern, pattern_imports = cls.serialize(value.pattern)
|
||||
regex_flags, flag_imports = cls.serialize(value.flags)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractBaseUser
|
||||
from django.core.validators import RegexValidator, validate_slug
|
||||
from django.db import connection, models
|
||||
from django.db.migrations.autodetector import MigrationAutodetector
|
||||
from django.db.migrations.graph import MigrationGraph
|
||||
|
@ -997,6 +1000,46 @@ class AutodetectorTests(TestCase):
|
|||
self.assertOperationAttributes(changes, "testapp", 0, 0, old_name="Author", new_name="NewAuthor")
|
||||
self.assertOperationAttributes(changes, "testapp", 0, 1, name="newauthor", table="author_three")
|
||||
|
||||
def test_identical_regex_doesnt_alter(self):
|
||||
from_state = ModelState(
|
||||
"testapp", "model", [("id", models.AutoField(primary_key=True, validators=[
|
||||
RegexValidator(
|
||||
re.compile('^[-a-zA-Z0-9_]+\\Z'),
|
||||
"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.",
|
||||
'invalid'
|
||||
)
|
||||
]))]
|
||||
)
|
||||
to_state = ModelState(
|
||||
"testapp", "model", [("id", models.AutoField(primary_key=True, validators=[validate_slug]))]
|
||||
)
|
||||
before = self.make_project_state([from_state])
|
||||
after = self.make_project_state([to_state])
|
||||
autodetector = MigrationAutodetector(before, after)
|
||||
changes = autodetector._detect_changes()
|
||||
# Right number/type of migrations?
|
||||
self.assertNumberMigrations(changes, "testapp", 0)
|
||||
|
||||
def test_different_regex_does_alter(self):
|
||||
from_state = ModelState(
|
||||
"testapp", "model", [("id", models.AutoField(primary_key=True, validators=[
|
||||
RegexValidator(
|
||||
re.compile('^[a-z]+\\Z', 32),
|
||||
"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.",
|
||||
'invalid'
|
||||
)
|
||||
]))]
|
||||
)
|
||||
to_state = ModelState(
|
||||
"testapp", "model", [("id", models.AutoField(primary_key=True, validators=[validate_slug]))]
|
||||
)
|
||||
before = self.make_project_state([from_state])
|
||||
after = self.make_project_state([to_state])
|
||||
autodetector = MigrationAutodetector(before, after)
|
||||
changes = autodetector._detect_changes()
|
||||
self.assertNumberMigrations(changes, "testapp", 1)
|
||||
self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
|
||||
|
||||
def test_empty_foo_together(self):
|
||||
"""
|
||||
#23452 - Empty unique/index_together shouldn't generate a migration.
|
||||
|
|
Loading…
Reference in New Issue