Modified auth management commands to handle custom user definitions.
This commit is contained in:
parent
7cc0baf89d
commit
dabe362836
|
@ -4,6 +4,7 @@ from django.contrib.admin.forms import AdminAuthenticationForm
|
||||||
from django.contrib.auth.views import login
|
from django.contrib.auth.views import login
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
|
|
||||||
|
|
||||||
def staff_member_required(view_func):
|
def staff_member_required(view_func):
|
||||||
"""
|
"""
|
||||||
Decorator for views that checks that the user is logged in and is a staff
|
Decorator for views that checks that the user is logged in and is a staff
|
||||||
|
|
|
@ -4,9 +4,11 @@ Creates permissions for all installed apps that need permissions.
|
||||||
import getpass
|
import getpass
|
||||||
import locale
|
import locale
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
from django.contrib.auth import models as auth_app
|
from django.contrib.auth import models as auth_app
|
||||||
|
from django.core import exceptions
|
||||||
from django.db.models import get_models, signals
|
from django.db.models import get_models, signals
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User, get_user_model
|
||||||
|
|
||||||
|
|
||||||
def _get_permission_codename(action, opts):
|
def _get_permission_codename(action, opts):
|
||||||
|
@ -60,7 +62,9 @@ def create_permissions(app, created_models, verbosity, **kwargs):
|
||||||
def create_superuser(app, created_models, verbosity, db, **kwargs):
|
def create_superuser(app, created_models, verbosity, db, **kwargs):
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
|
||||||
if auth_app.User in created_models and kwargs.get('interactive', True):
|
UserModel = get_user_model()
|
||||||
|
|
||||||
|
if UserModel in created_models and kwargs.get('interactive', True):
|
||||||
msg = ("\nYou just installed Django's auth system, which means you "
|
msg = ("\nYou just installed Django's auth system, which means you "
|
||||||
"don't have any superusers defined.\nWould you like to create one "
|
"don't have any superusers defined.\nWould you like to create one "
|
||||||
"now? (yes/no): ")
|
"now? (yes/no): ")
|
||||||
|
@ -100,16 +104,24 @@ def get_default_username(check_db=True):
|
||||||
:returns: The username, or an empty string if no username can be
|
:returns: The username, or an empty string if no username can be
|
||||||
determined.
|
determined.
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.management.commands.createsuperuser import (
|
# If the User model has been swapped out, we can't make any assumptions
|
||||||
RE_VALID_USERNAME)
|
# about the default user name.
|
||||||
|
if User._meta.swapped:
|
||||||
|
return ''
|
||||||
|
|
||||||
default_username = get_system_username()
|
default_username = get_system_username()
|
||||||
try:
|
try:
|
||||||
default_username = unicodedata.normalize('NFKD', default_username)\
|
default_username = unicodedata.normalize('NFKD', default_username)\
|
||||||
.encode('ascii', 'ignore').replace(' ', '').lower()
|
.encode('ascii', 'ignore').replace(' ', '').lower()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
return ''
|
return ''
|
||||||
if not RE_VALID_USERNAME.match(default_username):
|
|
||||||
|
# Run the username validator
|
||||||
|
try:
|
||||||
|
User._meta.get_field('username').run_validators(default_username)
|
||||||
|
except exceptions.ValidationError:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
# Don't return the default username if it is already taken.
|
# Don't return the default username if it is already taken.
|
||||||
if check_db and default_username:
|
if check_db and default_username:
|
||||||
try:
|
try:
|
||||||
|
@ -120,7 +132,6 @@ def get_default_username(check_db=True):
|
||||||
return ''
|
return ''
|
||||||
return default_username
|
return default_username
|
||||||
|
|
||||||
|
|
||||||
signals.post_syncdb.connect(create_permissions,
|
signals.post_syncdb.connect(create_permissions,
|
||||||
dispatch_uid="django.contrib.auth.management.create_permissions")
|
dispatch_uid="django.contrib.auth.management.create_permissions")
|
||||||
signals.post_syncdb.connect(create_superuser,
|
signals.post_syncdb.connect(create_superuser,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import getpass
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import get_user_model
|
||||||
from django.db import DEFAULT_DB_ALIAS
|
from django.db import DEFAULT_DB_ALIAS
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,12 +30,16 @@ class Command(BaseCommand):
|
||||||
else:
|
else:
|
||||||
username = getpass.getuser()
|
username = getpass.getuser()
|
||||||
|
|
||||||
|
UserModel = get_user_model()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
u = User.objects.using(options.get('database')).get(username=username)
|
u = UserModel.objects.using(options.get('database')).get(**{
|
||||||
except User.DoesNotExist:
|
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
||||||
|
})
|
||||||
|
except UserModel.DoesNotExist:
|
||||||
raise CommandError("user '%s' does not exist" % username)
|
raise CommandError("user '%s' does not exist" % username)
|
||||||
|
|
||||||
self.stdout.write("Changing password for user '%s'\n" % u.username)
|
self.stdout.write("Changing password for user '%s'\n" % u)
|
||||||
|
|
||||||
MAX_TRIES = 3
|
MAX_TRIES = 3
|
||||||
count = 0
|
count = 0
|
||||||
|
@ -48,9 +52,9 @@ class Command(BaseCommand):
|
||||||
count = count + 1
|
count = count + 1
|
||||||
|
|
||||||
if count == MAX_TRIES:
|
if count == MAX_TRIES:
|
||||||
raise CommandError("Aborting password change for user '%s' after %s attempts" % (username, count))
|
raise CommandError("Aborting password change for user '%s' after %s attempts" % (u, count))
|
||||||
|
|
||||||
u.set_password(p1)
|
u.set_password(p1)
|
||||||
u.save()
|
u.save()
|
||||||
|
|
||||||
return "Password changed successfully for user '%s'" % u.username
|
return "Password changed successfully for user '%s'" % u
|
||||||
|
|
|
@ -3,108 +3,113 @@ Management utility to create superusers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import getpass
|
import getpass
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.auth.management import get_default_username
|
from django.contrib.auth.management import get_default_username
|
||||||
|
from django.contrib.auth.models import get_user_model
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db import DEFAULT_DB_ALIAS
|
from django.db import DEFAULT_DB_ALIAS
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.text import capfirst
|
||||||
|
|
||||||
RE_VALID_USERNAME = re.compile('[\w.@+-]+$')
|
|
||||||
|
|
||||||
EMAIL_RE = re.compile(
|
|
||||||
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
|
|
||||||
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string
|
|
||||||
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
|
|
||||||
|
|
||||||
|
|
||||||
def is_valid_email(value):
|
|
||||||
if not EMAIL_RE.search(value):
|
|
||||||
raise exceptions.ValidationError(_('Enter a valid e-mail address.'))
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('--username', dest='username', default=None,
|
make_option('--username', dest='username', default=None,
|
||||||
help='Specifies the username for the superuser.'),
|
help='Specifies the username for the superuser.'),
|
||||||
make_option('--email', dest='email', default=None,
|
|
||||||
help='Specifies the email address for the superuser.'),
|
|
||||||
make_option('--noinput', action='store_false', dest='interactive', default=True,
|
make_option('--noinput', action='store_false', dest='interactive', default=True,
|
||||||
help=('Tells Django to NOT prompt the user for input of any kind. '
|
help=('Tells Django to NOT prompt the user for input of any kind. '
|
||||||
'You must use --username and --email with --noinput, and '
|
'You must use --username with --noinput, along with an option for '
|
||||||
'superusers created with --noinput will not be able to log '
|
'any other required field. Superusers created with --noinput will '
|
||||||
'in until they\'re given a valid password.')),
|
' not be able to log in until they\'re given a valid password.')),
|
||||||
make_option('--database', action='store', dest='database',
|
make_option('--database', action='store', dest='database',
|
||||||
default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
|
default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
|
||||||
|
) + tuple(
|
||||||
|
make_option('--%s' % field, dest=field, default=None,
|
||||||
|
help='Specifies the %s for the superuser.' % field)
|
||||||
|
for field in getattr(get_user_model(), 'REQUIRED_FIELDS', ['email'])
|
||||||
)
|
)
|
||||||
|
|
||||||
help = 'Used to create a superuser.'
|
help = 'Used to create a superuser.'
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
username = options.get('username', None)
|
username = options.get('username', None)
|
||||||
email = options.get('email', None)
|
|
||||||
interactive = options.get('interactive')
|
interactive = options.get('interactive')
|
||||||
verbosity = int(options.get('verbosity', 1))
|
verbosity = int(options.get('verbosity', 1))
|
||||||
database = options.get('database')
|
database = options.get('database')
|
||||||
|
|
||||||
# Do quick and dirty validation if --noinput
|
UserModel = get_user_model()
|
||||||
if not interactive:
|
|
||||||
if not username or not email:
|
username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
|
||||||
raise CommandError("You must use --username and --email with --noinput.")
|
other_fields = getattr(UserModel, 'REQUIRED_FIELDS', ['email'])
|
||||||
if not RE_VALID_USERNAME.match(username):
|
|
||||||
raise CommandError("Invalid username. Use only letters, digits, and underscores")
|
|
||||||
try:
|
|
||||||
is_valid_email(email)
|
|
||||||
except exceptions.ValidationError:
|
|
||||||
raise CommandError("Invalid email address.")
|
|
||||||
|
|
||||||
# If not provided, create the user with an unusable password
|
# If not provided, create the user with an unusable password
|
||||||
password = None
|
password = None
|
||||||
|
other_data = {}
|
||||||
|
|
||||||
# Prompt for username/email/password. Enclose this whole thing in a
|
# Do quick and dirty validation if --noinput
|
||||||
# try/except to trap for a keyboard interrupt and exit gracefully.
|
if not interactive:
|
||||||
if interactive:
|
try:
|
||||||
|
if not username:
|
||||||
|
raise CommandError("You must use --username with --noinput.")
|
||||||
|
username = username_field.clean(username, None)
|
||||||
|
|
||||||
|
for field_name in other_fields:
|
||||||
|
if options.get(field_name):
|
||||||
|
field = UserModel._meta.get_field(field_name)
|
||||||
|
other_data[field_name] = field.clean(options[field_name], None)
|
||||||
|
else:
|
||||||
|
raise CommandError("You must use --%s with --noinput." % field_name)
|
||||||
|
except exceptions.ValidationError, e:
|
||||||
|
raise CommandError('; '.join(e.messages))
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Prompt for username/password, and any other required fields.
|
||||||
|
# Enclose this whole thing in a try/except to trap for a
|
||||||
|
# keyboard interrupt and exit gracefully.
|
||||||
default_username = get_default_username()
|
default_username = get_default_username()
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Get a username
|
# Get a username
|
||||||
while 1:
|
while username is None:
|
||||||
|
username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
|
||||||
if not username:
|
if not username:
|
||||||
input_msg = 'Username'
|
input_msg = capfirst(username_field.verbose_name)
|
||||||
if default_username:
|
if default_username:
|
||||||
input_msg += ' (leave blank to use %r)' % default_username
|
input_msg += ' (leave blank to use %r)' % default_username
|
||||||
username = raw_input(input_msg + ': ')
|
raw_value = raw_input(input_msg + ': ')
|
||||||
if default_username and username == '':
|
if default_username and raw_value == '':
|
||||||
username = default_username
|
username = default_username
|
||||||
if not RE_VALID_USERNAME.match(username):
|
try:
|
||||||
self.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.")
|
username = username_field.clean(raw_value, None)
|
||||||
|
except exceptions.ValidationError, e:
|
||||||
|
self.stderr.write("Error: %s" % '; '.join(e.messages))
|
||||||
username = None
|
username = None
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
User.objects.using(database).get(username=username)
|
UserModel.objects.using(database).get(**{
|
||||||
except User.DoesNotExist:
|
getattr(UserModel, 'USERNAME_FIELD', 'username'): username
|
||||||
break
|
})
|
||||||
|
except UserModel.DoesNotExist:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
self.stderr.write("Error: That username is already taken.")
|
self.stderr.write("Error: That username is already taken.")
|
||||||
username = None
|
username = None
|
||||||
|
|
||||||
# Get an email
|
for field_name in other_fields:
|
||||||
while 1:
|
field = UserModel._meta.get_field(field_name)
|
||||||
if not email:
|
other_data[field_name] = None
|
||||||
email = raw_input('E-mail address: ')
|
while other_data[field_name] is None:
|
||||||
|
raw_value = raw_input(capfirst(field.verbose_name + ': '))
|
||||||
try:
|
try:
|
||||||
is_valid_email(email)
|
other_data[field_name] = field.clean(raw_value, None)
|
||||||
except exceptions.ValidationError:
|
except exceptions.ValidationError, e:
|
||||||
self.stderr.write("Error: That e-mail address is invalid.")
|
self.stderr.write("Error: %s" % '; '.join(e.messages))
|
||||||
email = None
|
other_data[field_name] = None
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Get a password
|
# Get a password
|
||||||
while 1:
|
while password is None:
|
||||||
if not password:
|
if not password:
|
||||||
password = getpass.getpass()
|
password = getpass.getpass()
|
||||||
password2 = getpass.getpass('Password (again): ')
|
password2 = getpass.getpass('Password (again): ')
|
||||||
|
@ -116,12 +121,11 @@ class Command(BaseCommand):
|
||||||
self.stderr.write("Error: Blank passwords aren't allowed.")
|
self.stderr.write("Error: Blank passwords aren't allowed.")
|
||||||
password = None
|
password = None
|
||||||
continue
|
continue
|
||||||
break
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.stderr.write("\nOperation cancelled.")
|
self.stderr.write("\nOperation cancelled.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
User.objects.db_manager(database).create_superuser(username, email, password)
|
UserModel.objects.db_manager(database).create_superuser(username=username, password=password, **other_data)
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
self.stdout.write("Superuser created successfully.")
|
self.stdout.write("Superuser created successfully.")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
|
from django.core import validators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.manager import EmptyManager
|
from django.db.models.manager import EmptyManager
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
|
@ -128,7 +131,7 @@ class Group(models.Model):
|
||||||
return (self.name,)
|
return (self.name,)
|
||||||
|
|
||||||
|
|
||||||
class UserManager(models.Manager):
|
class BaseUserManager(models.Manager):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def normalize_email(cls, email):
|
def normalize_email(cls, email):
|
||||||
|
@ -145,6 +148,24 @@ class UserManager(models.Manager):
|
||||||
email = '@'.join([email_name, domain_part.lower()])
|
email = '@'.join([email_name, domain_part.lower()])
|
||||||
return email
|
return email
|
||||||
|
|
||||||
|
def make_random_password(self, length=10,
|
||||||
|
allowed_chars='abcdefghjkmnpqrstuvwxyz'
|
||||||
|
'ABCDEFGHJKLMNPQRSTUVWXYZ'
|
||||||
|
'23456789'):
|
||||||
|
"""
|
||||||
|
Generates a random password with the given length and given
|
||||||
|
allowed_chars. Note that the default value of allowed_chars does not
|
||||||
|
have "I" or "O" or letters and digits that look similar -- just to
|
||||||
|
avoid confusion.
|
||||||
|
"""
|
||||||
|
return get_random_string(length, allowed_chars)
|
||||||
|
|
||||||
|
def get_by_natural_key(self, username):
|
||||||
|
return self.get(**{getattr(self, 'USERNAME_FIELD', 'username'): username})
|
||||||
|
|
||||||
|
|
||||||
|
class UserManager(BaseUserManager):
|
||||||
|
|
||||||
def create_user(self, username, email=None, password=None):
|
def create_user(self, username, email=None, password=None):
|
||||||
"""
|
"""
|
||||||
Creates and saves a User with the given username, email and password.
|
Creates and saves a User with the given username, email and password.
|
||||||
|
@ -169,21 +190,6 @@ class UserManager(models.Manager):
|
||||||
u.save(using=self._db)
|
u.save(using=self._db)
|
||||||
return u
|
return u
|
||||||
|
|
||||||
def make_random_password(self, length=10,
|
|
||||||
allowed_chars='abcdefghjkmnpqrstuvwxyz'
|
|
||||||
'ABCDEFGHJKLMNPQRSTUVWXYZ'
|
|
||||||
'23456789'):
|
|
||||||
"""
|
|
||||||
Generates a random password with the given length and given
|
|
||||||
allowed_chars. Note that the default value of allowed_chars does not
|
|
||||||
have "I" or "O" or letters and digits that look similar -- just to
|
|
||||||
avoid confusion.
|
|
||||||
"""
|
|
||||||
return get_random_string(length, allowed_chars)
|
|
||||||
|
|
||||||
def get_by_natural_key(self, username):
|
|
||||||
return self.get(username=username)
|
|
||||||
|
|
||||||
|
|
||||||
# A few helper functions for common logic between User and AnonymousUser.
|
# A few helper functions for common logic between User and AnonymousUser.
|
||||||
def _user_get_all_permissions(user, obj):
|
def _user_get_all_permissions(user, obj):
|
||||||
|
@ -219,6 +225,7 @@ def _user_has_module_perms(user, app_label):
|
||||||
|
|
||||||
class AbstractBaseUser(models.Model):
|
class AbstractBaseUser(models.Model):
|
||||||
password = models.CharField(_('password'), max_length=128)
|
password = models.CharField(_('password'), max_length=128)
|
||||||
|
last_login = models.DateTimeField(_('last login'), default=timezone.now)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
@ -264,6 +271,12 @@ class AbstractBaseUser(models.Model):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_model():
|
||||||
|
"Return the User model that is active in this project"
|
||||||
|
app_label, model_name = settings.AUTH_USER_MODEL.split('.')
|
||||||
|
return models.get_model(app_label, model_name)
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractBaseUser):
|
class User(AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
Users within the Django authentication system are represented by this
|
Users within the Django authentication system are represented by this
|
||||||
|
@ -273,10 +286,13 @@ class User(AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
username = models.CharField(_('username'), max_length=30, unique=True,
|
username = models.CharField(_('username'), max_length=30, unique=True,
|
||||||
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
|
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
|
||||||
'@/./+/-/_ characters'))
|
'@/./+/-/_ characters'),
|
||||||
|
validators=[
|
||||||
|
validators.RegexValidator(re.compile('^[\w.@+-]+$'), _(u'Enter a valid username.'), 'invalid')
|
||||||
|
])
|
||||||
first_name = models.CharField(_('first name'), max_length=30, blank=True)
|
first_name = models.CharField(_('first name'), max_length=30, blank=True)
|
||||||
last_name = models.CharField(_('last name'), max_length=30, blank=True)
|
last_name = models.CharField(_('last name'), max_length=30, blank=True)
|
||||||
email = models.EmailField(_('e-mail address'), blank=True)
|
email = models.EmailField(_('email address'), blank=True)
|
||||||
is_staff = models.BooleanField(_('staff status'), default=False,
|
is_staff = models.BooleanField(_('staff status'), default=False,
|
||||||
help_text=_('Designates whether the user can log into this admin '
|
help_text=_('Designates whether the user can log into this admin '
|
||||||
'site.'))
|
'site.'))
|
||||||
|
@ -286,7 +302,6 @@ class User(AbstractBaseUser):
|
||||||
is_superuser = models.BooleanField(_('superuser status'), default=False,
|
is_superuser = models.BooleanField(_('superuser status'), default=False,
|
||||||
help_text=_('Designates that this user has all permissions without '
|
help_text=_('Designates that this user has all permissions without '
|
||||||
'explicitly assigning them.'))
|
'explicitly assigning them.'))
|
||||||
last_login = models.DateTimeField(_('last login'), default=timezone.now)
|
|
||||||
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
|
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
|
||||||
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
|
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
|
||||||
blank=True, help_text=_('The groups this user belongs to. A user will '
|
blank=True, help_text=_('The groups this user belongs to. A user will '
|
||||||
|
@ -295,6 +310,7 @@ class User(AbstractBaseUser):
|
||||||
user_permissions = models.ManyToManyField(Permission,
|
user_permissions = models.ManyToManyField(Permission,
|
||||||
verbose_name=_('user permissions'), blank=True,
|
verbose_name=_('user permissions'), blank=True,
|
||||||
help_text='Specific permissions for this user.')
|
help_text='Specific permissions for this user.')
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -318,6 +334,10 @@ class User(AbstractBaseUser):
|
||||||
full_name = u'%s %s' % (self.first_name, self.last_name)
|
full_name = u'%s %s' % (self.first_name, self.last_name)
|
||||||
return full_name.strip()
|
return full_name.strip()
|
||||||
|
|
||||||
|
def get_short_name(self):
|
||||||
|
"Returns the short name for the user."
|
||||||
|
return self.first_name
|
||||||
|
|
||||||
def get_group_permissions(self, obj=None):
|
def get_group_permissions(self, obj=None):
|
||||||
"""
|
"""
|
||||||
Returns a list of permission strings that this user has through his/her
|
Returns a list of permission strings that this user has through his/her
|
||||||
|
|
|
@ -1,26 +1,15 @@
|
||||||
from django.contrib.auth.tests.auth_backends import (BackendTest,
|
from django.contrib.auth.tests.custom_user import *
|
||||||
RowlevelBackendTest, AnonymousUserBackendTest, NoBackendsTest,
|
from django.contrib.auth.tests.auth_backends import *
|
||||||
InActiveUserBackendTest)
|
from django.contrib.auth.tests.basic import *
|
||||||
from django.contrib.auth.tests.basic import BasicTestCase
|
from django.contrib.auth.tests.context_processors import *
|
||||||
from django.contrib.auth.tests.context_processors import AuthContextProcessorTests
|
from django.contrib.auth.tests.decorators import *
|
||||||
from django.contrib.auth.tests.decorators import LoginRequiredTestCase
|
from django.contrib.auth.tests.forms import *
|
||||||
from django.contrib.auth.tests.forms import (UserCreationFormTest,
|
from django.contrib.auth.tests.remote_user import *
|
||||||
AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest,
|
from django.contrib.auth.tests.management import *
|
||||||
UserChangeFormTest, PasswordResetFormTest)
|
from django.contrib.auth.tests.models import *
|
||||||
from django.contrib.auth.tests.remote_user import (RemoteUserTest,
|
from django.contrib.auth.tests.hashers import *
|
||||||
RemoteUserNoCreateTest, RemoteUserCustomTest)
|
from django.contrib.auth.tests.signals import *
|
||||||
from django.contrib.auth.tests.management import (
|
from django.contrib.auth.tests.tokens import *
|
||||||
GetDefaultUsernameTestCase,
|
from django.contrib.auth.tests.views import *
|
||||||
ChangepasswordManagementCommandTestCase,
|
|
||||||
)
|
|
||||||
from django.contrib.auth.tests.models import (ProfileTestCase, NaturalKeysTestCase,
|
|
||||||
LoadDataWithoutNaturalKeysTestCase, LoadDataWithNaturalKeysTestCase,
|
|
||||||
UserManagerTestCase)
|
|
||||||
from django.contrib.auth.tests.hashers import TestUtilsHashPass
|
|
||||||
from django.contrib.auth.tests.signals import SignalTestCase
|
|
||||||
from django.contrib.auth.tests.tokens import TokenGeneratorTest
|
|
||||||
from django.contrib.auth.tests.views import (AuthViewNamedURLTests,
|
|
||||||
PasswordResetTest, ChangePasswordTest, LoginTest, LogoutTest,
|
|
||||||
LoginURLSettings)
|
|
||||||
|
|
||||||
# The password for the fixture data users is 'password'
|
# The password for the fixture data users is 'password'
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.unittest import skipUnless
|
|
||||||
from django.contrib.auth.models import User, AnonymousUser
|
from django.contrib.auth.models import User, AnonymousUser
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
try:
|
|
||||||
import crypt as crypt_module
|
|
||||||
except ImportError:
|
|
||||||
crypt_module = None
|
|
||||||
|
|
||||||
|
|
||||||
class BasicTestCase(TestCase):
|
class BasicTestCase(TestCase):
|
||||||
def test_user(self):
|
def test_user(self):
|
||||||
|
@ -35,7 +29,7 @@ class BasicTestCase(TestCase):
|
||||||
self.assertFalse(u.is_superuser)
|
self.assertFalse(u.is_superuser)
|
||||||
|
|
||||||
# Check API-based user creation with no password
|
# Check API-based user creation with no password
|
||||||
u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
User.objects.create_user('testuser2', 'test2@example.com')
|
||||||
self.assertFalse(u.has_usable_password())
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
def test_user_no_email(self):
|
def test_user_no_email(self):
|
||||||
|
@ -66,48 +60,3 @@ class BasicTestCase(TestCase):
|
||||||
self.assertTrue(super.is_superuser)
|
self.assertTrue(super.is_superuser)
|
||||||
self.assertTrue(super.is_active)
|
self.assertTrue(super.is_active)
|
||||||
self.assertTrue(super.is_staff)
|
self.assertTrue(super.is_staff)
|
||||||
|
|
||||||
def test_createsuperuser_management_command(self):
|
|
||||||
"Check the operation of the createsuperuser management command"
|
|
||||||
# We can use the management command to create a superuser
|
|
||||||
new_io = StringIO()
|
|
||||||
call_command("createsuperuser",
|
|
||||||
interactive=False,
|
|
||||||
username="joe",
|
|
||||||
email="joe@somewhere.org",
|
|
||||||
stdout=new_io
|
|
||||||
)
|
|
||||||
command_output = new_io.getvalue().strip()
|
|
||||||
self.assertEqual(command_output, 'Superuser created successfully.')
|
|
||||||
u = User.objects.get(username="joe")
|
|
||||||
self.assertEqual(u.email, 'joe@somewhere.org')
|
|
||||||
|
|
||||||
# created password should be unusable
|
|
||||||
self.assertFalse(u.has_usable_password())
|
|
||||||
|
|
||||||
# We can supress output on the management command
|
|
||||||
new_io = StringIO()
|
|
||||||
call_command("createsuperuser",
|
|
||||||
interactive=False,
|
|
||||||
username="joe2",
|
|
||||||
email="joe2@somewhere.org",
|
|
||||||
verbosity=0,
|
|
||||||
stdout=new_io
|
|
||||||
)
|
|
||||||
command_output = new_io.getvalue().strip()
|
|
||||||
self.assertEqual(command_output, '')
|
|
||||||
u = User.objects.get(username="joe2")
|
|
||||||
self.assertEqual(u.email, 'joe2@somewhere.org')
|
|
||||||
self.assertFalse(u.has_usable_password())
|
|
||||||
|
|
||||||
|
|
||||||
new_io = StringIO()
|
|
||||||
call_command("createsuperuser",
|
|
||||||
interactive=False,
|
|
||||||
username="joe+admin@somewhere.org",
|
|
||||||
email="joe@somewhere.org",
|
|
||||||
stdout=new_io
|
|
||||||
)
|
|
||||||
u = User.objects.get(username="joe+admin@somewhere.org")
|
|
||||||
self.assertEqual(u.email, 'joe@somewhere.org')
|
|
||||||
self.assertFalse(u.has_usable_password())
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
# The custom User uses email as the unique identifier, and requires
|
||||||
|
# that every user provide a date of birth. This lets us test
|
||||||
|
# changes in username datatype, and non-text required fields.
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
|
||||||
|
|
||||||
|
|
||||||
|
class CustomUserManager(BaseUserManager):
|
||||||
|
def create_user(self, email, date_of_birth, password=None):
|
||||||
|
"""
|
||||||
|
Creates and saves a User with the given email and password.
|
||||||
|
"""
|
||||||
|
if not email:
|
||||||
|
raise ValueError('Users must have an email address')
|
||||||
|
|
||||||
|
user = self.model(
|
||||||
|
email=CustomUserManager.normalize_email(email),
|
||||||
|
date_of_birth=date_of_birth,
|
||||||
|
)
|
||||||
|
|
||||||
|
user.set_password(password)
|
||||||
|
user.save(using=self._db)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def create_superuser(self, username, password, date_of_birth):
|
||||||
|
u = self.create_user(username, password=password, date_of_birth=date_of_birth)
|
||||||
|
u.is_admin = True
|
||||||
|
u.save(using=self._db)
|
||||||
|
return u
|
||||||
|
|
||||||
|
|
||||||
|
class CustomUser(AbstractBaseUser):
|
||||||
|
email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
|
||||||
|
is_admin = models.BooleanField(default=False)
|
||||||
|
date_of_birth = models.DateField()
|
||||||
|
|
||||||
|
objects = CustomUserManager()
|
||||||
|
|
||||||
|
USERNAME_FIELD = 'email'
|
||||||
|
REQUIRED_FIELDS = ['date_of_birth']
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'auth'
|
||||||
|
|
||||||
|
def get_full_name(self):
|
||||||
|
return self.email
|
||||||
|
|
||||||
|
def get_short_name(self):
|
||||||
|
return self.email
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.email
|
||||||
|
|
||||||
|
# Maybe required?
|
||||||
|
def get_group_permissions(self, obj=None):
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def get_all_permissions(self, obj=None):
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def has_perm(self, perm, obj=None):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def has_perms(self, perm_list, obj=None):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def has_module_perms(self, app_label):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Admin required fields
|
||||||
|
@property
|
||||||
|
def is_staff(self):
|
||||||
|
return self.is_admin
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self):
|
||||||
|
return True
|
|
@ -1,9 +1,14 @@
|
||||||
|
from datetime import date
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
from django.contrib.auth import models, management
|
from django.contrib.auth import models, management
|
||||||
from django.contrib.auth.management.commands import changepassword
|
from django.contrib.auth.management.commands import changepassword
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth.tests import CustomUser
|
||||||
|
from django.core.management import call_command
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
|
|
||||||
class GetDefaultUsernameTestCase(TestCase):
|
class GetDefaultUsernameTestCase(TestCase):
|
||||||
|
@ -64,3 +69,92 @@ class ChangepasswordManagementCommandTestCase(TestCase):
|
||||||
|
|
||||||
with self.assertRaises(CommandError):
|
with self.assertRaises(CommandError):
|
||||||
command.execute("joe", stdout=self.stdout, stderr=self.stderr)
|
command.execute("joe", stdout=self.stdout, stderr=self.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
class CreatesuperuserManagementCommandTestCase(TestCase):
|
||||||
|
|
||||||
|
def test_createsuperuser(self):
|
||||||
|
"Check the operation of the createsuperuser management command"
|
||||||
|
# We can use the management command to create a superuser
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe",
|
||||||
|
email="joe@somewhere.org",
|
||||||
|
stdout=new_io
|
||||||
|
)
|
||||||
|
command_output = new_io.getvalue().strip()
|
||||||
|
self.assertEqual(command_output, 'Superuser created successfully.')
|
||||||
|
u = User.objects.get(username="joe")
|
||||||
|
self.assertEqual(u.email, 'joe@somewhere.org')
|
||||||
|
|
||||||
|
# created password should be unusable
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
|
def test_verbosity_zero(self):
|
||||||
|
# We can supress output on the management command
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe2",
|
||||||
|
email="joe2@somewhere.org",
|
||||||
|
verbosity=0,
|
||||||
|
stdout=new_io
|
||||||
|
)
|
||||||
|
command_output = new_io.getvalue().strip()
|
||||||
|
self.assertEqual(command_output, '')
|
||||||
|
u = User.objects.get(username="joe2")
|
||||||
|
self.assertEqual(u.email, 'joe2@somewhere.org')
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
|
def test_email_in_username(self):
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe+admin@somewhere.org",
|
||||||
|
email="joe@somewhere.org",
|
||||||
|
stdout=new_io
|
||||||
|
)
|
||||||
|
u = User.objects.get(username="joe+admin@somewhere.org")
|
||||||
|
self.assertEqual(u.email, 'joe@somewhere.org')
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
|
@override_settings(AUTH_USER_MODEL='auth.CustomUser')
|
||||||
|
def test_swappable_user(self):
|
||||||
|
"A superuser can be created when a custom User model is in use"
|
||||||
|
# We can use the management command to create a superuser
|
||||||
|
# We skip validation because the temporary substitution of the
|
||||||
|
# swappable User model messes with validation.
|
||||||
|
new_io = StringIO()
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe@somewhere.org",
|
||||||
|
date_of_birth="1976-04-01",
|
||||||
|
stdout=new_io,
|
||||||
|
skip_validation=True
|
||||||
|
)
|
||||||
|
command_output = new_io.getvalue().strip()
|
||||||
|
self.assertEqual(command_output, 'Superuser created successfully.')
|
||||||
|
u = CustomUser.objects.get(email="joe@somewhere.org")
|
||||||
|
self.assertEqual(u.date_of_birth, date(1976, 4, 1))
|
||||||
|
|
||||||
|
# created password should be unusable
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
|
@override_settings(AUTH_USER_MODEL='auth.CustomUser')
|
||||||
|
def test_swappable_user_missing_required_field(self):
|
||||||
|
"A superuser can be created when a custom User model is in use"
|
||||||
|
# We can use the management command to create a superuser
|
||||||
|
# We skip validation because the temporary substitution of the
|
||||||
|
# swappable User model messes with validation.
|
||||||
|
new_io = StringIO()
|
||||||
|
with self.assertRaises(CommandError):
|
||||||
|
call_command("createsuperuser",
|
||||||
|
interactive=False,
|
||||||
|
username="joe@somewhere.org",
|
||||||
|
stdout=new_io,
|
||||||
|
stderr=new_io,
|
||||||
|
skip_validation=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(CustomUser.objects.count(), 0)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.conf import settings
|
||||||
from django.utils.http import int_to_base36, base36_to_int
|
from django.utils.http import int_to_base36, base36_to_int
|
||||||
from django.utils.crypto import constant_time_compare, salted_hmac
|
from django.utils.crypto import constant_time_compare, salted_hmac
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetTokenGenerator(object):
|
class PasswordResetTokenGenerator(object):
|
||||||
"""
|
"""
|
||||||
Strategy object used to generate and check tokens for the password
|
Strategy object used to generate and check tokens for the password
|
||||||
|
|
|
@ -3,42 +3,54 @@ Global Django exception and warning classes.
|
||||||
"""
|
"""
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
|
|
||||||
class DjangoRuntimeWarning(RuntimeWarning):
|
class DjangoRuntimeWarning(RuntimeWarning):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ObjectDoesNotExist(Exception):
|
class ObjectDoesNotExist(Exception):
|
||||||
"The requested object does not exist"
|
"The requested object does not exist"
|
||||||
silent_variable_failure = True
|
silent_variable_failure = True
|
||||||
|
|
||||||
|
|
||||||
class MultipleObjectsReturned(Exception):
|
class MultipleObjectsReturned(Exception):
|
||||||
"The query returned multiple objects when only one was expected."
|
"The query returned multiple objects when only one was expected."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SuspiciousOperation(Exception):
|
class SuspiciousOperation(Exception):
|
||||||
"The user did something suspicious"
|
"The user did something suspicious"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PermissionDenied(Exception):
|
class PermissionDenied(Exception):
|
||||||
"The user did not have permission to do that"
|
"The user did not have permission to do that"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ViewDoesNotExist(Exception):
|
class ViewDoesNotExist(Exception):
|
||||||
"The requested view does not exist"
|
"The requested view does not exist"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MiddlewareNotUsed(Exception):
|
class MiddlewareNotUsed(Exception):
|
||||||
"This middleware is not used in this server configuration"
|
"This middleware is not used in this server configuration"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ImproperlyConfigured(Exception):
|
class ImproperlyConfigured(Exception):
|
||||||
"Django is somehow improperly configured"
|
"Django is somehow improperly configured"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FieldError(Exception):
|
class FieldError(Exception):
|
||||||
"""Some kind of problem with a model field."""
|
"""Some kind of problem with a model field."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
NON_FIELD_ERRORS = '__all__'
|
NON_FIELD_ERRORS = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class ValidationError(Exception):
|
class ValidationError(Exception):
|
||||||
"""An error while validating data."""
|
"""An error while validating data."""
|
||||||
def __init__(self, message, code=None, params=None):
|
def __init__(self, message, code=None, params=None):
|
||||||
|
@ -85,4 +97,3 @@ class ValidationError(Exception):
|
||||||
else:
|
else:
|
||||||
error_dict[NON_FIELD_ERRORS] = self.messages
|
error_dict[NON_FIELD_ERRORS] = self.messages
|
||||||
return error_dict
|
return error_dict
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.utils.ipv6 import is_valid_ipv6_address
|
||||||
# These values, if given to validate(), will trigger the self.required check.
|
# These values, if given to validate(), will trigger the self.required check.
|
||||||
EMPTY_VALUES = (None, '', [], (), {})
|
EMPTY_VALUES = (None, '', [], (), {})
|
||||||
|
|
||||||
|
|
||||||
class RegexValidator(object):
|
class RegexValidator(object):
|
||||||
regex = ''
|
regex = ''
|
||||||
message = _(u'Enter a valid value.')
|
message = _(u'Enter a valid value.')
|
||||||
|
@ -33,6 +34,7 @@ class RegexValidator(object):
|
||||||
if not self.regex.search(smart_unicode(value)):
|
if not self.regex.search(smart_unicode(value)):
|
||||||
raise ValidationError(self.message, code=self.code)
|
raise ValidationError(self.message, code=self.code)
|
||||||
|
|
||||||
|
|
||||||
class URLValidator(RegexValidator):
|
class URLValidator(RegexValidator):
|
||||||
regex = re.compile(
|
regex = re.compile(
|
||||||
r'^(?:http|ftp)s?://' # http:// or https://
|
r'^(?:http|ftp)s?://' # http:// or https://
|
||||||
|
@ -68,6 +70,7 @@ def validate_integer(value):
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
raise ValidationError('')
|
raise ValidationError('')
|
||||||
|
|
||||||
|
|
||||||
class EmailValidator(RegexValidator):
|
class EmailValidator(RegexValidator):
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
|
@ -99,10 +102,12 @@ validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of l
|
||||||
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
|
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
|
||||||
validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
|
validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
|
||||||
|
|
||||||
|
|
||||||
def validate_ipv6_address(value):
|
def validate_ipv6_address(value):
|
||||||
if not is_valid_ipv6_address(value):
|
if not is_valid_ipv6_address(value):
|
||||||
raise ValidationError(_(u'Enter a valid IPv6 address.'), code='invalid')
|
raise ValidationError(_(u'Enter a valid IPv6 address.'), code='invalid')
|
||||||
|
|
||||||
|
|
||||||
def validate_ipv46_address(value):
|
def validate_ipv46_address(value):
|
||||||
try:
|
try:
|
||||||
validate_ipv4_address(value)
|
validate_ipv4_address(value)
|
||||||
|
@ -118,6 +123,7 @@ ip_address_validator_map = {
|
||||||
'ipv6': ([validate_ipv6_address], _('Enter a valid IPv6 address.')),
|
'ipv6': ([validate_ipv6_address], _('Enter a valid IPv6 address.')),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def ip_address_validators(protocol, unpack_ipv4):
|
def ip_address_validators(protocol, unpack_ipv4):
|
||||||
"""
|
"""
|
||||||
Depending on the given parameters returns the appropriate validators for
|
Depending on the given parameters returns the appropriate validators for
|
||||||
|
@ -157,25 +163,28 @@ class BaseValidator(object):
|
||||||
params=params,
|
params=params,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MaxValueValidator(BaseValidator):
|
class MaxValueValidator(BaseValidator):
|
||||||
compare = lambda self, a, b: a > b
|
compare = lambda self, a, b: a > b
|
||||||
message = _(u'Ensure this value is less than or equal to %(limit_value)s.')
|
message = _(u'Ensure this value is less than or equal to %(limit_value)s.')
|
||||||
code = 'max_value'
|
code = 'max_value'
|
||||||
|
|
||||||
|
|
||||||
class MinValueValidator(BaseValidator):
|
class MinValueValidator(BaseValidator):
|
||||||
compare = lambda self, a, b: a < b
|
compare = lambda self, a, b: a < b
|
||||||
message = _(u'Ensure this value is greater than or equal to %(limit_value)s.')
|
message = _(u'Ensure this value is greater than or equal to %(limit_value)s.')
|
||||||
code = 'min_value'
|
code = 'min_value'
|
||||||
|
|
||||||
|
|
||||||
class MinLengthValidator(BaseValidator):
|
class MinLengthValidator(BaseValidator):
|
||||||
compare = lambda self, a, b: a < b
|
compare = lambda self, a, b: a < b
|
||||||
clean = lambda self, x: len(x)
|
clean = lambda self, x: len(x)
|
||||||
message = _(u'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).')
|
message = _(u'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).')
|
||||||
code = 'min_length'
|
code = 'min_length'
|
||||||
|
|
||||||
|
|
||||||
class MaxLengthValidator(BaseValidator):
|
class MaxLengthValidator(BaseValidator):
|
||||||
compare = lambda self, a, b: a > b
|
compare = lambda self, a, b: a > b
|
||||||
clean = lambda self, x: len(x)
|
clean = lambda self, x: len(x)
|
||||||
message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).')
|
message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).')
|
||||||
code = 'max_length'
|
code = 'max_length'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue