Loading django/contrib/auth/models.py +1 −1 Original line number Diff line number Diff line Loading @@ -362,7 +362,7 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin): help_text=_('Required. 30 characters or fewer. Letters, numbers and ' '@/./+/-/_ characters'), validators=[ validators.RegexValidator(re.compile('^[\w.@+-]+$'), _('Enter a valid username.'), 'invalid') validators.RegexValidator(r'^[\w.@+-]+$', _('Enter a valid username.'), 'invalid') ]) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=30, blank=True) Loading django/core/validators.py +9 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ from __future__ import unicode_literals import re from django.core.exceptions import ValidationError from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _, ungettext_lazy from django.utils.encoding import force_text from django.utils.ipv6 import is_valid_ipv6_address Loading @@ -14,6 +15,7 @@ from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit EMPTY_VALUES = (None, '', [], (), {}) @deconstructible class RegexValidator(object): regex = '' message = _('Enter a valid value.') Loading @@ -39,6 +41,7 @@ class RegexValidator(object): raise ValidationError(self.message, code=self.code) @deconstructible class URLValidator(RegexValidator): regex = re.compile( r'^(?:http|ftp)s?://' # http:// or https:// Loading Loading @@ -77,6 +80,7 @@ def validate_integer(value): raise ValidationError(_('Enter a valid integer.'), code='invalid') @deconstructible class EmailValidator(object): message = _('Enter a valid email address.') code = 'invalid' Loading Loading @@ -173,6 +177,7 @@ comma_separated_int_list_re = re.compile('^[\d,]+$') validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _('Enter only digits separated by commas.'), 'invalid') @deconstructible class BaseValidator(object): compare = lambda self, a, b: a is not b clean = lambda self, x: x Loading @@ -189,18 +194,21 @@ class BaseValidator(object): raise ValidationError(self.message, code=self.code, params=params) @deconstructible class MaxValueValidator(BaseValidator): compare = lambda self, a, b: a > b message = _('Ensure this value is less than or equal to %(limit_value)s.') code = 'max_value' @deconstructible class MinValueValidator(BaseValidator): compare = lambda self, a, b: a < b message = _('Ensure this value is greater than or equal to %(limit_value)s.') code = 'min_value' @deconstructible class MinLengthValidator(BaseValidator): compare = lambda self, a, b: a < b clean = lambda self, x: len(x) Loading @@ -211,6 +219,7 @@ class MinLengthValidator(BaseValidator): code = 'min_length' @deconstructible class MaxLengthValidator(BaseValidator): compare = lambda self, a, b: a > b clean = lambda self, x: len(x) Loading django/db/migrations/writer.py +3 −2 Original line number Diff line number Diff line Loading @@ -146,6 +146,9 @@ class MigrationWriter(object): elif isinstance(value, models.Field): attr_name, path, args, kwargs = value.deconstruct() return cls.serialize_deconstructed(path, args, kwargs) # Anything that knows how to deconstruct itself. elif hasattr(value, 'deconstruct'): return cls.serialize_deconstructed(*value.deconstruct()) # Functions elif isinstance(value, (types.FunctionType, types.BuiltinFunctionType)): # @classmethod? Loading @@ -153,8 +156,6 @@ class MigrationWriter(object): klass = value.__self__ module = klass.__module__ return "%s.%s.%s" % (module, klass.__name__, value.__name__), set(["import %s" % module]) elif hasattr(value, 'deconstruct'): return cls.serialize_deconstructed(*value.deconstruct()) elif value.__name__ == '<lambda>': raise ValueError("Cannot serialize function: lambda") elif value.__module__ is None: Loading django/utils/deconstruct.py 0 → 100644 +35 −0 Original line number Diff line number Diff line def deconstructible(*args, **kwargs): """ Class decorator that allow the decorated class to be serialized by the migrations subsystem. Accepts an optional kwarg `path` to specify the import path. """ path = kwargs.pop('path', None) def decorator(klass): def __new__(cls, *args, **kwargs): # We capture the arguments to make returning them trivial obj = super(klass, cls).__new__(cls) obj._constructor_args = (args, kwargs) return obj def deconstruct(obj): """ Returns a 3-tuple of class import path, positional arguments, and keyword arguments. """ return ( path or '%s.%s' % (obj.__class__.__module__, obj.__class__.__name__), obj._constructor_args[0], obj._constructor_args[1], ) klass.__new__ = staticmethod(__new__) klass.deconstruct = deconstruct return klass if not args: return decorator return decorator(*args, **kwargs) tests/migrations/test_writer.py +14 −0 Original line number Diff line number Diff line Loading @@ -6,11 +6,13 @@ import copy import datetime import os from django.core.validators import RegexValidator, EmailValidator from django.db import models, migrations from django.db.migrations.writer import MigrationWriter from django.db.models.loading import cache from django.test import TestCase, override_settings from django.utils import six from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _ Loading Loading @@ -77,6 +79,18 @@ class WriterTests(TestCase): self.assertSerializedEqual(datetime.datetime.today) self.assertSerializedEqual(datetime.date.today()) self.assertSerializedEqual(datetime.date.today) # Classes validator = RegexValidator(message="hello") string, imports = MigrationWriter.serialize(validator) self.assertEqual(string, "django.core.validators.RegexValidator(message=%s)" % repr("hello")) self.serialize_round_trip(validator) validator = EmailValidator(message="hello") # Test with a subclass. string, imports = MigrationWriter.serialize(validator) self.assertEqual(string, "django.core.validators.EmailValidator(message=%s)" % repr("hello")) self.serialize_round_trip(validator) validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello") string, imports = MigrationWriter.serialize(validator) self.assertEqual(string, "custom.EmailValidator(message=%s)" % repr("hello")) # Django fields self.assertSerializedFieldEqual(models.CharField(max_length=255)) self.assertSerializedFieldEqual(models.TextField(null=True, blank=True)) Loading Loading
django/contrib/auth/models.py +1 −1 Original line number Diff line number Diff line Loading @@ -362,7 +362,7 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin): help_text=_('Required. 30 characters or fewer. Letters, numbers and ' '@/./+/-/_ characters'), validators=[ validators.RegexValidator(re.compile('^[\w.@+-]+$'), _('Enter a valid username.'), 'invalid') validators.RegexValidator(r'^[\w.@+-]+$', _('Enter a valid username.'), 'invalid') ]) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=30, blank=True) Loading
django/core/validators.py +9 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ from __future__ import unicode_literals import re from django.core.exceptions import ValidationError from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _, ungettext_lazy from django.utils.encoding import force_text from django.utils.ipv6 import is_valid_ipv6_address Loading @@ -14,6 +15,7 @@ from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit EMPTY_VALUES = (None, '', [], (), {}) @deconstructible class RegexValidator(object): regex = '' message = _('Enter a valid value.') Loading @@ -39,6 +41,7 @@ class RegexValidator(object): raise ValidationError(self.message, code=self.code) @deconstructible class URLValidator(RegexValidator): regex = re.compile( r'^(?:http|ftp)s?://' # http:// or https:// Loading Loading @@ -77,6 +80,7 @@ def validate_integer(value): raise ValidationError(_('Enter a valid integer.'), code='invalid') @deconstructible class EmailValidator(object): message = _('Enter a valid email address.') code = 'invalid' Loading Loading @@ -173,6 +177,7 @@ comma_separated_int_list_re = re.compile('^[\d,]+$') validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _('Enter only digits separated by commas.'), 'invalid') @deconstructible class BaseValidator(object): compare = lambda self, a, b: a is not b clean = lambda self, x: x Loading @@ -189,18 +194,21 @@ class BaseValidator(object): raise ValidationError(self.message, code=self.code, params=params) @deconstructible class MaxValueValidator(BaseValidator): compare = lambda self, a, b: a > b message = _('Ensure this value is less than or equal to %(limit_value)s.') code = 'max_value' @deconstructible class MinValueValidator(BaseValidator): compare = lambda self, a, b: a < b message = _('Ensure this value is greater than or equal to %(limit_value)s.') code = 'min_value' @deconstructible class MinLengthValidator(BaseValidator): compare = lambda self, a, b: a < b clean = lambda self, x: len(x) Loading @@ -211,6 +219,7 @@ class MinLengthValidator(BaseValidator): code = 'min_length' @deconstructible class MaxLengthValidator(BaseValidator): compare = lambda self, a, b: a > b clean = lambda self, x: len(x) Loading
django/db/migrations/writer.py +3 −2 Original line number Diff line number Diff line Loading @@ -146,6 +146,9 @@ class MigrationWriter(object): elif isinstance(value, models.Field): attr_name, path, args, kwargs = value.deconstruct() return cls.serialize_deconstructed(path, args, kwargs) # Anything that knows how to deconstruct itself. elif hasattr(value, 'deconstruct'): return cls.serialize_deconstructed(*value.deconstruct()) # Functions elif isinstance(value, (types.FunctionType, types.BuiltinFunctionType)): # @classmethod? Loading @@ -153,8 +156,6 @@ class MigrationWriter(object): klass = value.__self__ module = klass.__module__ return "%s.%s.%s" % (module, klass.__name__, value.__name__), set(["import %s" % module]) elif hasattr(value, 'deconstruct'): return cls.serialize_deconstructed(*value.deconstruct()) elif value.__name__ == '<lambda>': raise ValueError("Cannot serialize function: lambda") elif value.__module__ is None: Loading
django/utils/deconstruct.py 0 → 100644 +35 −0 Original line number Diff line number Diff line def deconstructible(*args, **kwargs): """ Class decorator that allow the decorated class to be serialized by the migrations subsystem. Accepts an optional kwarg `path` to specify the import path. """ path = kwargs.pop('path', None) def decorator(klass): def __new__(cls, *args, **kwargs): # We capture the arguments to make returning them trivial obj = super(klass, cls).__new__(cls) obj._constructor_args = (args, kwargs) return obj def deconstruct(obj): """ Returns a 3-tuple of class import path, positional arguments, and keyword arguments. """ return ( path or '%s.%s' % (obj.__class__.__module__, obj.__class__.__name__), obj._constructor_args[0], obj._constructor_args[1], ) klass.__new__ = staticmethod(__new__) klass.deconstruct = deconstruct return klass if not args: return decorator return decorator(*args, **kwargs)
tests/migrations/test_writer.py +14 −0 Original line number Diff line number Diff line Loading @@ -6,11 +6,13 @@ import copy import datetime import os from django.core.validators import RegexValidator, EmailValidator from django.db import models, migrations from django.db.migrations.writer import MigrationWriter from django.db.models.loading import cache from django.test import TestCase, override_settings from django.utils import six from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _ Loading Loading @@ -77,6 +79,18 @@ class WriterTests(TestCase): self.assertSerializedEqual(datetime.datetime.today) self.assertSerializedEqual(datetime.date.today()) self.assertSerializedEqual(datetime.date.today) # Classes validator = RegexValidator(message="hello") string, imports = MigrationWriter.serialize(validator) self.assertEqual(string, "django.core.validators.RegexValidator(message=%s)" % repr("hello")) self.serialize_round_trip(validator) validator = EmailValidator(message="hello") # Test with a subclass. string, imports = MigrationWriter.serialize(validator) self.assertEqual(string, "django.core.validators.EmailValidator(message=%s)" % repr("hello")) self.serialize_round_trip(validator) validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello") string, imports = MigrationWriter.serialize(validator) self.assertEqual(string, "custom.EmailValidator(message=%s)" % repr("hello")) # Django fields self.assertSerializedFieldEqual(models.CharField(max_length=255)) self.assertSerializedFieldEqual(models.TextField(null=True, blank=True)) Loading