diff options
86 files changed, 7202 insertions, 6410 deletions
@@ -49,6 +49,7 @@ answer newbie questions, and generally made Django that much better: andy@jadedplanet.net Fabrice Aneche <akh@nobugware.com> ant9000@netwise.it + Florian Apolloner David Ascher <http://ascher.ca/> david@kazserve.org Arthur <avandorp@gmail.com> @@ -244,7 +245,6 @@ answer newbie questions, and generally made Django that much better: phil@produxion.net phil.h.smith@gmail.com Gustavo Picon - pigletto Luke Plant <http://lukeplant.me.uk/> plisk Daniel Poelzleithner <http://poelzi.org/> @@ -281,6 +281,7 @@ answer newbie questions, and generally made Django that much better: sopel Leo Soto <leo.soto@gmail.com> Wiliam Alves de Souza <wiliamsouza83@gmail.com> + Don Spaulding <donspauldingii@gmail.com> Bjørn Stabell <bjorn@exoweb.net> Georgi Stanojevski <glisha@gmail.com> Vasiliy Stavenko <stavenko@gmail.com> @@ -325,6 +326,7 @@ answer newbie questions, and generally made Django that much better: Rachel Willmer <http://www.willmer.com/kb/> Gary Wilson <gary.wilson@gmail.com> Jakub Wiśniowski <restless.being@gmail.com> + Maciej Wiśniowski <pigletto@gmail.com> wojtek ye7cakf02@sneakemail.com ymasuda@ethercube.com diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py index 44ee2e55dd..f9cd20966e 100644 --- a/django/contrib/admin/templatetags/admin_modify.py +++ b/django/contrib/admin/templatetags/admin_modify.py @@ -40,7 +40,7 @@ class FieldWidgetNode(template.Node): default = None def __init__(self, bound_field_var): - self.bound_field_var = bound_field_var + self.bound_field_var = template.Variable(bound_field_var) def get_nodelist(cls, klass): if klass not in cls.nodelists: @@ -64,7 +64,7 @@ class FieldWidgetNode(template.Node): get_nodelist = classmethod(get_nodelist) def render(self, context): - bound_field = template.resolve_variable(self.bound_field_var, context) + bound_field = self.bound_field_var.resolve(context) context.push() context['bound_field'] = bound_field diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py index 4b8efcca46..be6cfede11 100644 --- a/django/contrib/auth/backends.py +++ b/django/contrib/auth/backends.py @@ -1,3 +1,4 @@ +from django.db import connection from django.contrib.auth.models import User class ModelBackend: @@ -14,6 +15,49 @@ class ModelBackend: except User.DoesNotExist: return None + def get_group_permissions(self, user_obj): + "Returns a list of permission strings that this user has through his/her groups." + if not hasattr(user_obj, '_group_perm_cache'): + cursor = connection.cursor() + # The SQL below works out to the following, after DB quoting: + # cursor.execute(""" + # SELECT ct."app_label", p."codename" + # FROM "auth_permission" p, "auth_group_permissions" gp, "auth_user_groups" ug, "django_content_type" ct + # WHERE p."id" = gp."permission_id" + # AND gp."group_id" = ug."group_id" + # AND ct."id" = p."content_type_id" + # AND ug."user_id" = %s, [self.id]) + qn = connection.ops.quote_name + sql = """ + SELECT ct.%s, p.%s + FROM %s p, %s gp, %s ug, %s ct + WHERE p.%s = gp.%s + AND gp.%s = ug.%s + AND ct.%s = p.%s + AND ug.%s = %%s""" % ( + qn('app_label'), qn('codename'), + qn('auth_permission'), qn('auth_group_permissions'), + qn('auth_user_groups'), qn('django_content_type'), + qn('id'), qn('permission_id'), + qn('group_id'), qn('group_id'), + qn('id'), qn('content_type_id'), + qn('user_id'),) + cursor.execute(sql, [user_obj.id]) + user_obj._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()]) + return user_obj._group_perm_cache + + def get_all_permissions(self, user_obj): + if not hasattr(user_obj, '_perm_cache'): + user_obj._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()]) + user_obj._perm_cache.update(self.get_group_permissions(user_obj)) + return user_obj._perm_cache + + def has_perm(self, user_obj, perm): + return perm in self.get_all_permissions(user_obj) + + def has_module_perms(self, user_obj, app_label): + return bool(len([p for p in self.get_all_permissions(user_obj) if p[:p.index('.')] == app_label])) + def get_user(self, user_id): try: return User.objects.get(pk=user_id) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index baf7e6e210..62e4a8bc59 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -1,6 +1,7 @@ +from django.contrib import auth from django.core import validators from django.core.exceptions import ImproperlyConfigured -from django.db import connection, models +from django.db import models from django.db.models.manager import EmptyManager from django.contrib.contenttypes.models import ContentType from django.utils.encoding import smart_str @@ -195,64 +196,68 @@ class User(models.Model): return self.password != UNUSABLE_PASSWORD def get_group_permissions(self): - "Returns a list of permission strings that this user has through his/her groups." - if not hasattr(self, '_group_perm_cache'): - cursor = connection.cursor() - # The SQL below works out to the following, after DB quoting: - # cursor.execute(""" - # SELECT ct."app_label", p."codename" - # FROM "auth_permission" p, "auth_group_permissions" gp, "auth_user_groups" ug, "django_content_type" ct - # WHERE p."id" = gp."permission_id" - # AND gp."group_id" = ug."group_id" - # AND ct."id" = p."content_type_id" - # AND ug."user_id" = %s, [self.id]) - qn = connection.ops.quote_name - sql = """ - SELECT ct.%s, p.%s - FROM %s p, %s gp, %s ug, %s ct - WHERE p.%s = gp.%s - AND gp.%s = ug.%s - AND ct.%s = p.%s - AND ug.%s = %%s""" % ( - qn('app_label'), qn('codename'), - qn('auth_permission'), qn('auth_group_permissions'), - qn('auth_user_groups'), qn('django_content_type'), - qn('id'), qn('permission_id'), - qn('group_id'), qn('group_id'), - qn('id'), qn('content_type_id'), - qn('user_id'),) - cursor.execute(sql, [self.id]) - self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()]) - return self._group_perm_cache + """ + Returns a list of permission strings that this user has through + his/her groups. This method queries all available auth backends. + """ + permissions = set() + for backend in auth.get_backends(): + if hasattr(backend, "get_group_permissions"): + permissions.update(backend.get_group_permissions(self)) + return permissions def get_all_permissions(self): - if not hasattr(self, '_perm_cache'): - self._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in self.user_permissions.select_related()]) - self._perm_cache.update(self.get_group_permissions()) - return self._perm_cache + permissions = set() + for backend in auth.get_backends(): + if hasattr(backend, "get_all_permissions"): + permissions.update(backend.get_all_permissions(self)) + return permissions def has_perm(self, perm): - "Returns True if the user has the specified permission." + """ + Returns True if the user has the specified permission. This method + queries all available auth backends, but returns immediately if any + backend returns True. Thus, a user who has permission from a single + auth backend is assumed to have permission in general. + """ + # Inactive users have no permissions. if not self.is_active: return False + + # Superusers have all permissions. if self.is_superuser: return True - return perm in self.get_all_permissions() + + # Otherwise we need to check the backends. + for backend in auth.get_backends(): + if hasattr(backend, "has_perm"): + if backend.has_perm(self, perm): + return True + return False def has_perms(self, perm_list): - "Returns True if the user has each of the specified permissions." + """Returns True if the user has each of the specified permissions.""" for perm in perm_list: if not self.has_perm(perm): return False return True def has_module_perms(self, app_label): - "Returns True if the user has any permissions in the given app label." + """ + Returns True if the user has any permissions in the given app + label. Uses pretty much the same logic as has_perm, above. + """ if not self.is_active: return False + if self.is_superuser: return True - return bool(len([p for p in self.get_all_permissions() if p[:p.index('.')] == app_label])) + + for backend in auth.get_backends(): + if hasattr(backend, "has_module_perms"): + if backend.has_module_perms(self, app_label): + return True + return False def get_and_delete_messages(self): messages = [] @@ -285,7 +290,12 @@ class User(models.Model): class Message(models.Model): """ - The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message. + The message system is a lightweight way to queue messages for given + users. A message is associated with a User instance (so it is only + applicable for registered users). There's no concept of expiration or + timestamps. Messages are created by the Django admin after successful + actions. For example, "The poll Foo was created successfully." is a + message. """ user = models.ForeignKey(User) message = models.TextField(_('message')) diff --git a/django/contrib/comments/templatetags/comments.py b/django/contrib/comments/templatetags/comments.py index 1d4628978d..959cec4c7f 100644 --- a/django/contrib/comments/templatetags/comments.py +++ b/django/contrib/comments/templatetags/comments.py @@ -19,6 +19,8 @@ class CommentFormNode(template.Node): ratings_optional=False, ratings_required=False, rating_options='', is_public=True): self.content_type = content_type + if obj_id_lookup_var is not None: + obj_id_lookup_var = template.Variable(obj_id_lookup_var) self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free self.photos_optional, self.photos_required = photos_optional, photos_required self.ratings_optional, self.ratings_required = ratings_optional, ratings_required @@ -32,7 +34,7 @@ class CommentFormNode(template.Node): context.push() if self.obj_id_lookup_var is not None: try: - self.obj_id = template.resolve_variable(self.obj_id_lookup_var, context) + self.obj_id = self.obj_id_lookup_var.resolve(context) except template.VariableDoesNotExist: return '' # Validate that this object ID is valid for this content-type. @@ -75,6 +77,8 @@ class CommentFormNode(template.Node): class CommentCountNode(template.Node): def __init__(self, package, module, context_var_name, obj_id, var_name, free): self.package, self.module = package, module + if context_var_name is not None: + context_var_name = template.Variable(context_var_name) self.context_var_name, self.obj_id = context_var_name, obj_id self.var_name, self.free = var_name, free @@ -82,7 +86,7 @@ class CommentCountNode(template.Node): from django.conf import settings manager = self.free and FreeComment.objects or Comment.objects if self.context_var_name is not None: - self.obj_id = template.resolve_variable(self.context_var_name, context) + self.obj_id = self.context_var_name.resolve(context) comment_count = manager.filter(object_id__exact=self.obj_id, content_type__app_label__exact=self.package, content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count() @@ -92,6 +96,8 @@ class CommentCountNode(template.Node): class CommentListNode(template.Node): def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None): self.package, self.module = package, module + if context_var_name is not None: + context_var_name = template.Variable(context_var_name) self.context_var_name, self.obj_id = context_var_name, obj_id self.var_name, self.free = var_name, free self.ordering = ordering @@ -102,7 +108,7 @@ class CommentListNode(template.Node): get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma if self.context_var_name is not None: try: - self.obj_id = template.resolve_variable(self.context_var_name, context) + self.obj_id = self.context_var_name.resolve(context) except template.VariableDoesNotExist: return '' kwargs = { diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 382212bb70..e6be0482de 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -16,7 +16,6 @@ class SessionBase(object): """ Base class for all Session classes. """ - TEST_COOKIE_NAME = 'testcookie' TEST_COOKIE_VALUE = 'worked' @@ -59,7 +58,7 @@ class SessionBase(object): def delete_test_cookie(self): del self[self.TEST_COOKIE_NAME] - + def encode(self, session_dict): "Returns the given session dictionary pickled and encoded as a string." pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL) @@ -77,28 +76,33 @@ class SessionBase(object): # just return an empty dictionary (an empty session). except: return {} - + def _get_new_session_key(self): "Returns session key that isn't being used." # The random module is seeded when this Apache child is created. # Use settings.SECRET_KEY as added salt. + try: + pid = os.getpid() + except AttributeError: + # No getpid() in Jython, for example + pid = 1 while 1: - session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), - os.getpid(), time.time(), settings.SECRET_KEY)).hexdigest() + session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), + pid, time.time(), settings.SECRET_KEY)).hexdigest() if not self.exists(session_key): break return session_key - + def _get_session_key(self): if self._session_key: return self._session_key else: self._session_key = self._get_new_session_key() return self._session_key - + def _set_session_key(self, session_key): self._session_key = session_key - + session_key = property(_get_session_key, _set_session_key) def _get_session(self): @@ -114,9 +118,9 @@ class SessionBase(object): return self._session_cache _session = property(_get_session) - + # Methods that child classes must implement. - + def exists(self, session_key): """ Returns True if the given session_key already exists. @@ -140,4 +144,3 @@ class SessionBase(object): Loads the session data and returns a dictionary. """ raise NotImplementedError - diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index a4731652f5..e15dfccdf2 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -1,18 +1,104 @@ import django +from django.core.management.base import BaseCommand, CommandError, handle_default_options from optparse import OptionParser import os import sys +from imp import find_module # For backwards compatibility: get_version() used to be in this module. get_version = django.get_version -def load_command_class(name): +# A cache of loaded commands, so that call_command +# doesn't have to reload every time it is called +_commands = None + +def find_commands(management_dir): + """ + Given a path to a management directory, return a list of all the command names + that are available. Returns an empty list if no commands are defined. + """ + command_dir = os.path.join(management_dir,'commands') + try: + return [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] + except OSError: + return [] + +def find_management_module(app_name): """ - Given a command name, returns the Command class instance. Raises - ImportError if it doesn't exist. + Determine the path to the management module for the application named, + without acutally importing the application or the management module. + + Raises ImportError if the management module cannot be found for any reason. + """ + parts = app_name.split('.') + parts.append('management') + parts.reverse() + path = None + while parts: + part = parts.pop() + f,path,descr = find_module(part, path and [path] or None) + return path + +def load_command_class(app_name, name): """ - # Let the ImportError propogate. - return getattr(__import__('django.core.management.commands.%s' % name, {}, {}, ['Command']), 'Command')() + Given a command name and an application name, returns the Command + class instance. All errors raised by the importation process + (ImportError, AttributeError) are allowed to propagate. + """ + return getattr(__import__('%s.management.commands.%s' % (app_name, name), + {}, {}, ['Command']), 'Command')() + +def get_commands(load_user_commands=True, project_directory=None): + """ + Returns a dictionary of commands against the application in which + those commands can be found. This works by looking for a + management.commands package in django.core, and in each installed + application -- if a commands package exists, all commands in that + package are registered. + + Core commands are always included; user-defined commands will also + be included if ``load_user_commands`` is True. If a project directory + is provided, the startproject command will be disabled, and the + startapp command will be modified to use that directory. + + The dictionary is in the format {command_name: app_name}. Key-value + pairs from this dictionary can then be used in calls to + load_command_class(app_name, command_name) + + If a specific version of a command must be loaded (e.g., with the + startapp command), the instantiated module can be placed in the + dictionary in place of the application name. + + The dictionary is cached on the first call, and reused on subsequent + calls. + """ + global _commands + if _commands is None: + _commands = dict([(name, 'django.core') + for name in find_commands(__path__[0])]) + if load_user_commands: + # Get commands from all installed apps + from django.conf import settings + for app_name in settings.INSTALLED_APPS: + try: + path = find_management_module(app_name) + _commands.update(dict([(name, app_name) + for name in find_commands(path)])) + except ImportError: + pass # No management module - ignore this app + + if project_directory: + # Remove the "startproject" command from self.commands, because + # that's a django-admin.py command, not a manage.py command. + del _commands['startproject'] + + # Override the startapp command so that it always uses the + # project_directory, not the current working directory + # (which is default). + from django.core.management.commands.startapp import ProjectCommand + _commands['startapp'] = ProjectCommand(project_directory) + + return _commands def call_command(name, *args, **options): """ @@ -25,8 +111,26 @@ def call_command(name, *args, **options): call_command('shell', plain=True) call_command('sqlall', 'myapp') """ - klass = load_command_class(name) + try: + app_name = get_commands()[name] + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, name) + except KeyError: + raise CommandError, "Unknown command: %r" % name return klass.execute(*args, **options) + +class LaxOptionParser(OptionParser): + """ + An option parser that doesn't raise any errors on unknown options. + + This is needed because the --settings and --pythonpath options affect + the commands (and thus the options) that are available to the user. + """ + def error(self, msg): + pass class ManagementUtility(object): """ @@ -38,21 +142,9 @@ class ManagementUtility(object): def __init__(self, argv=None): self.argv = argv or sys.argv[:] self.prog_name = os.path.basename(self.argv[0]) - self.commands = self.default_commands() - - def default_commands(self): - """ - Returns a dictionary of instances of all available Command classes. - - This works by looking for and loading all Python modules in the - django.core.management.commands package. - - The dictionary is in the format {name: command_instance}. - """ - command_dir = os.path.join(__path__[0], 'commands') - names = [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] - return dict([(name, load_command_class(name)) for name in names]) - + self.project_directory = None + self.user_commands = False + def main_help_text(self): """ Returns the script's main help text, as a string. @@ -61,7 +153,7 @@ class ManagementUtility(object): usage.append('Django command line tool, version %s' % django.get_version()) usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name) usage.append('Available subcommands:') - commands = self.commands.keys() + commands = get_commands(self.user_commands, self.project_directory).keys() commands.sort() for cmd in commands: usage.append(' %s' % cmd) @@ -74,16 +166,33 @@ class ManagementUtility(object): django-admin.py or manage.py) if it can't be found. """ try: - return self.commands[subcommand] + app_name = get_commands(self.user_commands, self.project_directory)[subcommand] + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, subcommand) except KeyError: sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % (subcommand, self.prog_name)) sys.exit(1) - + return klass + def execute(self): """ Given the command-line arguments, this figures out which subcommand is being run, creates a parser appropriate to that command, and runs it. """ + # Preprocess options to extract --settings and --pythonpath. These options + # could affect the commands that are available, so they must be processed + # early + parser = LaxOptionParser(version=get_version(), + option_list=BaseCommand.option_list) + try: + options, args = parser.parse_args(self.argv) + handle_default_options(options) + except: + pass # Ignore any option errors at this point. + try: subcommand = self.argv[1] except IndexError: @@ -91,8 +200,8 @@ class ManagementUtility(object): sys.exit(1) if subcommand == 'help': - if len(self.argv) > 2: - self.fetch_command(self.argv[2]).print_help(self.prog_name, self.argv[2]) + if len(args) > 2: + self.fetch_command(args[2]).print_help(self.prog_name, args[2]) else: sys.stderr.write(self.main_help_text() + '\n') sys.exit(1) @@ -116,16 +225,9 @@ class ProjectManagementUtility(ManagementUtility): """ def __init__(self, argv, project_directory): super(ProjectManagementUtility, self).__init__(argv) - - # Remove the "startproject" command from self.commands, because - # that's a django-admin.py command, not a manage.py command. - del self.commands['startproject'] - - # Override the startapp command so that it always uses the - # project_directory, not the current working directory (which is default). - from django.core.management.commands.startapp import ProjectCommand - self.commands['startapp'] = ProjectCommand(project_directory) - + self.project_directory = project_directory + self.user_commands = True + def setup_environ(settings_mod): """ Configure the runtime environment. This can also be used by external diff --git a/django/core/management/base.py b/django/core/management/base.py index d883fe23dc..26ecef4ff5 100644 --- a/django/core/management/base.py +++ b/django/core/management/base.py @@ -9,6 +9,17 @@ import os class CommandError(Exception): pass +def handle_default_options(options): + """ + Include any default options that all commands should accept + here so that ManagementUtility can handle them before searching + for user commands. + """ + if options.settings: + os.environ['DJANGO_SETTINGS_MODULE'] = options.settings + if options.pythonpath: + sys.path.insert(0, options.pythonpath) + class BaseCommand(object): # Metadata about this command. option_list = ( @@ -55,10 +66,7 @@ class BaseCommand(object): def run_from_argv(self, argv): parser = self.create_parser(argv[0], argv[1]) options, args = parser.parse_args(argv[2:]) - if options.settings: - os.environ['DJANGO_SETTINGS_MODULE'] = options.settings - if options.pythonpath: - sys.path.insert(0, options.pythonpath) + handle_default_options(options) self.execute(*args, **options.__dict__) def execute(self, *args, **options): diff --git a/django/db/backends/ado_mssql/creation.py b/django/db/backends/ado_mssql/creation.py index 1411ca4d6a..d4ba8f2897 100644 --- a/django/db/backends/ado_mssql/creation.py +++ b/django/db/backends/ado_mssql/creation.py @@ -6,10 +6,10 @@ DATA_TYPES = { 'DateField': 'smalldatetime', 'DateTimeField': 'smalldatetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', - 'ImageField': 'varchar(100)', + 'ImageField': 'varchar(%(max_length)s)', 'IntegerField': 'int', 'IPAddressField': 'char(15)', 'NullBooleanField': 'bit', diff --git a/django/db/backends/mysql/creation.py b/django/db/backends/mysql/creation.py index b2b3992651..efb351c07e 100644 --- a/django/db/backends/mysql/creation.py +++ b/django/db/backends/mysql/creation.py @@ -10,10 +10,10 @@ DATA_TYPES = { 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', - 'ImageField': 'varchar(100)', + 'ImageField': 'varchar(%(max_length)s)', 'IntegerField': 'integer', 'IPAddressField': 'char(15)', 'NullBooleanField': 'bool', diff --git a/django/db/backends/mysql_old/creation.py b/django/db/backends/mysql_old/creation.py index b2b3992651..efb351c07e 100644 --- a/django/db/backends/mysql_old/creation.py +++ b/django/db/backends/mysql_old/creation.py @@ -10,10 +10,10 @@ DATA_TYPES = { 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', - 'ImageField': 'varchar(100)', + 'ImageField': 'varchar(%(max_length)s)', 'IntegerField': 'integer', 'IPAddressField': 'char(15)', 'NullBooleanField': 'bool', diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index d080b5d283..f4ada55ac6 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -13,10 +13,10 @@ DATA_TYPES = { 'DateField': 'DATE', 'DateTimeField': 'TIMESTAMP', 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'NVARCHAR2(100)', - 'FilePathField': 'NVARCHAR2(100)', + 'FileField': 'NVARCHAR2(%(max_length)s)', + 'FilePathField': 'NVARCHAR2(%(max_length)s)', 'FloatField': 'DOUBLE PRECISION', - 'ImageField': 'NVARCHAR2(100)', + 'ImageField': 'NVARCHAR2(%(max_length)s)', 'IntegerField': 'NUMBER(11)', 'IPAddressField': 'VARCHAR2(15)', 'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', @@ -28,7 +28,7 @@ DATA_TYPES = { 'SmallIntegerField': 'NUMBER(11)', 'TextField': 'NCLOB', 'TimeField': 'TIMESTAMP', - 'URLField': 'VARCHAR2(200)', + 'URLField': 'VARCHAR2(%(max_length)s)', 'USStateField': 'CHAR(2)', } diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py index ceffea19e6..b3e374da27 100644 --- a/django/db/backends/postgresql/creation.py +++ b/django/db/backends/postgresql/creation.py @@ -10,10 +10,10 @@ DATA_TYPES = { 'DateField': 'date', 'DateTimeField': 'timestamp with time zone', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', - 'ImageField': 'varchar(100)', + 'ImageField': 'varchar(%(max_length)s)', 'IntegerField': 'integer', 'IPAddressField': 'inet', 'NullBooleanField': 'boolean', diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py index eccb19a160..54b75f23be 100644 --- a/django/db/backends/sqlite3/creation.py +++ b/django/db/backends/sqlite3/creation.py @@ -9,10 +9,10 @@ DATA_TYPES = { 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'decimal', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'real', - 'ImageField': 'varchar(100)', + 'ImageField': 'varchar(%(max_length)s)', 'IntegerField': 'integer', 'IPAddressField': 'char(15)', 'NullBooleanField': 'bool', diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 597271c997..67f14f9baf 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -684,8 +684,7 @@ class DecimalField(Field): class EmailField(CharField): def __init__(self, *args, **kwargs): - if 'max_length' not in kwargs: - kwargs['max_length'] = 75 + kwargs['max_length'] = kwargs.get('max_length', 75) CharField.__init__(self, *args, **kwargs) def get_internal_type(self): @@ -705,6 +704,7 @@ class EmailField(CharField): class FileField(Field): def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs): self.upload_to = upload_to + kwargs['max_length'] = kwargs.get('max_length', 100) Field.__init__(self, verbose_name, name, **kwargs) def get_db_prep_save(self, value): @@ -806,6 +806,7 @@ class FileField(Field): class FilePathField(Field): def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs): self.path, self.match, self.recursive = path, match, recursive + kwargs['max_length'] = kwargs.get('max_length', 100) Field.__init__(self, verbose_name, name, **kwargs) def get_manipulator_field_objs(self): diff --git a/django/db/models/query.py b/django/db/models/query.py index 23d0bac6c8..4d0d295e97 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1180,7 +1180,7 @@ def delete_objects(seen_objs): if field.rel and field.null and field.rel.to in seen_objs: setattr(instance, field.attname, None) - setattr(instance, cls._meta.pk.attname, None) dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance) + setattr(instance, cls._meta.pk.attname, None) transaction.commit_unless_managed() diff --git a/django/middleware/http.py b/django/middleware/http.py index 78e066c67b..71cdf7aa5d 100644 --- a/django/middleware/http.py +++ b/django/middleware/http.py @@ -54,8 +54,7 @@ class SetRemoteAddrFromForwardedFor(object): except KeyError: return None else: - # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. - # Take just the last one. - # See http://bob.pythonmac.org/archives/2005/09/23/apache-x-forwarded-for-caveat/ - real_ip = real_ip.split(",")[-1].strip() + # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. The + # client's IP will be the first one. + real_ip = real_ip.split(",")[0].strip() request.META['REMOTE_ADDR'] = real_ip diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py index 93cfa1d8fa..9bb90416c4 100644 --- a/django/oldforms/__init__.py +++ b/django/oldforms/__init__.py @@ -447,7 +447,7 @@ class LargeTextField(TextField): self.field_name, self.rows, self.cols, escape(data)) class HiddenField(FormField): - def __init__(self, field_name, is_required=False, validator_list=None): + def __init__(self, field_name, is_required=False, validator_list=None, max_length=None): if validator_list is None: validator_list = [] self.field_name, self.is_required = field_name, is_required self.validator_list = validator_list[:] @@ -674,7 +674,7 @@ class CheckboxSelectMultipleField(SelectMultipleField): #################### class FileUploadField(FormField): - def __init__(self, field_name, is_required=False, validator_list=None): + def __init__(self, field_name, is_required=False, validator_list=None, max_length=None): if validator_list is None: validator_list = [] self.field_name, self.is_required = field_name, is_required self.validator_list = [self.isNonEmptyFile] + validator_list @@ -946,7 +946,7 @@ class IPAddressField(TextField): class FilePathField(SelectField): "A SelectField whose choices are the files in a given directory." - def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None): + def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None, max_length=None): import os from django.db.models import BLANK_CHOICE_DASH if match is not None: diff --git a/django/template/__init__.py b/django/template/__init__.py index 449e0d0c28..1cfd85be06 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -88,8 +88,6 @@ UNKNOWN_SOURCE="<unknown source>" tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))) -# matches if the string is valid number -number_re = re.compile(r'[-+]?(\d+|\d*\.\d+)$') # global dictionary of libraries that have been loaded using get_library libraries = {} @@ -564,18 +562,19 @@ class FilterExpression(object): elif constant_arg is not None: args.append((False, constant_arg.replace(r'\"', '"'))) elif var_arg: - args.append((True, var_arg)) + args.append((True, Variable(var_arg))) filter_func = parser.find_filter(filter_name) self.args_check(filter_name,filter_func, args) filters.append( (filter_func,args)) upto = match.end() if upto != len(token): raise TemplateSyntaxError, "Could not parse the remainder: '%s' from '%s'" % (token[upto:], token) - self.var, self.filters = var, filters + self.filters = filters + self.var = Variable(var) def resolve(self, context, ignore_failures=False): try: - obj = resolve_variable(self.var, context) + obj = self.var.resolve(context) except VariableDoesNotExist: if ignore_failures: obj = None @@ -595,7 +594,7 @@ class FilterExpression(object): if not lookup: arg_vals.append(arg) else: - arg_vals.append(resolve_variable(arg, context)) + arg_vals.append(arg.resolve(context)) obj = func(obj, *arg_vals) return obj @@ -637,37 +636,98 @@ class FilterExpression(object): def resolve_variable(path, context): """ Returns the resolved variable, which may contain attribute syntax, within - the given context. The variable may be a hard-coded string (if it begins - and ends with single or double quote marks). + the given context. + + Deprecated; use the Variable class instead. + """ + return Variable(path).resolve(context) - >>> c = {'article': {'section':'News'}} - >>> resolve_variable('article.section', c) - u'News' - >>> resolve_variable('article', c) - {'section': 'News'} - >>> class AClass: pass - >>> c = AClass() - >>> c.article = AClass() - >>> c.article.section = 'News' - >>> resolve_variable('article.section', c) - u'News' +class Variable(object): + """ + A template variable, resolvable against a given context. The variable may be + a hard-coded string (if it begins and ends with single or double quote + marks):: + + >>> c = {'article': {'section':'News'}} + >>> Variable('article.section').resolve(c) + u'News' + >>> Variable('article').resolve(c) + {'section': 'News'} + >>> class AClass: pass + >>> c = AClass() + >>> c.article = AClass() + >>> c.article.section = 'News' + >>> Variable('article.section').resolve(c) + u'News' (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.') """ - if number_re.match(path): - number_type = '.' in path and float or int - current = number_type(path) - elif path[0] in ('"', "'") and path[0] == path[-1]: - current = path[1:-1] - else: + + def __init__(self, var): + self.var = var + self.literal = None + self.lookups = None + + try: + # First try to treat this variable as a number. + # + # Note that this could cause an OverflowError here that we're not + # catching. Since this should only happen at compile time, that's + # probably OK. + self.literal = float(var) + + # So it's a float... is it an int? If the original value contained a + # dot or an "e" then it was a float, not an int. + if '.' not in var and 'e' not in var.lower(): + self.literal = int(self.literal) + + # "2." is invalid + if var.endswith('.'): + raise ValueError + + except ValueError: + # A ValueError means that the variable isn't a number. + # If it's wrapped with quotes (single or double), then + # we're also dealing with a literal. + if var[0] in "\"'" and var[0] == var[-1]: + self.literal = var[1:-1] + + else: + # Otherwise we'll set self.lookups so that resolve() knows we're + # dealing with a bonafide variable + self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) + + def resolve(self, context): + """Resolve this variable against a given context.""" + if self.lookups is not None: + # We're dealing with a variable that needs to be resolved + return self._resolve_lookup(context) + else: + # We're dealing with a literal, so it's already been "resolved" + return self.literal + + def __repr__(self): + return "<%s: %r>" % (self.__class__.__name__, self.var) + + def __str__(self): + return self.var + + def _resolve_lookup(self, context): + """ + Performs resolution of a real variable (i.e. not a literal) against the + given context. + + As indicated by the method's name, this method is an implementation + detail and shouldn't be called by external code. Use Variable.resolve() + instead. + """ current = context - bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR) - while bits: + for bit in self.lookups: try: # dictionary lookup - current = current[bits[0]] + current = current[bit] except (TypeError, AttributeError, KeyError): try: # attribute lookup - current = getattr(current, bits[0]) + current = getattr(current, bit) if callable(current): if getattr(current, 'alters_data', False): current = settings.TEMPLATE_STRING_IF_INVALID @@ -685,27 +745,27 @@ def resolve_variable(path, context): raise except (TypeError, AttributeError): try: # list-index lookup - current = current[int(bits[0])] + current = current[int(bit)] except (IndexError, # list index out of range ValueError, # invalid literal for int() - KeyError, # current is a dict without `int(bits[0])` key + KeyError, # current is a dict without `int(bit)` key TypeError, # unsubscriptable object ): - raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bits[0], current)) # missing attribute + raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute except Exception, e: if getattr(e, 'silent_variable_failure', False): current = settings.TEMPLATE_STRING_IF_INVALID else: raise - del bits[0] - if isinstance(current, (basestring, Promise)): - try: - current = force_unicode(current) - except UnicodeDecodeError: - # Failing to convert to unicode can happen sometimes (e.g. debug - # tracebacks). So we allow it in this particular instance. - pass - return current + + if isinstance(current, (basestring, Promise)): + try: + current = force_unicode(current) + except UnicodeDecodeError: + # Failing to convert to unicode can happen sometimes (e.g. debug + # tracebacks). So we allow it in this particular instance. + pass + return current class Node(object): def render(self, context): @@ -861,10 +921,10 @@ class Library(object): class SimpleNode(Node): def __init__(self, vars_to_resolve): - self.vars_to_resolve = vars_to_resolve + self.vars_to_resolve = map(Variable, vars_to_resolve) def render(self, context): - resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] + resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] return func(*resolved_vars) compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode) @@ -883,10 +943,10 @@ class Library(object): class InclusionNode(Node): def __init__(self, vars_to_resolve): - self.vars_to_resolve = vars_to_resolve + self.vars_to_resolve = map(Variable, vars_to_resolve) def render(self, context): - resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] + resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] if takes_context: args = [context] + resolved_vars else: diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 1fd6d02c70..f1fcf5fe90 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -1,6 +1,6 @@ "Default variable filters" -from django.template import resolve_variable, Library +from django.template import Variable, Library from django.conf import settings from django.utils.translation import ugettext, ungettext from django.utils.encoding import force_unicode, smart_str, iri_to_uri @@ -297,7 +297,8 @@ def dictsort(value, arg): Takes a list of dicts, returns that list sorted by the property given in the argument. """ - decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value] + var_resolve = Variable(arg).resolve + decorated = [(var_resolve(item), item) for item in value] decorated.sort() return [item[1] for item in decorated] @@ -306,7 +307,8 @@ def dictsortreversed(value, arg): Takes a list of dicts, returns that list sorted in reverse order by the property given in the argument. """ - decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value] + var_resolve = Variable(arg).resolve + decorated = [(var_resolve(item), item) for item in value] decorated.sort() decorated.reverse() return [item[1] for item in decorated] diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index e23295f732..151985bfdd 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -1,6 +1,6 @@ "Default tags used by the template system, available to all templates." -from django.template import Node, NodeList, Template, Context, resolve_variable +from django.template import Node, NodeList, Template, Context, Variable from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END from django.template import get_library, Library, InvalidTemplateLibrary from django.conf import settings @@ -30,7 +30,7 @@ class CycleNode(Node): def render(self, context): self.counter += 1 value = self.cyclevars[self.counter % self.cyclevars_len] - value = resolve_variable(value, context) + value = Variable(value).resolve(context) if self.variable_name: context[self.variable_name] = value return value @@ -57,12 +57,12 @@ class FilterNode(Node): class FirstOfNode(Node): def __init__(self, vars): - self.vars = vars + self.vars = map(Variable, vars) def render(self, context): for var in self.vars: try: - value = resolve_variable(var, context) + value = var.resolve(context) except VariableDoesNotExist: continue if value: @@ -147,7 +147,7 @@ class IfChangedNode(Node): def __init__(self, nodelist, *varlist): self.nodelist = nodelist self._last_seen = None - self._varlist = varlist + self._varlist = map(Variable, varlist) def render(self, context): if 'forloop' in context and context['forloop']['first']: @@ -156,7 +156,7 @@ class IfChangedNode(Node): if self._varlist: # Consider multiple parameters. # This automatically behaves like a OR evaluation of the multiple variables. - compare_to = [resolve_variable(var, context) for var in self._varlist] + compare_to = [var.resolve(context) for var in self._varlist] else: compare_to = self.nodelist.render(context) except VariableDoesNotExist: @@ -175,7 +175,7 @@ class IfChangedNode(Node): class IfEqualNode(Node): def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): - self.var1, self.var2 = var1, var2 + self.var1, self.var2 = Variable(var1), Variable(var2) self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false self.negate = negate @@ -184,11 +184,11 @@ class IfEqualNode(Node): def render(self, context): try: - val1 = resolve_variable(self.var1, context) + val1 = self.var1.resolve(context) except VariableDoesNotExist: val1 = None try: - val2 = resolve_variable(self.var2, context) + val2 = self.var2.resolve(context) except VariableDoesNotExist: val2 = None if (self.negate and val1 != val2) or (not self.negate and val1 == val2): diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index 19f368711c..652fda11ce 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -1,4 +1,4 @@ -from django.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable +from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable from django.template import Library, Node from django.template.loader import get_template, get_template_from_string, find_template_source from django.conf import settings @@ -99,11 +99,11 @@ class ConstantIncludeNode(Node): class IncludeNode(Node): def __init__(self, template_name): - self.template_name = template_name + self.template_name = Variable(template_name) def render(self, context): try: - template_name = resolve_variable(self.template_name, context) + template_name = self.template_name.resolve(context) t = get_template(template_name) return t.render(context) except TemplateSyntaxError, e: diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py index 1e85c6b5d1..d5b0741a61 100644 --- a/django/templatetags/i18n.py +++ b/django/templatetags/i18n.py @@ -1,4 +1,4 @@ -from django.template import Node, resolve_variable +from django.template import Node, Variable from django.template import TemplateSyntaxError, TokenParser, Library from django.template import TOKEN_TEXT, TOKEN_VAR from django.utils import translation @@ -32,11 +32,11 @@ class GetCurrentLanguageBidiNode(Node): class TranslateNode(Node): def __init__(self, value, noop): - self.value = value + self.value = Variable(value) self.noop = noop def render(self, context): - value = resolve_variable(self.value, context) + value = self.value.resolve(context) if self.noop: return value else: diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 40e99c3962..2f3c9bb568 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -149,7 +149,7 @@ class MultiValueDict(dict): dict.__init__(self, key_to_list_mapping) def __repr__(self): - return "<MultiValueDict: %s>" % dict.__repr__(self) + return "<%s: %s>" % (self.__class__.__name__, dict.__repr__(self)) def __getitem__(self, key): """ diff --git a/django/utils/encoding.py b/django/utils/encoding.py index 69c3e9c28b..6daae4386d 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -1,5 +1,6 @@ import types import urllib +import datetime from django.utils.functional import Promise class StrAndUnicode(object): @@ -30,7 +31,7 @@ def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'): If strings_only is True, don't convert (some) non-string-like objects. """ - if strings_only and isinstance(s, (types.NoneType, int, long)): + if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.time, float)): return s if not isinstance(s, basestring,): if hasattr(s, '__unicode__'): diff --git a/docs/authentication.txt b/docs/authentication.txt index 713e86c140..aee9c5224a 100644 --- a/docs/authentication.txt +++ b/docs/authentication.txt @@ -1062,3 +1062,40 @@ object the first time a user authenticates:: return User.objects.get(pk=user_id) except User.DoesNotExist: return None + +Handling authorization in custom backends +----------------------------------------- + +Custom auth backends can provide their own permissions. + +The user model will delegate permission lookup functions +(``get_group_permissions()``, ``get_all_permissions()``, ``has_perm()``, and +``has_module_perms()``) to any authentication backend that implements these +functions. + +The permissions given to the user will be the superset of all permissions +returned by all backends. That is, Django grants a permission to a user that any +one backend grants. + +The simple backend above could implement permissions for the magic admin fairly +simply:: + + class SettingsBackend: + + # ... + + def has_perm(self, user_obj, perm): + if user_obj.username == settings.ADMIN_LOGIN: + return True + else: + return False + +This gives full permissions to the user granted access in the above example. Notice +that the backend auth functions all take the user object as an argument, and +they also accept the same arguments given to the associated ``User`` functions. + +A full authorization implementation can be found in +``django/contrib/auth/backends.py`` _, which is the default backend and queries +the ``auth_permission`` table most of the time. + +.. _django/contrib/auth/backends.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py diff --git a/docs/db-api.txt b/docs/db-api.txt index 08b5391e3c..adca8b4d5c 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -952,7 +952,7 @@ Example:: If you pass ``in_bulk()`` an empty list, you'll get an empty dictionary. ``iterator()`` -~~~~~~~~~~~~ +~~~~~~~~~~~~~~ Evaluates the ``QuerySet`` (by performing the query) and returns an `iterator`_ over the results. A ``QuerySet`` typically reads all of diff --git a/docs/django-admin.txt b/docs/django-admin.txt index 0f99987bad..f098dfa988 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -735,3 +735,32 @@ distribution. It enables tab-completion of ``django-admin.py`` and * Press [TAB] to see all available options. * Type ``sql``, then [TAB], to see all available options whose names start with ``sql``. + +Customized actions +================== + +**New in Django development version** + +If you want to add an action of your own to ``manage.py``, you can. +Simply add a ``management/commands`` directory to your application. +Each python module in that directory will be discovered and registered as +a command that can be executed as an action when you run ``manage.py``:: + + /fancy_blog + __init__.py + models.py + /management + __init__.py + /commands + __init__.py + explode.py + views.py + +In this example, ``explode`` command will be made available to any project +that includes the ``fancy_blog`` application in ``settings.INSTALLED_APPS``. + +The ``explode.py`` module has only one requirement -- it must define a class +called ``Command`` that extends ``django.core.management.base.BaseCommand``. + +For more details on how to define your own commands, look at the code for the +existing ``django-admin.py`` commands, in ``/django/core/management/commands``. diff --git a/docs/email.txt b/docs/email.txt index 17c2b2115a..2bad79ce33 100644 --- a/docs/email.txt +++ b/docs/email.txt @@ -100,31 +100,31 @@ mail_admins() ============= ``django.core.mail.mail_admins()`` is a shortcut for sending an e-mail to the -site admins, as defined in the `ADMINS setting`_. Here's the definition:: +site admins, as defined in the `ADMINS`_ setting. Here's the definition:: mail_admins(subject, message, fail_silently=False) ``mail_admins()`` prefixes the subject with the value of the -`EMAIL_SUBJECT_PREFIX setting`_, which is ``"[Django] "`` by default. +`EMAIL_SUBJECT_PREFIX`_ setting, which is ``"[Django] "`` by default. -The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`_. +The "From:" header of the e-mail will be the value of the `SERVER_EMAIL`_ setting. This method exists for convenience and readability. -.. _ADMINS setting: ../settings/#admins -.. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix -.. _SERVER_EMAIL setting: ../settings/#server-email +.. _ADMINS: ../settings/#admins +.. _EMAIL_SUBJECT_PREFIX: ../settings/#email-subject-prefix +.. _SERVER_EMAIL: ../settings/#server-email mail_managers() function ======================== ``django.core.mail.mail_managers()`` is just like ``mail_admins()``, except it -sends an e-mail to the site managers, as defined in the `MANAGERS setting`_. +sends an e-mail to the site managers, as defined in the `MANAGERS`_ setting. Here's the definition:: mail_managers(subject, message, fail_silently=False) -.. _MANAGERS setting: ../settings/#managers +.. _MANAGERS: ../settings/#managers Examples ======== @@ -225,7 +225,7 @@ optional and can be set at any time prior to calling the ``send()`` method. * ``from_email``: The sender's address. Both ``fred@example.com`` and ``Fred <fred@example.com>`` forms are legal. If omitted, the - ``DEFAULT_FROM_EMAIL`` setting is used. + `DEFAULT_FROM_EMAIL`_ setting is used. * ``to``: A list or tuple of recipient addresses. @@ -297,6 +297,8 @@ The class has the following methods: message.attach_file('/images/weather_map.png') +.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email + Sending alternative content types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/generic_views.txt b/docs/generic_views.txt index 87b82f7adf..08ff01c372 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -800,9 +800,14 @@ specify the page number in the URL in one of two ways: variable. You can iterate over the list provided by ``page_range`` to create a link to every page of results. -These values and lists are is 1-based, not 0-based, so the first page would be +These values and lists are 1-based, not 0-based, so the first page would be represented as page ``1``. +An example of the use of pagination can be found in the `object pagination`_ +example model. + +.. _`object pagination`: ../models/pagination/ + **New in Django development version:** As a special case, you are also permitted to use diff --git a/docs/model-api.txt b/docs/model-api.txt index adb9cfceb1..a0844ea961 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -293,6 +293,10 @@ visiting its URL on your site. Don't allow that. .. _`strftime formatting`: http://docs.python.org/lib/module-time.html#l2h-1941 +**New in development version:** By default, ``FileField`` instances are +created as ``varchar(100)`` columns in your database. As with other fields, you +can change the maximum length using the ``max_length`` argument. + ``FilePathField`` ~~~~~~~~~~~~~~~~~ @@ -330,6 +334,10 @@ not the full path. So, this example:: because the ``match`` applies to the base filename (``foo.gif`` and ``bar.gif``). +**New in development version:** By default, ``FilePathField`` instances are +created as ``varchar(100)`` columns in your database. As with other fields, you +can change the maximum length using the ``max_length`` argument. + ``FloatField`` ~~~~~~~~~~~~~~ @@ -361,6 +369,11 @@ Requires the `Python Imaging Library`_. .. _Python Imaging Library: http://www.pythonware.com/products/pil/ .. _elsewhere: ../db-api/#get-foo-height-and-get-foo-width +**New in development version:** By default, ``ImageField`` instances are +created as ``varchar(100)`` columns in your database. As with other fields, you +can change the maximum length using the ``max_length`` argument. + + ``IntegerField`` ~~~~~~~~~~~~~~~~ diff --git a/docs/newforms.txt b/docs/newforms.txt index 6ff96e3b98..54bd6306c7 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -1926,11 +1926,22 @@ of the model fields: .. note:: If you specify ``fields`` when creating a form with ``form_for_model()``, - make sure that the fields that are *not* specified can provide default - values, or are allowed to have a value of ``None``. If a field isn't - specified on a form, the object created from the form can't provide - a value for that attribute, which will prevent the new instance from - being saved. + then the fields that are *not* specified will not be set by the form's + ``save()`` method. Django will prevent any attempt to save an incomplete + model, so if the model does not allow the missing fields to be empty, and + does not provide a default value for the missing fields, any attempt to + ``save()`` a ``form_for_model`` with missing fields will fail. To avoid + this failure, you must use ``save(commit=False)`` and manually set any + extra required fields:: + + instance = form.save(commit=False) + instance.required_field = 'new value' + instance.save() + + See the `section on saving forms`_ for more details on using + ``save(commit=False)``. + +.. _section on saving forms: `The save() method`_ Overriding the default field types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/request_response.txt b/docs/request_response.txt index bf914fb5ff..7806886841 100644 --- a/docs/request_response.txt +++ b/docs/request_response.txt @@ -190,7 +190,7 @@ necessary because some HTML form elements, notably That means you can't change attributes of ``request.POST`` and ``request.GET`` directly. -``QueryDict`` implements the all standard dictionary methods, because it's a +``QueryDict`` implements all the standard dictionary methods, because it's a subclass of dictionary. Exceptions are outlined here: * ``__getitem__(key)`` -- Returns the value for the given key. If the key diff --git a/docs/sessions.txt b/docs/sessions.txt index ab7ea56aaa..96e8d36854 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -198,14 +198,14 @@ Using sessions out of views An API is available to manipulate session data outside of a view:: - >>> from django.contrib.sessions.engines.db import SessionStore + >>> from django.contrib.sessions.backends.db import SessionStore >>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead') >>> s['last_login'] = datetime.datetime(2005, 8, 20, 13, 35, 10) >>> s['last_login'] datetime.datetime(2005, 8, 20, 13, 35, 0) >>> s.save() -If you're using the ``django.contrib.sessions.engine.db`` backend, each +If you're using the ``django.contrib.sessions.backends.db`` backend, each session is just a normal Django model. The ``Session`` model is defined in ``django/contrib/sessions/models.py``. Because it's a normal model, you can access sessions using the normal Django database API:: diff --git a/docs/settings.txt b/docs/settings.txt index e40374a822..7ad1f6441d 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -363,7 +363,7 @@ regular expression which will hide from the DEBUG view anything that contains be able to give backtraces without seeing sensitive (or offensive) settings. Still, note that there are always going to be sections of your debug output that -are inapporpriate for public consumption. File paths, configuration options, and +are inappropriate for public consumption. File paths, configuration options, and the like all give attackers extra information about your server. Never deploy a site with ``DEBUG`` turned on. diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 232f54061f..b6173ad39a 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -928,10 +928,36 @@ current context, available in the ``render`` method:: ``resolve_variable`` will try to resolve ``blog_entry.date_updated`` and then format it accordingly. -.. note:: - The ``resolve_variable()`` function will throw a ``VariableDoesNotExist`` - exception if it cannot resolve the string passed to it in the current - context of the page. +.. admonition:: New in development version: + + Variable resolution has changed in the development version of Django. + ``template.resolve_variable()`` is still available, but has been deprecated + in favor of a new ``template.Variable`` class. Using this class will usually + be more efficient than calling ``template.resolve_variable`` + + To use the ``Variable`` class, simply instantiate it with the name of the + variable to be resolved, and then call ``variable.resolve(context)``. So, + in the development version, the above example would be more correctly + written as: + + .. parsed-literal:: + + class FormatTimeNode(template.Node): + def __init__(self, date_to_be_formatted, format_string): + self.date_to_be_formatted = **Variable(date_to_be_formatted)** + self.format_string = format_string + + def render(self, context): + try: + actual_date = **self.date_to_be_formatted.resolve(context)** + return actual_date.strftime(self.format_string) + except template.VariableDoesNotExist: + return '' + + Changes are highlighted in bold. + +Variable resolution will throw a ``VariableDoesNotExist`` exception if it cannot +resolve the string passed to it in the current context of the page. Shortcut for simple tags ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index a0a68d4bcc..a2388223f0 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -63,6 +63,9 @@ class Movie(models.Model): def __unicode__(self): return self.title + +class Score(models.Model): + score = models.FloatField() __test__ = {'API_TESTS':""" # Create some data: @@ -83,7 +86,7 @@ __test__ = {'API_TESTS':""" >>> a2 = Article( ... author = joe, ... headline = "Time to reform copyright", -... pub_date = datetime(2006, 6, 16, 13, 00)) +... pub_date = datetime(2006, 6, 16, 13, 00, 11, 345)) >>> a1.save(); a2.save() >>> a1.categories = [sports, op_ed] >>> a2.categories = [music, op_ed] @@ -181,7 +184,7 @@ __test__ = {'API_TESTS':""" # Serializer output can be restricted to a subset of fields >>> print serializers.serialize("json", Article.objects.all(), fields=('headline','pub_date')) -[{"pk": 1, "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 2, "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}] +[{"pk": 1, "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 2, "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:11"}}, {"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}] # Every string is serialized as a unicode object, also primary key # which is 'varchar' @@ -207,4 +210,11 @@ u'G\u0119\u015bl\u0105 ja\u017a\u0144' >>> print list(serializers.deserialize('json', serializers.serialize('json', [mv2])))[0].object.id None +# Serialization and deserialization of floats: +>>> sc = Score(score=3.4) +>>> print serializers.serialize("json", [sc]) +[{"pk": null, "model": "serializers.score", "fields": {"score": 3.4}}] +>>> print list(serializers.deserialize('json', serializers.serialize('json', [sc])))[0].object.score +3.4 + """} diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py index d41142135e..1fb73828bb 100644 --- a/tests/modeltests/signals/models.py +++ b/tests/modeltests/signals/models.py @@ -54,7 +54,7 @@ Is updated pre_delete signal, Tom Smith instance.id is not None: True post_delete signal, Tom Smith -instance.id is None: True +instance.id is None: False >>> p2 = Person(first_name='James', last_name='Jones') >>> p2.id = 99999 @@ -73,7 +73,7 @@ Is created pre_delete signal, James Jones instance.id is not None: True post_delete signal, James Jones -instance.id is None: True +instance.id is None: False >>> Person.objects.all() [<Person: James Jones>] diff --git a/tests/modeltests/user_commands/__init__.py b/tests/modeltests/user_commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/user_commands/__init__.py diff --git a/tests/modeltests/user_commands/management/__init__.py b/tests/modeltests/user_commands/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/user_commands/management/__init__.py diff --git a/tests/modeltests/user_commands/management/commands/__init__.py b/tests/modeltests/user_commands/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/user_commands/management/commands/__init__.py diff --git a/tests/modeltests/user_commands/management/commands/dance.py b/tests/modeltests/user_commands/management/commands/dance.py new file mode 100644 index 0000000000..5886cd1d8f --- /dev/null +++ b/tests/modeltests/user_commands/management/commands/dance.py @@ -0,0 +1,9 @@ +from django.core.management.base import BaseCommand + +class Command(BaseCommand): + help = "Dance around like a madman." + args = '' + requires_model_validation = True + + def handle(self, *args, **options): + print "I don't feel like dancing."
\ No newline at end of file diff --git a/tests/modeltests/user_commands/models.py b/tests/modeltests/user_commands/models.py new file mode 100644 index 0000000000..5f96806dac --- /dev/null +++ b/tests/modeltests/user_commands/models.py @@ -0,0 +1,30 @@ +""" +37. User-registered management commands + +The manage.py utility provides a number of useful commands for managing a +Django project. If you want to add a utility command of your own, you can. + +The user-defined command 'dance' is defined in the management/commands +subdirectory of this test application. It is a simple command that responds +with a printed message when invoked. + +For more details on how to define your own manage.py commands, look at the +django.core.management.commands directory. This directory contains the +definitions for the base Django manage.py commands. +""" + +__test__ = {'API_TESTS': """ +>>> from django.core import management + +# Invoke a simple user-defined command +>>> management.call_command('dance') +I don't feel like dancing. + +# Invoke a command that doesn't exist +>>> management.call_command('explode') +Traceback (most recent call last): +... +CommandError: Unknown command: 'explode' + + +"""}
\ No newline at end of file diff --git a/tests/regressiontests/auth_backends/__init__.py b/tests/regressiontests/auth_backends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/auth_backends/__init__.py diff --git a/tests/regressiontests/auth_backends/models.py b/tests/regressiontests/auth_backends/models.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/auth_backends/models.py diff --git a/tests/regressiontests/auth_backends/tests.py b/tests/regressiontests/auth_backends/tests.py new file mode 100644 index 0000000000..71e58442e7 --- /dev/null +++ b/tests/regressiontests/auth_backends/tests.py @@ -0,0 +1,66 @@ +""" +>>> from django.contrib.auth.models import User, Group, Permission +>>> from django.contrib.contenttypes.models import ContentType + +# No Permissions assigned yet, should return False except for superuser + +>>> user = User.objects.create_user('test', 'test@example.com', 'test') +>>> user.has_perm("auth.test") +False +>>> user.is_staff=True +>>> user.save() +>>> user.has_perm("auth.test") +False +>>> user.is_superuser=True +>>> user.save() +>>> user.has_perm("auth.test") +True +>>> user.is_staff = False +>>> user.is_superuser = False +>>> user.save() +>>> user.has_perm("auth.test") +False +>>> content_type=ContentType.objects.get_for_model(Group) +>>> perm = Permission.objects.create(name="test", content_type=content_type, codename="test") +>>> user.user_permissions.add(perm) +>>> user.save() + +# reloading user to purge the _perm_cache + +>>> user = User.objects.get(username="test") +>>> user.get_all_permissions() +set([u'auth.test']) +>>> user.get_group_permissions() +set([]) +>>> user.has_module_perms("Group") +False +>>> user.has_module_perms("auth") +True +>>> perm = Permission.objects.create(name="test2", content_type=content_type, codename="test2") +>>> user.user_permissions.add(perm) +>>> user.save() +>>> perm = Permission.objects.create(name="test3", content_type=content_type, codename="test3") +>>> user.user_permissions.add(perm) +>>> user.save() +>>> user = User.objects.get(username="test") +>>> user.get_all_permissions() +set([u'auth.test2', u'auth.test', u'auth.test3']) +>>> user.has_perm('test') +False +>>> user.has_perm('auth.test') +True +>>> user.has_perms(['auth.test2', 'auth.test3']) +True +>>> perm = Permission.objects.create(name="test_group", content_type=content_type, codename="test_group") +>>> group = Group.objects.create(name='test_group') +>>> group.permissions.add(perm) +>>> group.save() +>>> user.groups.add(group) +>>> user = User.objects.get(username="test") +>>> user.get_all_permissions() +set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group']) +>>> user.get_group_permissions() +set([u'auth.test_group']) +>>> user.has_perms(['auth.test3', 'auth.test_group']) +True +"""
\ No newline at end of file diff --git a/tests/regressiontests/forms/extra.py b/tests/regressiontests/forms/extra.py new file mode 100644 index 0000000000..7f6175f649 --- /dev/null +++ b/tests/regressiontests/forms/extra.py @@ -0,0 +1,398 @@ +# -*- coding: utf-8 -*- +tests = r""" +>>> from django.newforms import * +>>> import datetime +>>> import time +>>> import re +>>> try: +... from decimal import Decimal +... except ImportError: +... from django.utils._decimal import Decimal + +############### +# Extra stuff # +############### + +The newforms library comes with some extra, higher-level Field and Widget +classes that demonstrate some of the library's abilities. + +# SelectDateWidget ############################################################ + +>>> from django.newforms.extras import SelectDateWidget +>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016')) +>>> print w.render('mydate', '') +<select name="mydate_month"> +<option value="1">January</option> +<option value="2">February</option> +<option value="3">March</option> +<option value="4">April</option> +<option value="5">May</option> +<option value="6">June</option> +<option value="7">July</option> +<option value="8">August</option> +<option value="9">September</option> +<option value="10">October</option> +<option value="11">November</option> +<option value="12">December</option> +</select> +<select name="mydate_day"> +<option value="1">1</option> +<option value="2">2</option> +<option value="3">3</option> +<option value="4">4</option> +<option value="5">5</option> +<option value="6">6</option> +<option value="7">7</option> +<option value="8">8</option> +<option value="9">9</option> +<option value="10">10</option> +<option value="11">11</option> +<option value="12">12</option> +<option value="13">13</option> +<option value="14">14</option> +<option value="15">15</option> +<option value="16">16</option> +<option value="17">17</option> +<option value="18">18</option> +<option value="19">19</option> +<option value="20">20</option> +<option value="21">21</option> +<option value="22">22</option> +<option value="23">23</option> +<option value="24">24</option> +<option value="25">25</option> +<option value="26">26</option> +<option value="27">27</option> +<option value="28">28</option> +<option value="29">29</option> +<option value="30">30</option> +<option value="31">31</option> +</select> +<select name="mydate_year"> +<option value="2007">2007</option> +<option value="2008">2008</option> +<option value="2009">2009</option> +<option value="2010">2010</option> +<option value="2011">2011</option> +<option value="2012">2012</option> +<option value="2013">2013</option> +<option value="2014">2014</option> +<option value="2015">2015</option> +<option value="2016">2016</option> +</select> +>>> w.render('mydate', None) == w.render('mydate', '') +True +>>> print w.render('mydate', '2010-04-15') +<select name="mydate_month"> +<option value="1">January</option> +<option value="2">February</option> +<option value="3">March</option> +<option value="4" selected="selected">April</option> +<option value="5">May</option> +<option value="6">June</option> +<option value="7">July</option> +<option value="8">August</option> +<option value="9">September</option> +<option value="10">October</option> +<option value="11">November</option> +<option value="12">December</option> +</select> +<select name="mydate_day"> +<option value="1">1</option> +<option value="2">2</option> +<option value="3">3</option> +<option value="4">4</option> +<option value="5">5</option> +<option value="6">6</option> +<option value="7">7</option> +<option value="8">8</option> +<option value="9">9</option> +<option value="10">10</option> +<option value="11">11</option> +<option value="12">12</option> +<option value="13">13</option> +<option value="14">14</option> +<option value="15" selected="selected">15</option> +<option value="16">16</option> +<option value="17">17</option> +<option value="18">18</option> +<option value="19">19</option> +<option value="20">20</option> +<option value="21">21</option> +<option value="22">22</option> +<option value="23">23</option> +<option value="24">24</option> +<option value="25">25</option> +<option value="26">26</option> +<option value="27">27</option> +<option value="28">28</option> +<option value="29">29</option> +<option value="30">30</option> +<option value="31">31</option> +</select> +<select name="mydate_year"> +<option value="2007">2007</option> +<option value="2008">2008</option> +<option value="2009">2009</option> +<option value="2010" selected="selected">2010</option> +<option value="2011">2011</option> +<option value="2012">2012</option> +<option value="2013">2013</option> +<option value="2014">2014</option> +<option value="2015">2015</option> +<option value="2016">2016</option> +</select> + +Using a SelectDateWidget in a form: + +>>> class GetDate(Form): +... mydate = DateField(widget=SelectDateWidget) +>>> a = GetDate({'mydate_month':'4', 'mydate_day':'1', 'mydate_year':'2008'}) +>>> print a.is_valid() +True +>>> print a.cleaned_data['mydate'] +2008-04-01 + +As with any widget that implements get_value_from_datadict, +we must be prepared to accept the input from the "as_hidden" +rendering as well. + +>>> print a['mydate'].as_hidden() +<input type="hidden" name="mydate" value="2008-4-1" id="id_mydate" /> +>>> b=GetDate({'mydate':'2008-4-1'}) +>>> print b.is_valid() +True +>>> print b.cleaned_data['mydate'] +2008-04-01 + + +# MultiWidget and MultiValueField ############################################# +# MultiWidgets are widgets composed of other widgets. They are usually +# combined with MultiValueFields - a field that is composed of other fields. +# MulitWidgets can themselved be composed of other MultiWidgets. +# SplitDateTimeWidget is one example of a MultiWidget. + +>>> class ComplexMultiWidget(MultiWidget): +... def __init__(self, attrs=None): +... widgets = ( +... TextInput(), +... SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), +... SplitDateTimeWidget(), +... ) +... super(ComplexMultiWidget, self).__init__(widgets, attrs) +... +... def decompress(self, value): +... if value: +... data = value.split(',') +... return [data[0], data[1], datetime.datetime(*time.strptime(data[2], "%Y-%m-%d %H:%M:%S")[0:6])] +... return [None, None, None] +... def format_output(self, rendered_widgets): +... return u'\n'.join(rendered_widgets) +>>> w = ComplexMultiWidget() +>>> print w.render('name', 'some text,JP,2007-04-25 06:24:00') +<input type="text" name="name_0" value="some text" /> +<select multiple="multiple" name="name_1"> +<option value="J" selected="selected">John</option> +<option value="P" selected="selected">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> +<input type="text" name="name_2_0" value="2007-04-25" /><input type="text" name="name_2_1" value="06:24:00" /> + +>>> class ComplexField(MultiValueField): +... def __init__(self, required=True, widget=None, label=None, initial=None): +... fields = ( +... CharField(), +... MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), +... SplitDateTimeField() +... ) +... super(ComplexField, self).__init__(fields, required, widget, label, initial) +... +... def compress(self, data_list): +... if data_list: +... return '%s,%s,%s' % (data_list[0],''.join(data_list[1]),data_list[2]) +... return None + +>>> f = ComplexField(widget=w) +>>> f.clean(['some text', ['J','P'], ['2007-04-25','6:24:00']]) +u'some text,JP,2007-04-25 06:24:00' +>>> f.clean(['some text',['X'], ['2007-04-25','6:24:00']]) +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. X is not one of the available choices.'] + +# If insufficient data is provided, None is substituted +>>> f.clean(['some text',['JP']]) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> class ComplexFieldForm(Form): +... field1 = ComplexField(widget=w) +>>> f = ComplexFieldForm() +>>> print f +<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" id="id_field1_0" /> +<select multiple="multiple" name="field1_1" id="id_field1_1"> +<option value="J">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> +<input type="text" name="field1_2_0" id="id_field1_2_0" /><input type="text" name="field1_2_1" id="id_field1_2_1" /></td></tr> + +>>> f = ComplexFieldForm({'field1_0':'some text','field1_1':['J','P'], 'field1_2_0':'2007-04-25', 'field1_2_1':'06:24:00'}) +>>> print f +<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" value="some text" id="id_field1_0" /> +<select multiple="multiple" name="field1_1" id="id_field1_1"> +<option value="J" selected="selected">John</option> +<option value="P" selected="selected">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> +<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr> + +>>> f.cleaned_data +{'field1': u'some text,JP,2007-04-25 06:24:00'} + + +# IPAddressField ################################################################## + +>>> f = IPAddressField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('127.0.0.1') +u'127.0.0.1' +>>> f.clean('foo') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] +>>> f.clean('127.0.0.') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] +>>> f.clean('1.2.3.4.5') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] +>>> f.clean('256.125.1.5') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] + +>>> f = IPAddressField(required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' +>>> f.clean('127.0.0.1') +u'127.0.0.1' +>>> f.clean('foo') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] +>>> f.clean('127.0.0.') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] +>>> f.clean('1.2.3.4.5') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] +>>> f.clean('256.125.1.5') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid IPv4 address.'] + +################################# +# Tests of underlying functions # +################################# + +# smart_unicode tests +>>> from django.utils.encoding import smart_unicode +>>> class Test: +... def __str__(self): +... return 'ŠĐĆŽćžšđ' +>>> class TestU: +... def __str__(self): +... return 'Foo' +... def __unicode__(self): +... return u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' +>>> smart_unicode(Test()) +u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' +>>> smart_unicode(TestU()) +u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' +>>> smart_unicode(1) +u'1' +>>> smart_unicode('foo') +u'foo' + + +#################################### +# Test accessing errors in clean() # +#################################### + +>>> class UserForm(Form): +... username = CharField(max_length=10) +... password = CharField(widget=PasswordInput) +... def clean(self): +... data = self.cleaned_data +... if not self.errors: +... data['username'] = data['username'].lower() +... return data + +>>> f = UserForm({'username': 'SirRobin', 'password': 'blue'}) +>>> f.is_valid() +True +>>> f.cleaned_data['username'] +u'sirrobin' + +####################################### +# Test overriding ErrorList in a form # +####################################### + +>>> from django.newforms.util import ErrorList +>>> class DivErrorList(ErrorList): +... def __unicode__(self): +... return self.as_divs() +... def as_divs(self): +... if not self: return u'' +... return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self]) +>>> class CommentForm(Form): +... name = CharField(max_length=50, required=False) +... email = EmailField() +... comment = CharField() +>>> data = dict(email='invalid') +>>> f = CommentForm(data, auto_id=False, error_class=DivErrorList) +>>> print f.as_p() +<p>Name: <input type="text" name="name" maxlength="50" /></p> +<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div> +<p>Email: <input type="text" name="email" value="invalid" /></p> +<div class="errorlist"><div class="error">This field is required.</div></div> +<p>Comment: <input type="text" name="comment" /></p> + +################################# +# Test multipart-encoded form # +################################# + +>>> class FormWithoutFile(Form): +... username = CharField() +>>> class FormWithFile(Form): +... username = CharField() +... file = FileField() +>>> class FormWithImage(Form): +... image = ImageField() + +>>> FormWithoutFile().is_multipart() +False +>>> FormWithFile().is_multipart() +True +>>> FormWithImage().is_multipart() +True + +""" diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py new file mode 100644 index 0000000000..3b93d70338 --- /dev/null +++ b/tests/regressiontests/forms/fields.py @@ -0,0 +1,1162 @@ +# -*- coding: utf-8 -*- +tests = r""" +>>> from django.newforms import * +>>> from django.newforms.widgets import RadioFieldRenderer +>>> import datetime +>>> import time +>>> import re +>>> try: +... from decimal import Decimal +... except ImportError: +... from django.utils._decimal import Decimal + + +########## +# Fields # +########## + +Each Field class does some sort of validation. Each Field has a clean() method, +which either raises django.newforms.ValidationError or returns the "clean" +data -- usually a Unicode object, but, in some rare cases, a list. + +Each Field's __init__() takes at least these parameters: + required -- Boolean that specifies whether the field is required. + True by default. + widget -- A Widget class, or instance of a Widget class, that should be + used for this Field when displaying it. Each Field has a default + Widget that it'll use if you don't specify this. In most cases, + the default widget is TextInput. + label -- A verbose name for this field, for use in displaying this field in + a form. By default, Django will use a "pretty" version of the form + field name, if the Field is part of a Form. + initial -- A value to use in this Field's initial display. This value is + *not* used as a fallback if data isn't given. + +Other than that, the Field subclasses have class-specific options for +__init__(). For example, CharField has a max_length option. + +# CharField ################################################################### + +>>> f = CharField() +>>> f.clean(1) +u'1' +>>> f.clean('hello') +u'hello' +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean([1, 2, 3]) +u'[1, 2, 3]' + +>>> f = CharField(required=False) +>>> f.clean(1) +u'1' +>>> f.clean('hello') +u'hello' +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +>>> f.clean([1, 2, 3]) +u'[1, 2, 3]' + +CharField accepts an optional max_length parameter: +>>> f = CharField(max_length=10, required=False) +>>> f.clean('12345') +u'12345' +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('1234567890a') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 10 characters (it has 11).'] + +CharField accepts an optional min_length parameter: +>>> f = CharField(min_length=10, required=False) +>>> f.clean('') +u'' +>>> f.clean('12345') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 10 characters (it has 5).'] +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('1234567890a') +u'1234567890a' + +>>> f = CharField(min_length=10, required=True) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('12345') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 10 characters (it has 5).'] +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('1234567890a') +u'1234567890a' + +# IntegerField ################################################################ + +>>> f = IntegerField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('1') +1 +>>> isinstance(f.clean('1'), int) +True +>>> f.clean('23') +23 +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a whole number.'] +>>> f.clean(42) +42 +>>> f.clean(3.14) +Traceback (most recent call last): +... +ValidationError: [u'Enter a whole number.'] +>>> f.clean('1 ') +1 +>>> f.clean(' 1') +1 +>>> f.clean(' 1 ') +1 +>>> f.clean('1a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a whole number.'] + +>>> f = IntegerField(required=False) +>>> f.clean('') +>>> repr(f.clean('')) +'None' +>>> f.clean(None) +>>> repr(f.clean(None)) +'None' +>>> f.clean('1') +1 +>>> isinstance(f.clean('1'), int) +True +>>> f.clean('23') +23 +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a whole number.'] +>>> f.clean('1 ') +1 +>>> f.clean(' 1') +1 +>>> f.clean(' 1 ') +1 +>>> f.clean('1a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a whole number.'] + +IntegerField accepts an optional max_value parameter: +>>> f = IntegerField(max_value=10) +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(1) +1 +>>> f.clean(10) +10 +>>> f.clean(11) +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 10.'] +>>> f.clean('10') +10 +>>> f.clean('11') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 10.'] + +IntegerField accepts an optional min_value parameter: +>>> f = IntegerField(min_value=10) +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(1) +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 10.'] +>>> f.clean(10) +10 +>>> f.clean(11) +11 +>>> f.clean('10') +10 +>>> f.clean('11') +11 + +min_value and max_value can be used together: +>>> f = IntegerField(min_value=10, max_value=20) +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(1) +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 10.'] +>>> f.clean(10) +10 +>>> f.clean(11) +11 +>>> f.clean('10') +10 +>>> f.clean('11') +11 +>>> f.clean(20) +20 +>>> f.clean(21) +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 20.'] + +# FloatField ################################################################## + +>>> f = FloatField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('1') +1.0 +>>> isinstance(f.clean('1'), float) +True +>>> f.clean('23') +23.0 +>>> f.clean('3.14') +3.1400000000000001 +>>> f.clean(3.14) +3.1400000000000001 +>>> f.clean(42) +42.0 +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('1.0 ') +1.0 +>>> f.clean(' 1.0') +1.0 +>>> f.clean(' 1.0 ') +1.0 +>>> f.clean('1.0a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] + +>>> f = FloatField(required=False) +>>> f.clean('') + +>>> f.clean(None) + +>>> f.clean('1') +1.0 + +FloatField accepts min_value and max_value just like IntegerField: +>>> f = FloatField(max_value=1.5, min_value=0.5) + +>>> f.clean('1.6') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 1.5.'] +>>> f.clean('0.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] +>>> f.clean('1.5') +1.5 +>>> f.clean('0.5') +0.5 + +# DecimalField ################################################################ + +>>> f = DecimalField(max_digits=4, decimal_places=2) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('1') +Decimal("1") +>>> isinstance(f.clean('1'), Decimal) +True +>>> f.clean('23') +Decimal("23") +>>> f.clean('3.14') +Decimal("3.14") +>>> f.clean(3.14) +Decimal("3.14") +>>> f.clean(Decimal('3.14')) +Decimal("3.14") +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('1.0 ') +Decimal("1.0") +>>> f.clean(' 1.0') +Decimal("1.0") +>>> f.clean(' 1.0 ') +Decimal("1.0") +>>> f.clean('1.0a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('123.45') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 4 digits in total.'] +>>> f.clean('1.234') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 2 decimal places.'] +>>> f.clean('123.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 2 digits before the decimal point.'] +>>> f.clean('-12.34') +Decimal("-12.34") +>>> f.clean('-123.45') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 4 digits in total.'] +>>> f.clean('-.12') +Decimal("-0.12") +>>> f.clean('-00.12') +Decimal("-0.12") +>>> f.clean('-000.12') +Decimal("-0.12") +>>> f.clean('-000.123') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 2 decimal places.'] +>>> f.clean('-000.1234') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 4 digits in total.'] +>>> f.clean('--0.12') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] + +>>> f = DecimalField(max_digits=4, decimal_places=2, required=False) +>>> f.clean('') + +>>> f.clean(None) + +>>> f.clean('1') +Decimal("1") + +DecimalField accepts min_value and max_value just like IntegerField: +>>> f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5')) + +>>> f.clean('1.6') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 1.5.'] +>>> f.clean('0.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] +>>> f.clean('1.5') +Decimal("1.5") +>>> f.clean('0.5') +Decimal("0.5") +>>> f.clean('.5') +Decimal("0.5") +>>> f.clean('00.50') +Decimal("0.50") + +# DateField ################################################################### + +>>> import datetime +>>> f = DateField() +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) +datetime.date(2006, 10, 25) +>>> f.clean('2006-10-25') +datetime.date(2006, 10, 25) +>>> f.clean('10/25/2006') +datetime.date(2006, 10, 25) +>>> f.clean('10/25/06') +datetime.date(2006, 10, 25) +>>> f.clean('Oct 25 2006') +datetime.date(2006, 10, 25) +>>> f.clean('October 25 2006') +datetime.date(2006, 10, 25) +>>> f.clean('October 25, 2006') +datetime.date(2006, 10, 25) +>>> f.clean('25 October 2006') +datetime.date(2006, 10, 25) +>>> f.clean('25 October, 2006') +datetime.date(2006, 10, 25) +>>> f.clean('2006-4-31') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('200a-10-25') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('25/10/06') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = DateField(required=False) +>>> f.clean(None) +>>> repr(f.clean(None)) +'None' +>>> f.clean('') +>>> repr(f.clean('')) +'None' + +DateField accepts an optional input_formats parameter: +>>> f = DateField(input_formats=['%Y %m %d']) +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.date(2006, 10, 25) +>>> f.clean('2006 10 25') +datetime.date(2006, 10, 25) + +The input_formats parameter overrides all default input formats, +so the default formats won't work unless you specify them: +>>> f.clean('2006-10-25') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('10/25/2006') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('10/25/06') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] + +# TimeField ################################################################### + +>>> import datetime +>>> f = TimeField() +>>> f.clean(datetime.time(14, 25)) +datetime.time(14, 25) +>>> f.clean(datetime.time(14, 25, 59)) +datetime.time(14, 25, 59) +>>> f.clean('14:25') +datetime.time(14, 25) +>>> f.clean('14:25:59') +datetime.time(14, 25, 59) +>>> f.clean('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid time.'] +>>> f.clean('1:24 p.m.') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid time.'] + +TimeField accepts an optional input_formats parameter: +>>> f = TimeField(input_formats=['%I:%M %p']) +>>> f.clean(datetime.time(14, 25)) +datetime.time(14, 25) +>>> f.clean(datetime.time(14, 25, 59)) +datetime.time(14, 25, 59) +>>> f.clean('4:25 AM') +datetime.time(4, 25) +>>> f.clean('4:25 PM') +datetime.time(16, 25) + +The input_formats parameter overrides all default input formats, +so the default formats won't work unless you specify them: +>>> f.clean('14:30:45') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid time.'] + +# DateTimeField ############################################################### + +>>> import datetime +>>> f = DateTimeField() +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) +datetime.datetime(2006, 10, 25, 14, 30, 59) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) +datetime.datetime(2006, 10, 25, 14, 30, 59, 200) +>>> f.clean('2006-10-25 14:30:45') +datetime.datetime(2006, 10, 25, 14, 30, 45) +>>> f.clean('2006-10-25 14:30:00') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('2006-10-25 14:30') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('2006-10-25') +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean('10/25/2006 14:30:45') +datetime.datetime(2006, 10, 25, 14, 30, 45) +>>> f.clean('10/25/2006 14:30:00') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('10/25/2006 14:30') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('10/25/2006') +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean('10/25/06 14:30:45') +datetime.datetime(2006, 10, 25, 14, 30, 45) +>>> f.clean('10/25/06 14:30:00') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('10/25/06 14:30') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('10/25/06') +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date/time.'] +>>> f.clean('2006-10-25 4:30 p.m.') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date/time.'] + +DateField accepts an optional input_formats parameter: +>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p']) +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) +datetime.datetime(2006, 10, 25, 14, 30, 59) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) +datetime.datetime(2006, 10, 25, 14, 30, 59, 200) +>>> f.clean('2006 10 25 2:30 PM') +datetime.datetime(2006, 10, 25, 14, 30) + +The input_formats parameter overrides all default input formats, +so the default formats won't work unless you specify them: +>>> f.clean('2006-10-25 14:30:45') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date/time.'] + +>>> f = DateTimeField(required=False) +>>> f.clean(None) +>>> repr(f.clean(None)) +'None' +>>> f.clean('') +>>> repr(f.clean('')) +'None' + +# RegexField ################################################################## + +>>> f = RegexField('^\d[A-F]\d$') +>>> f.clean('2A2') +u'2A2' +>>> f.clean('3F3') +u'3F3' +>>> f.clean('3G3') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean(' 2A2') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean('2A2 ') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = RegexField('^\d[A-F]\d$', required=False) +>>> f.clean('2A2') +u'2A2' +>>> f.clean('3F3') +u'3F3' +>>> f.clean('3G3') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean('') +u'' + +Alternatively, RegexField can take a compiled regular expression: +>>> f = RegexField(re.compile('^\d[A-F]\d$')) +>>> f.clean('2A2') +u'2A2' +>>> f.clean('3F3') +u'3F3' +>>> f.clean('3G3') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean(' 2A2') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean('2A2 ') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] + +RegexField takes an optional error_message argument: +>>> f = RegexField('^\d\d\d\d$', error_message='Enter a four-digit number.') +>>> f.clean('1234') +u'1234' +>>> f.clean('123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a four-digit number.'] +>>> f.clean('abcd') +Traceback (most recent call last): +... +ValidationError: [u'Enter a four-digit number.'] + +RegexField also access min_length and max_length parameters, for convenience. +>>> f = RegexField('^\d+$', min_length=5, max_length=10) +>>> f.clean('123') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 5 characters (it has 3).'] +>>> f.clean('abc') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 5 characters (it has 3).'] +>>> f.clean('12345') +u'12345' +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('12345678901') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 10 characters (it has 11).'] +>>> f.clean('12345a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] + +# EmailField ################################################################## + +>>> f = EmailField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('person@example.com') +u'person@example.com' +>>> f.clean('foo') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] +>>> f.clean('foo@') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] +>>> f.clean('foo@bar') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] + +>>> f = EmailField(required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' +>>> f.clean('person@example.com') +u'person@example.com' +>>> f.clean('foo') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] +>>> f.clean('foo@') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] +>>> f.clean('foo@bar') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] + +EmailField also access min_length and max_length parameters, for convenience. +>>> f = EmailField(min_length=10, max_length=15) +>>> f.clean('a@foo.com') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 10 characters (it has 9).'] +>>> f.clean('alf@foo.com') +u'alf@foo.com' +>>> f.clean('alf123456788@foo.com') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 15 characters (it has 20).'] + +# FileField ################################################################## + +>>> f = FileField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f.clean({}) +Traceback (most recent call last): +... +ValidationError: [u'No file was submitted.'] + +>>> f.clean('some content that is not a file') +Traceback (most recent call last): +... +ValidationError: [u'No file was submitted. Check the encoding type on the form.'] + +>>> f.clean({'filename': 'name', 'content':None}) +Traceback (most recent call last): +... +ValidationError: [u'The submitted file is empty.'] + +>>> f.clean({'filename': 'name', 'content':''}) +Traceback (most recent call last): +... +ValidationError: [u'The submitted file is empty.'] + +>>> type(f.clean({'filename': 'name', 'content':'Some File Content'})) +<class 'django.newforms.fields.UploadedFile'> + +# URLField ################################################################## + +>>> f = URLField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('http://localhost') +u'http://localhost' +>>> f.clean('http://example.com') +u'http://example.com' +>>> f.clean('http://www.example.com') +u'http://www.example.com' +>>> f.clean('http://www.example.com:8000/test') +u'http://www.example.com:8000/test' +>>> f.clean('http://200.8.9.10') +u'http://200.8.9.10' +>>> f.clean('http://200.8.9.10:8000/test') +u'http://200.8.9.10:8000/test' +>>> f.clean('foo') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://example') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://example.') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://.com') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] + +>>> f = URLField(required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' +>>> f.clean('http://example.com') +u'http://example.com' +>>> f.clean('http://www.example.com') +u'http://www.example.com' +>>> f.clean('foo') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://example') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://example.') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://.com') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] + +URLField takes an optional verify_exists parameter, which is False by default. +This verifies that the URL is live on the Internet and doesn't return a 404 or 500: +>>> f = URLField(verify_exists=True) +>>> f.clean('http://www.google.com') # This will fail if there's no Internet connection +u'http://www.google.com' +>>> f.clean('http://example') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid URL.'] +>>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') # bad domain +Traceback (most recent call last): +... +ValidationError: [u'This URL appears to be a broken link.'] +>>> f.clean('http://google.com/we-love-microsoft.html') # good domain, bad page +Traceback (most recent call last): +... +ValidationError: [u'This URL appears to be a broken link.'] +>>> f = URLField(verify_exists=True, required=False) +>>> f.clean('') +u'' +>>> f.clean('http://www.google.com') # This will fail if there's no Internet connection +u'http://www.google.com' + +URLField also access min_length and max_length parameters, for convenience. +>>> f = URLField(min_length=15, max_length=20) +>>> f.clean('http://f.com') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 15 characters (it has 12).'] +>>> f.clean('http://example.com') +u'http://example.com' +>>> f.clean('http://abcdefghijklmnopqrstuvwxyz.com') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 20 characters (it has 37).'] + +URLField should prepend 'http://' if no scheme was given +>>> f = URLField(required=False) +>>> f.clean('example.com') +u'http://example.com' +>>> f.clean('') +u'' +>>> f.clean('https://example.com') +u'https://example.com' + +# BooleanField ################################################################ + +>>> f = BooleanField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(True) +True +>>> f.clean(False) +False +>>> f.clean(1) +True +>>> f.clean(0) +False +>>> f.clean('Django rocks') +True + +>>> f = BooleanField(required=False) +>>> f.clean('') +False +>>> f.clean(None) +False +>>> f.clean(True) +True +>>> f.clean(False) +False +>>> f.clean(1) +True +>>> f.clean(0) +False +>>> f.clean('Django rocks') +True + +# ChoiceField ################################################################# + +>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')]) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(1) +u'1' +>>> f.clean('1') +u'1' +>>> f.clean('3') +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] + +>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')], required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' +>>> f.clean(1) +u'1' +>>> f.clean('1') +u'1' +>>> f.clean('3') +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] + +>>> f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')]) +>>> f.clean('J') +u'J' +>>> f.clean('John') +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] + +# NullBooleanField ############################################################ + +>>> f = NullBooleanField() +>>> f.clean('') +>>> f.clean(True) +True +>>> f.clean(False) +False +>>> f.clean(None) +>>> f.clean('1') +>>> f.clean('2') +>>> f.clean('3') +>>> f.clean('hello') + +# MultipleChoiceField ######################################################### + +>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')]) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean([1]) +[u'1'] +>>> f.clean(['1']) +[u'1'] +>>> f.clean(['1', '2']) +[u'1', u'2'] +>>> f.clean([1, '2']) +[u'1', u'2'] +>>> f.clean((1, '2')) +[u'1', u'2'] +>>> f.clean('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a list of values.'] +>>> f.clean([]) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(()) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(['3']) +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] + +>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')], required=False) +>>> f.clean('') +[] +>>> f.clean(None) +[] +>>> f.clean([1]) +[u'1'] +>>> f.clean(['1']) +[u'1'] +>>> f.clean(['1', '2']) +[u'1', u'2'] +>>> f.clean([1, '2']) +[u'1', u'2'] +>>> f.clean((1, '2')) +[u'1', u'2'] +>>> f.clean('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a list of values.'] +>>> f.clean([]) +[] +>>> f.clean(()) +[] +>>> f.clean(['3']) +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] + +# ComboField ################################################################## + +ComboField takes a list of fields that should be used to validate a value, +in that order. +>>> f = ComboField(fields=[CharField(max_length=20), EmailField()]) +>>> f.clean('test@example.com') +u'test@example.com' +>>> f.clean('longemailaddress@example.com') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 20 characters (it has 28).'] +>>> f.clean('not an e-mail') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = ComboField(fields=[CharField(max_length=20), EmailField()], required=False) +>>> f.clean('test@example.com') +u'test@example.com' +>>> f.clean('longemailaddress@example.com') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 20 characters (it has 28).'] +>>> f.clean('not an e-mail') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid e-mail address.'] +>>> f.clean('') +u'' +>>> f.clean(None) +u'' + +# SplitDateTimeField ########################################################## + +>>> f = SplitDateTimeField() +>>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)]) +datetime.datetime(2006, 1, 10, 7, 30) +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a list of values.'] +>>> f.clean(['hello', 'there']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.', u'Enter a valid time.'] +>>> f.clean(['2006-01-10', 'there']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid time.'] +>>> f.clean(['hello', '07:30']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] + +>>> f = SplitDateTimeField(required=False) +>>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)]) +datetime.datetime(2006, 1, 10, 7, 30) +>>> f.clean(['2006-01-10', '07:30']) +datetime.datetime(2006, 1, 10, 7, 30) +>>> f.clean(None) +>>> f.clean('') +>>> f.clean(['']) +>>> f.clean(['', '']) +>>> f.clean('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a list of values.'] +>>> f.clean(['hello', 'there']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.', u'Enter a valid time.'] +>>> f.clean(['2006-01-10', 'there']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid time.'] +>>> f.clean(['hello', '07:30']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean(['2006-01-10', '']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid time.'] +>>> f.clean(['2006-01-10']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid time.'] +>>> f.clean(['', '07:30']) +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +""" diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py new file mode 100644 index 0000000000..ed88e3a6bb --- /dev/null +++ b/tests/regressiontests/forms/forms.py @@ -0,0 +1,1606 @@ +# -*- coding: utf-8 -*- +tests = r""" +>>> from django.newforms import * +>>> import datetime +>>> import time +>>> import re +>>> try: +... from decimal import Decimal +... except ImportError: +... from django.utils._decimal import Decimal + +######### +# Forms # +######### + +A Form is a collection of Fields. It knows how to validate a set of data and it +knows how to render itself in a couple of default ways (e.g., an HTML table). +You can pass it data in __init__(), as a dictionary. + +# Form ######################################################################## + +>>> class Person(Form): +... first_name = CharField() +... last_name = CharField() +... birthday = DateField() + +Pass a dictionary to a Form's __init__(). +>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'}) +>>> p.is_bound +True +>>> p.errors +{} +>>> p.is_valid() +True +>>> p.errors.as_ul() +u'' +>>> p.errors.as_text() +u'' +>>> p.cleaned_data +{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +>>> print p['first_name'] +<input type="text" name="first_name" value="John" id="id_first_name" /> +>>> print p['last_name'] +<input type="text" name="last_name" value="Lennon" id="id_last_name" /> +>>> print p['birthday'] +<input type="text" name="birthday" value="1940-10-9" id="id_birthday" /> +>>> print p['nonexistentfield'] +Traceback (most recent call last): +... +KeyError: "Key 'nonexistentfield' not found in Form" + +>>> for boundfield in p: +... print boundfield +<input type="text" name="first_name" value="John" id="id_first_name" /> +<input type="text" name="last_name" value="Lennon" id="id_last_name" /> +<input type="text" name="birthday" value="1940-10-9" id="id_birthday" /> +>>> for boundfield in p: +... print boundfield.label, boundfield.data +First name John +Last name Lennon +Birthday 1940-10-9 +>>> print p +<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" value="John" id="id_first_name" /></td></tr> +<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" value="Lennon" id="id_last_name" /></td></tr> +<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></td></tr> + +Empty dictionaries are valid, too. +>>> p = Person({}) +>>> p.is_bound +True +>>> p.errors +{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} +>>> p.is_valid() +False +>>> p.cleaned_data +Traceback (most recent call last): +... +AttributeError: 'Person' object has no attribute 'cleaned_data' +>>> print p +<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr> +<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr> +<tr><th><label for="id_birthday">Birthday:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="birthday" id="id_birthday" /></td></tr> +>>> print p.as_table() +<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr> +<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr> +<tr><th><label for="id_birthday">Birthday:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="birthday" id="id_birthday" /></td></tr> +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li> +>>> print p.as_p() +<ul class="errorlist"><li>This field is required.</li></ul> +<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> +<ul class="errorlist"><li>This field is required.</li></ul> +<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p> +<ul class="errorlist"><li>This field is required.</li></ul> +<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p> + +If you don't pass any values to the Form's __init__(), or if you pass None, +the Form will be considered unbound and won't do any validation. Form.errors +will be an empty dictionary *but* Form.is_valid() will return False. +>>> p = Person() +>>> p.is_bound +False +>>> p.errors +{} +>>> p.is_valid() +False +>>> p.cleaned_data +Traceback (most recent call last): +... +AttributeError: 'Person' object has no attribute 'cleaned_data' +>>> print p +<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> +<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> +<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr> +>>> print p.as_table() +<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> +<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> +<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr> +>>> print p.as_ul() +<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> +<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> +<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li> +>>> print p.as_p() +<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> +<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p> +<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p> + +Unicode values are handled properly. +>>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'}) +>>> p.as_table() +u'<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" value="John" id="id_first_name" /></td></tr>\n<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></td></tr>\n<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></td></tr>' +>>> p.as_ul() +u'<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></li>\n<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></li>\n<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></li>' +>>> p.as_p() +u'<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></p>\n<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></p>\n<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></p>' + +>>> p = Person({'last_name': u'Lennon'}) +>>> p.errors +{'first_name': [u'This field is required.'], 'birthday': [u'This field is required.']} +>>> p.is_valid() +False +>>> p.errors.as_ul() +u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is required.</li></ul></li><li>birthday<ul class="errorlist"><li>This field is required.</li></ul></li></ul>' +>>> print p.errors.as_text() +* first_name + * This field is required. +* birthday + * This field is required. +>>> p.cleaned_data +Traceback (most recent call last): +... +AttributeError: 'Person' object has no attribute 'cleaned_data' +>>> p['first_name'].errors +[u'This field is required.'] +>>> p['first_name'].errors.as_ul() +u'<ul class="errorlist"><li>This field is required.</li></ul>' +>>> p['first_name'].errors.as_text() +u'* This field is required.' + +>>> p = Person() +>>> print p['first_name'] +<input type="text" name="first_name" id="id_first_name" /> +>>> print p['last_name'] +<input type="text" name="last_name" id="id_last_name" /> +>>> print p['birthday'] +<input type="text" name="birthday" id="id_birthday" /> + +cleaned_data will always *only* contain a key for fields defined in the +Form, even if you pass extra data when you define the Form. In this +example, we pass a bunch of extra fields to the form constructor, +but cleaned_data contains only the form's fields. +>>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'} +>>> p = Person(data) +>>> p.is_valid() +True +>>> p.cleaned_data +{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} + +cleaned_data will include a key and value for *all* fields defined in the Form, +even if the Form's data didn't include a value for fields that are not +required. In this example, the data dictionary doesn't include a value for the +"nick_name" field, but cleaned_data includes it. For CharFields, it's set to the +empty string. +>>> class OptionalPersonForm(Form): +... first_name = CharField() +... last_name = CharField() +... nick_name = CharField(required=False) +>>> data = {'first_name': u'John', 'last_name': u'Lennon'} +>>> f = OptionalPersonForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} + +For DateFields, it's set to None. +>>> class OptionalPersonForm(Form): +... first_name = CharField() +... last_name = CharField() +... birth_date = DateField(required=False) +>>> data = {'first_name': u'John', 'last_name': u'Lennon'} +>>> f = OptionalPersonForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'} + +"auto_id" tells the Form to add an "id" attribute to each form element. +If it's a string that contains '%s', Django will use that as a format string +into which the field's name will be inserted. It will also put a <label> around +the human-readable labels for a field. +>>> p = Person(auto_id='%s_id') +>>> print p.as_table() +<tr><th><label for="first_name_id">First name:</label></th><td><input type="text" name="first_name" id="first_name_id" /></td></tr> +<tr><th><label for="last_name_id">Last name:</label></th><td><input type="text" name="last_name" id="last_name_id" /></td></tr> +<tr><th><label for="birthday_id">Birthday:</label></th><td><input type="text" name="birthday" id="birthday_id" /></td></tr> +>>> print p.as_ul() +<li><label for="first_name_id">First name:</label> <input type="text" name="first_name" id="first_name_id" /></li> +<li><label for="last_name_id">Last name:</label> <input type="text" name="last_name" id="last_name_id" /></li> +<li><label for="birthday_id">Birthday:</label> <input type="text" name="birthday" id="birthday_id" /></li> +>>> print p.as_p() +<p><label for="first_name_id">First name:</label> <input type="text" name="first_name" id="first_name_id" /></p> +<p><label for="last_name_id">Last name:</label> <input type="text" name="last_name" id="last_name_id" /></p> +<p><label for="birthday_id">Birthday:</label> <input type="text" name="birthday" id="birthday_id" /></p> + +If auto_id is any True value whose str() does not contain '%s', the "id" +attribute will be the name of the field. +>>> p = Person(auto_id=True) +>>> print p.as_ul() +<li><label for="first_name">First name:</label> <input type="text" name="first_name" id="first_name" /></li> +<li><label for="last_name">Last name:</label> <input type="text" name="last_name" id="last_name" /></li> +<li><label for="birthday">Birthday:</label> <input type="text" name="birthday" id="birthday" /></li> + +If auto_id is any False value, an "id" attribute won't be output unless it +was manually entered. +>>> p = Person(auto_id=False) +>>> print p.as_ul() +<li>First name: <input type="text" name="first_name" /></li> +<li>Last name: <input type="text" name="last_name" /></li> +<li>Birthday: <input type="text" name="birthday" /></li> + +In this example, auto_id is False, but the "id" attribute for the "first_name" +field is given. Also note that field gets a <label>, while the others don't. +>>> class PersonNew(Form): +... first_name = CharField(widget=TextInput(attrs={'id': 'first_name_id'})) +... last_name = CharField() +... birthday = DateField() +>>> p = PersonNew(auto_id=False) +>>> print p.as_ul() +<li><label for="first_name_id">First name:</label> <input type="text" id="first_name_id" name="first_name" /></li> +<li>Last name: <input type="text" name="last_name" /></li> +<li>Birthday: <input type="text" name="birthday" /></li> + +If the "id" attribute is specified in the Form and auto_id is True, the "id" +attribute in the Form gets precedence. +>>> p = PersonNew(auto_id=True) +>>> print p.as_ul() +<li><label for="first_name_id">First name:</label> <input type="text" id="first_name_id" name="first_name" /></li> +<li><label for="last_name">Last name:</label> <input type="text" name="last_name" id="last_name" /></li> +<li><label for="birthday">Birthday:</label> <input type="text" name="birthday" id="birthday" /></li> + +>>> class SignupForm(Form): +... email = EmailField() +... get_spam = BooleanField() +>>> f = SignupForm(auto_id=False) +>>> print f['email'] +<input type="text" name="email" /> +>>> print f['get_spam'] +<input type="checkbox" name="get_spam" /> + +>>> f = SignupForm({'email': 'test@example.com', 'get_spam': True}, auto_id=False) +>>> print f['email'] +<input type="text" name="email" value="test@example.com" /> +>>> print f['get_spam'] +<input checked="checked" type="checkbox" name="get_spam" /> + +Any Field can have a Widget class passed to its constructor: +>>> class ContactForm(Form): +... subject = CharField() +... message = CharField(widget=Textarea) +>>> f = ContactForm(auto_id=False) +>>> print f['subject'] +<input type="text" name="subject" /> +>>> print f['message'] +<textarea rows="10" cols="40" name="message"></textarea> + +as_textarea(), as_text() and as_hidden() are shortcuts for changing the output +widget type: +>>> f['subject'].as_textarea() +u'<textarea rows="10" cols="40" name="subject"></textarea>' +>>> f['message'].as_text() +u'<input type="text" name="message" />' +>>> f['message'].as_hidden() +u'<input type="hidden" name="message" />' + +The 'widget' parameter to a Field can also be an instance: +>>> class ContactForm(Form): +... subject = CharField() +... message = CharField(widget=Textarea(attrs={'rows': 80, 'cols': 20})) +>>> f = ContactForm(auto_id=False) +>>> print f['message'] +<textarea rows="80" cols="20" name="message"></textarea> + +Instance-level attrs are *not* carried over to as_textarea(), as_text() and +as_hidden(): +>>> f['message'].as_text() +u'<input type="text" name="message" />' +>>> f = ContactForm({'subject': 'Hello', 'message': 'I love you.'}, auto_id=False) +>>> f['subject'].as_textarea() +u'<textarea rows="10" cols="40" name="subject">Hello</textarea>' +>>> f['message'].as_text() +u'<input type="text" name="message" value="I love you." />' +>>> f['message'].as_hidden() +u'<input type="hidden" name="message" value="I love you." />' + +For a form with a <select>, use ChoiceField: +>>> class FrameworkForm(Form): +... name = CharField() +... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')]) +>>> f = FrameworkForm(auto_id=False) +>>> print f['language'] +<select name="language"> +<option value="P">Python</option> +<option value="J">Java</option> +</select> +>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False) +>>> print f['language'] +<select name="language"> +<option value="P" selected="selected">Python</option> +<option value="J">Java</option> +</select> + +A subtlety: If one of the choices' value is the empty string and the form is +unbound, then the <option> for the empty-string choice will get selected="selected". +>>> class FrameworkForm(Form): +... name = CharField() +... language = ChoiceField(choices=[('', '------'), ('P', 'Python'), ('J', 'Java')]) +>>> f = FrameworkForm(auto_id=False) +>>> print f['language'] +<select name="language"> +<option value="" selected="selected">------</option> +<option value="P">Python</option> +<option value="J">Java</option> +</select> + +You can specify widget attributes in the Widget constructor. +>>> class FrameworkForm(Form): +... name = CharField() +... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(attrs={'class': 'foo'})) +>>> f = FrameworkForm(auto_id=False) +>>> print f['language'] +<select class="foo" name="language"> +<option value="P">Python</option> +<option value="J">Java</option> +</select> +>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False) +>>> print f['language'] +<select class="foo" name="language"> +<option value="P" selected="selected">Python</option> +<option value="J">Java</option> +</select> + +When passing a custom widget instance to ChoiceField, note that setting +'choices' on the widget is meaningless. The widget will use the choices +defined on the Field, not the ones defined on the Widget. +>>> class FrameworkForm(Form): +... name = CharField() +... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(choices=[('R', 'Ruby'), ('P', 'Perl')], attrs={'class': 'foo'})) +>>> f = FrameworkForm(auto_id=False) +>>> print f['language'] +<select class="foo" name="language"> +<option value="P">Python</option> +<option value="J">Java</option> +</select> +>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False) +>>> print f['language'] +<select class="foo" name="language"> +<option value="P" selected="selected">Python</option> +<option value="J">Java</option> +</select> + +You can set a ChoiceField's choices after the fact. +>>> class FrameworkForm(Form): +... name = CharField() +... language = ChoiceField() +>>> f = FrameworkForm(auto_id=False) +>>> print f['language'] +<select name="language"> +</select> +>>> f.fields['language'].choices = [('P', 'Python'), ('J', 'Java')] +>>> print f['language'] +<select name="language"> +<option value="P">Python</option> +<option value="J">Java</option> +</select> + +Add widget=RadioSelect to use that widget with a ChoiceField. +>>> class FrameworkForm(Form): +... name = CharField() +... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect) +>>> f = FrameworkForm(auto_id=False) +>>> print f['language'] +<ul> +<li><label><input type="radio" name="language" value="P" /> Python</label></li> +<li><label><input type="radio" name="language" value="J" /> Java</label></li> +</ul> +>>> print f +<tr><th>Name:</th><td><input type="text" name="name" /></td></tr> +<tr><th>Language:</th><td><ul> +<li><label><input type="radio" name="language" value="P" /> Python</label></li> +<li><label><input type="radio" name="language" value="J" /> Java</label></li> +</ul></td></tr> +>>> print f.as_ul() +<li>Name: <input type="text" name="name" /></li> +<li>Language: <ul> +<li><label><input type="radio" name="language" value="P" /> Python</label></li> +<li><label><input type="radio" name="language" value="J" /> Java</label></li> +</ul></li> + +Regarding auto_id and <label>, RadioSelect is a special case. Each radio button +gets a distinct ID, formed by appending an underscore plus the button's +zero-based index. +>>> f = FrameworkForm(auto_id='id_%s') +>>> print f['language'] +<ul> +<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> +<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> +</ul> + +When RadioSelect is used with auto_id, and the whole form is printed using +either as_table() or as_ul(), the label for the RadioSelect will point to the +ID of the *first* radio button. +>>> print f +<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" /></td></tr> +<tr><th><label for="id_language_0">Language:</label></th><td><ul> +<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> +<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> +</ul></td></tr> +>>> print f.as_ul() +<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li> +<li><label for="id_language_0">Language:</label> <ul> +<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> +<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> +</ul></li> +>>> print f.as_p() +<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p> +<p><label for="id_language_0">Language:</label> <ul> +<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> +<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> +</ul></p> + +MultipleChoiceField is a special case, as its data is required to be a list: +>>> class SongForm(Form): +... name = CharField() +... composers = MultipleChoiceField() +>>> f = SongForm(auto_id=False) +>>> print f['composers'] +<select multiple="multiple" name="composers"> +</select> +>>> class SongForm(Form): +... name = CharField() +... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')]) +>>> f = SongForm(auto_id=False) +>>> print f['composers'] +<select multiple="multiple" name="composers"> +<option value="J">John Lennon</option> +<option value="P">Paul McCartney</option> +</select> +>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False) +>>> print f['name'] +<input type="text" name="name" value="Yesterday" /> +>>> print f['composers'] +<select multiple="multiple" name="composers"> +<option value="J">John Lennon</option> +<option value="P" selected="selected">Paul McCartney</option> +</select> + +MultipleChoiceField rendered as_hidden() is a special case. Because it can +have multiple values, its as_hidden() renders multiple <input type="hidden"> +tags. +>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False) +>>> print f['composers'].as_hidden() +<input type="hidden" name="composers" value="P" /> +>>> f = SongForm({'name': 'From Me To You', 'composers': ['P', 'J']}, auto_id=False) +>>> print f['composers'].as_hidden() +<input type="hidden" name="composers" value="P" /> +<input type="hidden" name="composers" value="J" /> + +MultipleChoiceField can also be used with the CheckboxSelectMultiple widget. +>>> class SongForm(Form): +... name = CharField() +... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=CheckboxSelectMultiple) +>>> f = SongForm(auto_id=False) +>>> print f['composers'] +<ul> +<li><label><input type="checkbox" name="composers" value="J" /> John Lennon</label></li> +<li><label><input type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> +</ul> +>>> f = SongForm({'composers': ['J']}, auto_id=False) +>>> print f['composers'] +<ul> +<li><label><input checked="checked" type="checkbox" name="composers" value="J" /> John Lennon</label></li> +<li><label><input type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> +</ul> +>>> f = SongForm({'composers': ['J', 'P']}, auto_id=False) +>>> print f['composers'] +<ul> +<li><label><input checked="checked" type="checkbox" name="composers" value="J" /> John Lennon</label></li> +<li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> +</ul> + +Regarding auto_id, CheckboxSelectMultiple is a special case. Each checkbox +gets a distinct ID, formed by appending an underscore plus the checkbox's +zero-based index. +>>> f = SongForm(auto_id='%s_id') +>>> print f['composers'] +<ul> +<li><label><input type="checkbox" name="composers" value="J" id="composers_id_0" /> John Lennon</label></li> +<li><label><input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li> +</ul> + +Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict +conveniently work with this. +>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']} +>>> f = SongForm(data) +>>> f.errors +{} +>>> from django.http import QueryDict +>>> data = QueryDict('name=Yesterday&composers=J&composers=P') +>>> f = SongForm(data) +>>> f.errors +{} +>>> from django.utils.datastructures import MultiValueDict +>>> data = MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])) +>>> f = SongForm(data) +>>> f.errors +{} + +The MultipleHiddenInput widget renders multiple values as hidden fields. +>>> class SongFormHidden(Form): +... name = CharField() +... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput) +>>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False) +>>> print f.as_ul() +<li>Name: <input type="text" name="name" value="Yesterday" /><input type="hidden" name="composers" value="J" /> +<input type="hidden" name="composers" value="P" /></li> + +When using CheckboxSelectMultiple, the framework expects a list of input and +returns a list of input. +>>> f = SongForm({'name': 'Yesterday'}, auto_id=False) +>>> f.errors +{'composers': [u'This field is required.']} +>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False) +>>> f.errors +{} +>>> f.cleaned_data +{'composers': [u'J'], 'name': u'Yesterday'} +>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False) +>>> f.errors +{} +>>> f.cleaned_data +{'composers': [u'J', u'P'], 'name': u'Yesterday'} + +Validation errors are HTML-escaped when output as HTML. +>>> class EscapingForm(Form): +... special_name = CharField() +... def clean_special_name(self): +... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name']) + +>>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False) +>>> print f +<tr><th>Special name:</th><td><ul class="errorlist"><li>Something's wrong with 'Nothing to escape'</li></ul><input type="text" name="special_name" value="Nothing to escape" /></td></tr> +>>> f = EscapingForm({'special_name': "Should escape < & > and <script>alert('xss')</script>"}, auto_id=False) +>>> print f +<tr><th>Special name:</th><td><ul class="errorlist"><li>Something's wrong with 'Should escape < & > and <script>alert('xss')</script>'</li></ul><input type="text" name="special_name" value="Should escape < & > and <script>alert('xss')</script>" /></td></tr> + +""" + \ +r""" # [This concatenation is to keep the string below the jython's 32K limit]. +# Validating multiple fields in relation to another ########################### + +There are a couple of ways to do multiple-field validation. If you want the +validation message to be associated with a particular field, implement the +clean_XXX() method on the Form, where XXX is the field name. As in +Field.clean(), the clean_XXX() method should return the cleaned value. In the +clean_XXX() method, you have access to self.cleaned_data, which is a dictionary +of all the data that has been cleaned *so far*, in order by the fields, +including the current field (e.g., the field XXX if you're in clean_XXX()). +>>> class UserRegistration(Form): +... username = CharField(max_length=10) +... password1 = CharField(widget=PasswordInput) +... password2 = CharField(widget=PasswordInput) +... def clean_password2(self): +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: +... raise ValidationError(u'Please make sure your passwords match.') +... return self.cleaned_data['password2'] +>>> f = UserRegistration(auto_id=False) +>>> f.errors +{} +>>> f = UserRegistration({}, auto_id=False) +>>> f.errors +{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']} +>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False) +>>> f.errors +{'password2': [u'Please make sure your passwords match.']} +>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) +>>> f.errors +{} +>>> f.cleaned_data +{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} + +Another way of doing multiple-field validation is by implementing the +Form's clean() method. If you do this, any ValidationError raised by that +method will not be associated with a particular field; it will have a +special-case association with the field named '__all__'. +Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of +all the fields/values that have *not* raised a ValidationError. Also note +Form.clean() is required to return a dictionary of all clean data. +>>> class UserRegistration(Form): +... username = CharField(max_length=10) +... password1 = CharField(widget=PasswordInput) +... password2 = CharField(widget=PasswordInput) +... def clean(self): +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: +... raise ValidationError(u'Please make sure your passwords match.') +... return self.cleaned_data +>>> f = UserRegistration(auto_id=False) +>>> f.errors +{} +>>> f = UserRegistration({}, auto_id=False) +>>> print f.as_table() +<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr> +<tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr> +<tr><th>Password2:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" /></td></tr> +>>> f.errors +{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']} +>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False) +>>> f.errors +{'__all__': [u'Please make sure your passwords match.']} +>>> print f.as_table() +<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> +<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr> +<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr> +<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr> +>>> print f.as_ul() +<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li> +<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li> +<li>Password1: <input type="password" name="password1" value="foo" /></li> +<li>Password2: <input type="password" name="password2" value="bar" /></li> +>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) +>>> f.errors +{} +>>> f.cleaned_data +{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} + +# Dynamic construction ######################################################## + +It's possible to construct a Form dynamically by adding to the self.fields +dictionary in __init__(). Don't forget to call Form.__init__() within the +subclass' __init__(). +>>> class Person(Form): +... first_name = CharField() +... last_name = CharField() +... def __init__(self, *args, **kwargs): +... super(Person, self).__init__(*args, **kwargs) +... self.fields['birthday'] = DateField() +>>> p = Person(auto_id=False) +>>> print p +<tr><th>First name:</th><td><input type="text" name="first_name" /></td></tr> +<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr> +<tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr> + +Instances of a dynamic Form do not persist fields from one Form instance to +the next. +>>> class MyForm(Form): +... def __init__(self, data=None, auto_id=False, field_list=[]): +... Form.__init__(self, data, auto_id=auto_id) +... for field in field_list: +... self.fields[field[0]] = field[1] +>>> field_list = [('field1', CharField()), ('field2', CharField())] +>>> my_form = MyForm(field_list=field_list) +>>> print my_form +<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr> +<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> +>>> field_list = [('field3', CharField()), ('field4', CharField())] +>>> my_form = MyForm(field_list=field_list) +>>> print my_form +<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr> +<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> + +>>> class MyForm(Form): +... default_field_1 = CharField() +... default_field_2 = CharField() +... def __init__(self, data=None, auto_id=False, field_list=[]): +... Form.__init__(self, data, auto_id=auto_id) +... for field in field_list: +... self.fields[field[0]] = field[1] +>>> field_list = [('field1', CharField()), ('field2', CharField())] +>>> my_form = MyForm(field_list=field_list) +>>> print my_form +<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr> +<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr> +<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr> +<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> +>>> field_list = [('field3', CharField()), ('field4', CharField())] +>>> my_form = MyForm(field_list=field_list) +>>> print my_form +<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr> +<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr> +<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr> +<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> + +Similarly, changes to field attributes do not persist from one Form instance +to the next. +>>> class Person(Form): +... first_name = CharField(required=False) +... last_name = CharField(required=False) +... def __init__(self, names_required=False, *args, **kwargs): +... super(Person, self).__init__(*args, **kwargs) +... if names_required: +... self.fields['first_name'].required = True +... self.fields['first_name'].widget.attrs['class'] = 'required' +... self.fields['last_name'].required = True +... self.fields['last_name'].widget.attrs['class'] = 'required' +>>> f = Person(names_required=False) +>>> f['first_name'].field.required, f['last_name'].field.required +(False, False) +>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs +({}, {}) +>>> f = Person(names_required=True) +>>> f['first_name'].field.required, f['last_name'].field.required +(True, True) +>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs +({'class': 'required'}, {'class': 'required'}) +>>> f = Person(names_required=False) +>>> f['first_name'].field.required, f['last_name'].field.required +(False, False) +>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs +({}, {}) +>>> class Person(Form): +... first_name = CharField(max_length=30) +... last_name = CharField(max_length=30) +... def __init__(self, name_max_length=None, *args, **kwargs): +... super(Person, self).__init__(*args, **kwargs) +... if name_max_length: +... self.fields['first_name'].max_length = name_max_length +... self.fields['last_name'].max_length = name_max_length +>>> f = Person(name_max_length=None) +>>> f['first_name'].field.max_length, f['last_name'].field.max_length +(30, 30) +>>> f = Person(name_max_length=20) +>>> f['first_name'].field.max_length, f['last_name'].field.max_length +(20, 20) +>>> f = Person(name_max_length=None) +>>> f['first_name'].field.max_length, f['last_name'].field.max_length +(30, 30) + +HiddenInput widgets are displayed differently in the as_table(), as_ul() +and as_p() output of a Form -- their verbose names are not displayed, and a +separate row is not displayed. They're displayed in the last row of the +form, directly after that row's form element. +>>> class Person(Form): +... first_name = CharField() +... last_name = CharField() +... hidden_text = CharField(widget=HiddenInput) +... birthday = DateField() +>>> p = Person(auto_id=False) +>>> print p +<tr><th>First name:</th><td><input type="text" name="first_name" /></td></tr> +<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr> +<tr><th>Birthday:</th><td><input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></td></tr> +>>> print p.as_ul() +<li>First name: <input type="text" name="first_name" /></li> +<li>Last name: <input type="text" name="last_name" /></li> +<li>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></li> +>>> print p.as_p() +<p>First name: <input type="text" name="first_name" /></p> +<p>Last name: <input type="text" name="last_name" /></p> +<p>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></p> + +With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label. +>>> p = Person(auto_id='id_%s') +>>> print p +<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> +<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> +<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></td></tr> +>>> print p.as_ul() +<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> +<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> +<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></li> +>>> print p.as_p() +<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> +<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p> +<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></p> + +If a field with a HiddenInput has errors, the as_table() and as_ul() output +will include the error message(s) with the text "(Hidden field [fieldname]) " +prepended. This message is displayed at the top of the output, regardless of +its field's order in the form. +>>> p = Person({'first_name': 'John', 'last_name': 'Lennon', 'birthday': '1940-10-9'}, auto_id=False) +>>> print p +<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></td></tr> +<tr><th>First name:</th><td><input type="text" name="first_name" value="John" /></td></tr> +<tr><th>Last name:</th><td><input type="text" name="last_name" value="Lennon" /></td></tr> +<tr><th>Birthday:</th><td><input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></td></tr> +>>> print p.as_ul() +<li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li> +<li>First name: <input type="text" name="first_name" value="John" /></li> +<li>Last name: <input type="text" name="last_name" value="Lennon" /></li> +<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></li> +>>> print p.as_p() +<ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul> +<p>First name: <input type="text" name="first_name" value="John" /></p> +<p>Last name: <input type="text" name="last_name" value="Lennon" /></p> +<p>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></p> + +A corner case: It's possible for a form to have only HiddenInputs. +>>> class TestForm(Form): +... foo = CharField(widget=HiddenInput) +... bar = CharField(widget=HiddenInput) +>>> p = TestForm(auto_id=False) +>>> print p.as_table() +<input type="hidden" name="foo" /><input type="hidden" name="bar" /> +>>> print p.as_ul() +<input type="hidden" name="foo" /><input type="hidden" name="bar" /> +>>> print p.as_p() +<input type="hidden" name="foo" /><input type="hidden" name="bar" /> + +A Form's fields are displayed in the same order in which they were defined. +>>> class TestForm(Form): +... field1 = CharField() +... field2 = CharField() +... field3 = CharField() +... field4 = CharField() +... field5 = CharField() +... field6 = CharField() +... field7 = CharField() +... field8 = CharField() +... field9 = CharField() +... field10 = CharField() +... field11 = CharField() +... field12 = CharField() +... field13 = CharField() +... field14 = CharField() +>>> p = TestForm(auto_id=False) +>>> print p +<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr> +<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> +<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr> +<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> +<tr><th>Field5:</th><td><input type="text" name="field5" /></td></tr> +<tr><th>Field6:</th><td><input type="text" name="field6" /></td></tr> +<tr><th>Field7:</th><td><input type="text" name="field7" /></td></tr> +<tr><th>Field8:</th><td><input type="text" name="field8" /></td></tr> +<tr><th>Field9:</th><td><input type="text" name="field9" /></td></tr> +<tr><th>Field10:</th><td><input type="text" name="field10" /></td></tr> +<tr><th>Field11:</th><td><input type="text" name="field11" /></td></tr> +<tr><th>Field12:</th><td><input type="text" name="field12" /></td></tr> +<tr><th>Field13:</th><td><input type="text" name="field13" /></td></tr> +<tr><th>Field14:</th><td><input type="text" name="field14" /></td></tr> + +Some Field classes have an effect on the HTML attributes of their associated +Widget. If you set max_length in a CharField and its associated widget is +either a TextInput or PasswordInput, then the widget's rendered HTML will +include the "maxlength" attribute. +>>> class UserRegistration(Form): +... username = CharField(max_length=10) # uses TextInput by default +... password = CharField(max_length=10, widget=PasswordInput) +... realname = CharField(max_length=10, widget=TextInput) # redundantly define widget, just to test +... address = CharField() # no max_length defined here +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" maxlength="10" /></li> +<li>Password: <input type="password" name="password" maxlength="10" /></li> +<li>Realname: <input type="text" name="realname" maxlength="10" /></li> +<li>Address: <input type="text" name="address" /></li> + +If you specify a custom "attrs" that includes the "maxlength" attribute, +the Field's max_length attribute will override whatever "maxlength" you specify +in "attrs". +>>> class UserRegistration(Form): +... username = CharField(max_length=10, widget=TextInput(attrs={'maxlength': 20})) +... password = CharField(max_length=10, widget=PasswordInput) +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" maxlength="10" /></li> +<li>Password: <input type="password" name="password" maxlength="10" /></li> + +# Specifying labels ########################################################### + +You can specify the label for a field by using the 'label' argument to a Field +class. If you don't specify 'label', Django will use the field name with +underscores converted to spaces, and the initial letter capitalized. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, label='Your username') +... password1 = CharField(widget=PasswordInput) +... password2 = CharField(widget=PasswordInput, label='Password (again)') +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Your username: <input type="text" name="username" maxlength="10" /></li> +<li>Password1: <input type="password" name="password1" /></li> +<li>Password (again): <input type="password" name="password2" /></li> + +Labels for as_* methods will only end in a colon if they don't end in other +punctuation already. +>>> class Questions(Form): +... q1 = CharField(label='The first question') +... q2 = CharField(label='What is your name?') +... q3 = CharField(label='The answer to life is:') +... q4 = CharField(label='Answer this question!') +... q5 = CharField(label='The last question. Period.') +>>> print Questions(auto_id=False).as_p() +<p>The first question: <input type="text" name="q1" /></p> +<p>What is your name? <input type="text" name="q2" /></p> +<p>The answer to life is: <input type="text" name="q3" /></p> +<p>Answer this question! <input type="text" name="q4" /></p> +<p>The last question. Period. <input type="text" name="q5" /></p> +>>> print Questions().as_p() +<p><label for="id_q1">The first question:</label> <input type="text" name="q1" id="id_q1" /></p> +<p><label for="id_q2">What is your name?</label> <input type="text" name="q2" id="id_q2" /></p> +<p><label for="id_q3">The answer to life is:</label> <input type="text" name="q3" id="id_q3" /></p> +<p><label for="id_q4">Answer this question!</label> <input type="text" name="q4" id="id_q4" /></p> +<p><label for="id_q5">The last question. Period.</label> <input type="text" name="q5" id="id_q5" /></p> + +A label can be a Unicode object or a bytestring with special characters. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, label='ŠĐĆŽćžšđ') +... password = CharField(widget=PasswordInput, label=u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') +>>> p = UserRegistration(auto_id=False) +>>> p.as_ul() +u'<li>\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: <input type="text" name="username" maxlength="10" /></li>\n<li>\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: <input type="password" name="password" /></li>' + +If a label is set to the empty string for a field, that field won't get a label. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, label='') +... password = CharField(widget=PasswordInput) +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li> <input type="text" name="username" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration(auto_id='id_%s') +>>> print p.as_ul() +<li> <input id="id_username" type="text" name="username" maxlength="10" /></li> +<li><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></li> + +If label is None, Django will auto-create the label from the field name. This +is default behavior. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, label=None) +... password = CharField(widget=PasswordInput) +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration(auto_id='id_%s') +>>> print p.as_ul() +<li><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></li> +<li><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></li> + + +# Label Suffix ################################################################ + +You can specify the 'label_suffix' argument to a Form class to modify the +punctuation symbol used at the end of a label. By default, the colon (:) is +used, and is only appended to the label if the label doesn't already end with a +punctuation symbol: ., !, ? or :. If you specify a different suffix, it will +be appended regardless of the last character of the label. + +>>> class FavoriteForm(Form): +... color = CharField(label='Favorite color?') +... animal = CharField(label='Favorite animal') +... +>>> f = FavoriteForm(auto_id=False) +>>> print f.as_ul() +<li>Favorite color? <input type="text" name="color" /></li> +<li>Favorite animal: <input type="text" name="animal" /></li> +>>> f = FavoriteForm(auto_id=False, label_suffix='?') +>>> print f.as_ul() +<li>Favorite color? <input type="text" name="color" /></li> +<li>Favorite animal? <input type="text" name="animal" /></li> +>>> f = FavoriteForm(auto_id=False, label_suffix='') +>>> print f.as_ul() +<li>Favorite color? <input type="text" name="color" /></li> +<li>Favorite animal <input type="text" name="animal" /></li> +>>> f = FavoriteForm(auto_id=False, label_suffix=u'\u2192') +>>> f.as_ul() +u'<li>Favorite color? <input type="text" name="color" /></li>\n<li>Favorite animal\u2192 <input type="text" name="animal" /></li>' + +""" + \ +r""" # [This concatenation is to keep the string below the jython's 32K limit]. + +# Initial data ################################################################ + +You can specify initial data for a field by using the 'initial' argument to a +Field class. This initial data is displayed when a Form is rendered with *no* +data. It is not displayed when a Form is rendered with any data (including an +empty dictionary). Also, the initial value is *not* used if data for a +particular required field isn't provided. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, initial='django') +... password = CharField(widget=PasswordInput) + +Here, we're not submitting any data, so the initial value will be displayed. +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + +Here, we're submitting data, so the initial value will *not* be displayed. +>>> p = UserRegistration({}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u''}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u'foo'}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> + +An 'initial' value is *not* used as a fallback if data is not provided. In this +example, we don't provide a value for 'username', and the form raises a +validation error rather than using the initial value for 'username'. +>>> p = UserRegistration({'password': 'secret'}) +>>> p.errors +{'username': [u'This field is required.']} +>>> p.is_valid() +False + +# Dynamic initial data ######################################################## + +The previous technique dealt with "hard-coded" initial data, but it's also +possible to specify initial data after you've already created the Form class +(i.e., at runtime). Use the 'initial' parameter to the Form constructor. This +should be a dictionary containing initial values for one or more fields in the +form, keyed by field name. + +>>> class UserRegistration(Form): +... username = CharField(max_length=10) +... password = CharField(widget=PasswordInput) + +Here, we're not submitting any data, so the initial value will be displayed. +>>> p = UserRegistration(initial={'username': 'django'}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration(initial={'username': 'stephane'}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + +The 'initial' parameter is meaningless if you pass data. +>>> p = UserRegistration({}, initial={'username': 'django'}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u''}, initial={'username': 'django'}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u'foo'}, initial={'username': 'django'}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> + +A dynamic 'initial' value is *not* used as a fallback if data is not provided. +In this example, we don't provide a value for 'username', and the form raises a +validation error rather than using the initial value for 'username'. +>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'}) +>>> p.errors +{'username': [u'This field is required.']} +>>> p.is_valid() +False + +If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(), +then the latter will get precedence. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, initial='django') +... password = CharField(widget=PasswordInput) +>>> p = UserRegistration(initial={'username': 'babik'}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + +# Callable initial data ######################################################## + +The previous technique dealt with raw values as initial data, but it's also +possible to specify callable data. + +>>> class UserRegistration(Form): +... username = CharField(max_length=10) +... password = CharField(widget=PasswordInput) + +We need to define functions that get called later. +>>> def initial_django(): +... return 'django' +>>> def initial_stephane(): +... return 'stephane' + +Here, we're not submitting any data, so the initial value will be displayed. +>>> p = UserRegistration(initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + +The 'initial' parameter is meaningless if you pass data. +>>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u'foo'}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> + +A callable 'initial' value is *not* used as a fallback if data is not provided. +In this example, we don't provide a value for 'username', and the form raises a +validation error rather than using the initial value for 'username'. +>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django}) +>>> p.errors +{'username': [u'This field is required.']} +>>> p.is_valid() +False + +If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(), +then the latter will get precedence. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, initial=initial_django) +... password = CharField(widget=PasswordInput) +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + +# Help text ################################################################### + +You can specify descriptive text for a field by using the 'help_text' argument +to a Field class. This help text is displayed when a Form is rendered. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, help_text='e.g., user@example.com') +... password = CharField(widget=PasswordInput, help_text='Choose wisely.') +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li> +<li>Password: <input type="password" name="password" /> Choose wisely.</li> +>>> print p.as_p() +<p>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</p> +<p>Password: <input type="password" name="password" /> Choose wisely.</p> +>>> print p.as_table() +<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br />e.g., user@example.com</td></tr> +<tr><th>Password:</th><td><input type="password" name="password" /><br />Choose wisely.</td></tr> + +The help text is displayed whether or not data is provided for the form. +>>> p = UserRegistration({'username': u'foo'}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> e.g., user@example.com</li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> Choose wisely.</li> + +help_text is not displayed for hidden fields. It can be used for documentation +purposes, though. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, help_text='e.g., user@example.com') +... password = CharField(widget=PasswordInput) +... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination') +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li> +<li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li> + +Help text can include arbitrary Unicode characters. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, help_text='ŠĐĆŽćžšđ') +>>> p = UserRegistration(auto_id=False) +>>> p.as_ul() +u'<li>Username: <input type="text" name="username" maxlength="10" /> \u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</li>' + +# Subclassing forms ########################################################### + +You can subclass a Form to add fields. The resulting form subclass will have +all of the fields of the parent Form, plus whichever fields you define in the +subclass. +>>> class Person(Form): +... first_name = CharField() +... last_name = CharField() +... birthday = DateField() +>>> class Musician(Person): +... instrument = CharField() +>>> p = Person(auto_id=False) +>>> print p.as_ul() +<li>First name: <input type="text" name="first_name" /></li> +<li>Last name: <input type="text" name="last_name" /></li> +<li>Birthday: <input type="text" name="birthday" /></li> +>>> m = Musician(auto_id=False) +>>> print m.as_ul() +<li>First name: <input type="text" name="first_name" /></li> +<li>Last name: <input type="text" name="last_name" /></li> +<li>Birthday: <input type="text" name="birthday" /></li> +<li>Instrument: <input type="text" name="instrument" /></li> + +Yes, you can subclass multiple forms. The fields are added in the order in +which the parent classes are listed. +>>> class Person(Form): +... first_name = CharField() +... last_name = CharField() +... birthday = DateField() +>>> class Instrument(Form): +... instrument = CharField() +>>> class Beatle(Person, Instrument): +... haircut_type = CharField() +>>> b = Beatle(auto_id=False) +>>> print b.as_ul() +<li>First name: <input type="text" name="first_name" /></li> +<li>Last name: <input type="text" name="last_name" /></li> +<li>Birthday: <input type="text" name="birthday" /></li> +<li>Instrument: <input type="text" name="instrument" /></li> +<li>Haircut type: <input type="text" name="haircut_type" /></li> + +# Forms with prefixes ######################################################### + +Sometimes it's necessary to have multiple forms display on the same HTML page, +or multiple copies of the same form. We can accomplish this with form prefixes. +Pass the keyword argument 'prefix' to the Form constructor to use this feature. +This value will be prepended to each HTML form field name. One way to think +about this is "namespaces for HTML forms". Notice that in the data argument, +each field's key has the prefix, in this case 'person1', prepended to the +actual field name. +>>> class Person(Form): +... first_name = CharField() +... last_name = CharField() +... birthday = DateField() +>>> data = { +... 'person1-first_name': u'John', +... 'person1-last_name': u'Lennon', +... 'person1-birthday': u'1940-10-9' +... } +>>> p = Person(data, prefix='person1') +>>> print p.as_ul() +<li><label for="id_person1-first_name">First name:</label> <input type="text" name="person1-first_name" value="John" id="id_person1-first_name" /></li> +<li><label for="id_person1-last_name">Last name:</label> <input type="text" name="person1-last_name" value="Lennon" id="id_person1-last_name" /></li> +<li><label for="id_person1-birthday">Birthday:</label> <input type="text" name="person1-birthday" value="1940-10-9" id="id_person1-birthday" /></li> +>>> print p['first_name'] +<input type="text" name="person1-first_name" value="John" id="id_person1-first_name" /> +>>> print p['last_name'] +<input type="text" name="person1-last_name" value="Lennon" id="id_person1-last_name" /> +>>> print p['birthday'] +<input type="text" name="person1-birthday" value="1940-10-9" id="id_person1-birthday" /> +>>> p.errors +{} +>>> p.is_valid() +True +>>> p.cleaned_data +{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} + +Let's try submitting some bad data to make sure form.errors and field.errors +work as expected. +>>> data = { +... 'person1-first_name': u'', +... 'person1-last_name': u'', +... 'person1-birthday': u'' +... } +>>> p = Person(data, prefix='person1') +>>> p.errors +{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} +>>> p['first_name'].errors +[u'This field is required.'] +>>> p['person1-first_name'].errors +Traceback (most recent call last): +... +KeyError: "Key 'person1-first_name' not found in Form" + +In this example, the data doesn't have a prefix, but the form requires it, so +the form doesn't "see" the fields. +>>> data = { +... 'first_name': u'John', +... 'last_name': u'Lennon', +... 'birthday': u'1940-10-9' +... } +>>> p = Person(data, prefix='person1') +>>> p.errors +{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} + +With prefixes, a single data dictionary can hold data for multiple instances +of the same form. +>>> data = { +... 'person1-first_name': u'John', +... 'person1-last_name': u'Lennon', +... 'person1-birthday': u'1940-10-9', +... 'person2-first_name': u'Jim', +... 'person2-last_name': u'Morrison', +... 'person2-birthday': u'1943-12-8' +... } +>>> p1 = Person(data, prefix='person1') +>>> p1.is_valid() +True +>>> p1.cleaned_data +{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +>>> p2 = Person(data, prefix='person2') +>>> p2.is_valid() +True +>>> p2.cleaned_data +{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)} + +By default, forms append a hyphen between the prefix and the field name, but a +form can alter that behavior by implementing the add_prefix() method. This +method takes a field name and returns the prefixed field, according to +self.prefix. +>>> class Person(Form): +... first_name = CharField() +... last_name = CharField() +... birthday = DateField() +... def add_prefix(self, field_name): +... return self.prefix and '%s-prefix-%s' % (self.prefix, field_name) or field_name +>>> p = Person(prefix='foo') +>>> print p.as_ul() +<li><label for="id_foo-prefix-first_name">First name:</label> <input type="text" name="foo-prefix-first_name" id="id_foo-prefix-first_name" /></li> +<li><label for="id_foo-prefix-last_name">Last name:</label> <input type="text" name="foo-prefix-last_name" id="id_foo-prefix-last_name" /></li> +<li><label for="id_foo-prefix-birthday">Birthday:</label> <input type="text" name="foo-prefix-birthday" id="id_foo-prefix-birthday" /></li> +>>> data = { +... 'foo-prefix-first_name': u'John', +... 'foo-prefix-last_name': u'Lennon', +... 'foo-prefix-birthday': u'1940-10-9' +... } +>>> p = Person(data, prefix='foo') +>>> p.is_valid() +True +>>> p.cleaned_data +{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} + +# Forms with NullBooleanFields ################################################ + +NullBooleanField is a bit of a special case because its presentation (widget) +is different than its data. This is handled transparently, though. + +>>> class Person(Form): +... name = CharField() +... is_cool = NullBooleanField() +>>> p = Person({'name': u'Joe'}, auto_id=False) +>>> print p['is_cool'] +<select name="is_cool"> +<option value="1" selected="selected">Unknown</option> +<option value="2">Yes</option> +<option value="3">No</option> +</select> +>>> p = Person({'name': u'Joe', 'is_cool': u'1'}, auto_id=False) +>>> print p['is_cool'] +<select name="is_cool"> +<option value="1" selected="selected">Unknown</option> +<option value="2">Yes</option> +<option value="3">No</option> +</select> +>>> p = Person({'name': u'Joe', 'is_cool': u'2'}, auto_id=False) +>>> print p['is_cool'] +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2" selected="selected">Yes</option> +<option value="3">No</option> +</select> +>>> p = Person({'name': u'Joe', 'is_cool': u'3'}, auto_id=False) +>>> print p['is_cool'] +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2">Yes</option> +<option value="3" selected="selected">No</option> +</select> +>>> p = Person({'name': u'Joe', 'is_cool': True}, auto_id=False) +>>> print p['is_cool'] +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2" selected="selected">Yes</option> +<option value="3">No</option> +</select> +>>> p = Person({'name': u'Joe', 'is_cool': False}, auto_id=False) +>>> print p['is_cool'] +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2">Yes</option> +<option value="3" selected="selected">No</option> +</select> + +# Forms with FileFields ################################################ + +FileFields are a special case because they take their data from the request.FILES, +not request.POST. + +>>> class FileForm(Form): +... file1 = FileField() +>>> f = FileForm(auto_id=False) +>>> print f +<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> + +>>> f = FileForm(data={}, files={}, auto_id=False) +>>> print f +<tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> + +>>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False) +>>> print f +<tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> + +>>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False) +>>> print f +<tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr> + +>>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False) +>>> print f +<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> +>>> f.is_valid() +True + +# Basic form processing in a view ############################################# + +>>> from django.template import Template, Context +>>> class UserRegistration(Form): +... username = CharField(max_length=10) +... password1 = CharField(widget=PasswordInput) +... password2 = CharField(widget=PasswordInput) +... def clean(self): +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: +... raise ValidationError(u'Please make sure your passwords match.') +... return self.cleaned_data +>>> def my_function(method, post_data): +... if method == 'POST': +... form = UserRegistration(post_data, auto_id=False) +... else: +... form = UserRegistration(auto_id=False) +... if form.is_valid(): +... return 'VALID: %r' % form.cleaned_data +... t = Template('<form action="" method="post">\n<table>\n{{ form }}\n</table>\n<input type="submit" />\n</form>') +... return t.render(Context({'form': form})) + +Case 1: GET (an empty form, with no errors). +>>> print my_function('GET', {}) +<form action="" method="post"> +<table> +<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /></td></tr> +<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr> +<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr> +</table> +<input type="submit" /> +</form> + +Case 2: POST with erroneous data (a redisplayed form, with errors). +>>> print my_function('POST', {'username': 'this-is-a-long-username', 'password1': 'foo', 'password2': 'bar'}) +<form action="" method="post"> +<table> +<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> +<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr> +<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr> +<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr> +</table> +<input type="submit" /> +</form> + +Case 3: POST with valid data (the success message). +>>> print my_function('POST', {'username': 'adrian', 'password1': 'secret', 'password2': 'secret'}) +VALID: {'username': u'adrian', 'password1': u'secret', 'password2': u'secret'} + +# Some ideas for using templates with forms ################################### + +>>> class UserRegistration(Form): +... username = CharField(max_length=10, help_text="Good luck picking a username that doesn't already exist.") +... password1 = CharField(widget=PasswordInput) +... password2 = CharField(widget=PasswordInput) +... def clean(self): +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: +... raise ValidationError(u'Please make sure your passwords match.') +... return self.cleaned_data + +You have full flexibility in displaying form fields in a template. Just pass a +Form instance to the template, and use "dot" access to refer to individual +fields. Note, however, that this flexibility comes with the responsibility of +displaying all the errors, including any that might not be associated with a +particular field. +>>> t = Template('''<form action=""> +... {{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p> +... {{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p> +... {{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p> +... <input type="submit" /> +... </form>''') +>>> print t.render(Context({'form': UserRegistration(auto_id=False)})) +<form action=""> +<p><label>Your username: <input type="text" name="username" maxlength="10" /></label></p> +<p><label>Password: <input type="password" name="password1" /></label></p> +<p><label>Password (again): <input type="password" name="password2" /></label></p> +<input type="submit" /> +</form> +>>> print t.render(Context({'form': UserRegistration({'username': 'django'}, auto_id=False)})) +<form action=""> +<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> +<ul class="errorlist"><li>This field is required.</li></ul><p><label>Password: <input type="password" name="password1" /></label></p> +<ul class="errorlist"><li>This field is required.</li></ul><p><label>Password (again): <input type="password" name="password2" /></label></p> +<input type="submit" /> +</form> + +Use form.[field].label to output a field's label. You can specify the label for +a field by using the 'label' argument to a Field class. If you don't specify +'label', Django will use the field name with underscores converted to spaces, +and the initial letter capitalized. +>>> t = Template('''<form action=""> +... <p><label>{{ form.username.label }}: {{ form.username }}</label></p> +... <p><label>{{ form.password1.label }}: {{ form.password1 }}</label></p> +... <p><label>{{ form.password2.label }}: {{ form.password2 }}</label></p> +... <input type="submit" /> +... </form>''') +>>> print t.render(Context({'form': UserRegistration(auto_id=False)})) +<form action=""> +<p><label>Username: <input type="text" name="username" maxlength="10" /></label></p> +<p><label>Password1: <input type="password" name="password1" /></label></p> +<p><label>Password2: <input type="password" name="password2" /></label></p> +<input type="submit" /> +</form> + +User form.[field].label_tag to output a field's label with a <label> tag +wrapped around it, but *only* if the given field has an "id" attribute. +Recall from above that passing the "auto_id" argument to a Form gives each +field an "id" attribute. +>>> t = Template('''<form action=""> +... <p>{{ form.username.label_tag }}: {{ form.username }}</p> +... <p>{{ form.password1.label_tag }}: {{ form.password1 }}</p> +... <p>{{ form.password2.label_tag }}: {{ form.password2 }}</p> +... <input type="submit" /> +... </form>''') +>>> print t.render(Context({'form': UserRegistration(auto_id=False)})) +<form action=""> +<p>Username: <input type="text" name="username" maxlength="10" /></p> +<p>Password1: <input type="password" name="password1" /></p> +<p>Password2: <input type="password" name="password2" /></p> +<input type="submit" /> +</form> +>>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')})) +<form action=""> +<p><label for="id_username">Username</label>: <input id="id_username" type="text" name="username" maxlength="10" /></p> +<p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p> +<p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p> +<input type="submit" /> +</form> + +User form.[field].help_text to output a field's help text. If the given field +does not have help text, nothing will be output. +>>> t = Template('''<form action=""> +... <p>{{ form.username.label_tag }}: {{ form.username }}<br />{{ form.username.help_text }}</p> +... <p>{{ form.password1.label_tag }}: {{ form.password1 }}</p> +... <p>{{ form.password2.label_tag }}: {{ form.password2 }}</p> +... <input type="submit" /> +... </form>''') +>>> print t.render(Context({'form': UserRegistration(auto_id=False)})) +<form action=""> +<p>Username: <input type="text" name="username" maxlength="10" /><br />Good luck picking a username that doesn't already exist.</p> +<p>Password1: <input type="password" name="password1" /></p> +<p>Password2: <input type="password" name="password2" /></p> +<input type="submit" /> +</form> +>>> Template('{{ form.password1.help_text }}').render(Context({'form': UserRegistration(auto_id=False)})) +u'' + +The label_tag() method takes an optional attrs argument: a dictionary of HTML +attributes to add to the <label> tag. +>>> f = UserRegistration(auto_id='id_%s') +>>> for bf in f: +... print bf.label_tag(attrs={'class': 'pretty'}) +<label for="id_username" class="pretty">Username</label> +<label for="id_password1" class="pretty">Password1</label> +<label for="id_password2" class="pretty">Password2</label> + +To display the errors that aren't associated with a particular field -- e.g., +the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the +template. If used on its own, it is displayed as a <ul> (or an empty string, if +the list of errors is empty). You can also use it in {% if %} statements. +>>> t = Template('''<form action=""> +... {{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p> +... {{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p> +... {{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p> +... <input type="submit" /> +... </form>''') +>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)})) +<form action=""> +<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> +<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> +<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> +<input type="submit" /> +</form> +>>> t = Template('''<form action=""> +... {{ form.non_field_errors }} +... {{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p> +... {{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p> +... {{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p> +... <input type="submit" /> +... </form>''') +>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)})) +<form action=""> +<ul class="errorlist"><li>Please make sure your passwords match.</li></ul> +<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> +<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> +<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> +<input type="submit" /> +</form> +""" diff --git a/tests/regressiontests/forms/localflavor.py b/tests/regressiontests/forms/localflavor.py deleted file mode 100644 index 2fe15847c7..0000000000 --- a/tests/regressiontests/forms/localflavor.py +++ /dev/null @@ -1,2197 +0,0 @@ -# -*- coding: utf-8 -*- -# Tests for the different contrib/localflavor/ form fields. - -localflavor_tests = r""" -# USZipCodeField ############################################################## - -USZipCodeField validates that the data is either a five-digit U.S. zip code or -a zip+4. ->>> from django.contrib.localflavor.us.forms import USZipCodeField ->>> f = USZipCodeField() ->>> f.clean('60606') -u'60606' ->>> f.clean(60606) -u'60606' ->>> f.clean('04000') -u'04000' ->>> f.clean('4000') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-1234') -u'60606-1234' ->>> f.clean('6060-1234') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = USZipCodeField(required=False) ->>> f.clean('60606') -u'60606' ->>> f.clean(60606) -u'60606' ->>> f.clean('04000') -u'04000' ->>> f.clean('4000') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-1234') -u'60606-1234' ->>> f.clean('6060-1234') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# USPhoneNumberField ########################################################## - -USPhoneNumberField validates that the data is a valid U.S. phone number, -including the area code. It's normalized to XXX-XXX-XXXX format. ->>> from django.contrib.localflavor.us.forms import USPhoneNumberField ->>> f = USPhoneNumberField() ->>> f.clean('312-555-1212') -u'312-555-1212' ->>> f.clean('3125551212') -u'312-555-1212' ->>> f.clean('312 555-1212') -u'312-555-1212' ->>> f.clean('(312) 555-1212') -u'312-555-1212' ->>> f.clean('312 555 1212') -u'312-555-1212' ->>> f.clean('312.555.1212') -u'312-555-1212' ->>> f.clean('312.555-1212') -u'312-555-1212' ->>> f.clean(' (312) 555.1212 ') -u'312-555-1212' ->>> f.clean('555-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean('312-55-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = USPhoneNumberField(required=False) ->>> f.clean('312-555-1212') -u'312-555-1212' ->>> f.clean('3125551212') -u'312-555-1212' ->>> f.clean('312 555-1212') -u'312-555-1212' ->>> f.clean('(312) 555-1212') -u'312-555-1212' ->>> f.clean('312 555 1212') -u'312-555-1212' ->>> f.clean('312.555.1212') -u'312-555-1212' ->>> f.clean('312.555-1212') -u'312-555-1212' ->>> f.clean(' (312) 555.1212 ') -u'312-555-1212' ->>> f.clean('555-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean('312-55-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# USStateField ################################################################ - -USStateField validates that the data is either an abbreviation or name of a -U.S. state. ->>> from django.contrib.localflavor.us.forms import USStateField ->>> f = USStateField() ->>> f.clean('il') -u'IL' ->>> f.clean('IL') -u'IL' ->>> f.clean('illinois') -u'IL' ->>> f.clean(' illinois ') -u'IL' ->>> f.clean(60606) -Traceback (most recent call last): -... -ValidationError: [u'Enter a U.S. state or territory.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = USStateField(required=False) ->>> f.clean('il') -u'IL' ->>> f.clean('IL') -u'IL' ->>> f.clean('illinois') -u'IL' ->>> f.clean(' illinois ') -u'IL' ->>> f.clean(60606) -Traceback (most recent call last): -... -ValidationError: [u'Enter a U.S. state or territory.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# USStateSelect ############################################################### - -USStateSelect is a Select widget that uses a list of U.S. states/territories -as its choices. ->>> from django.contrib.localflavor.us.forms import USStateSelect ->>> w = USStateSelect() ->>> print w.render('state', 'IL') -<select name="state"> -<option value="AL">Alabama</option> -<option value="AK">Alaska</option> -<option value="AS">American Samoa</option> -<option value="AZ">Arizona</option> -<option value="AR">Arkansas</option> -<option value="CA">California</option> -<option value="CO">Colorado</option> -<option value="CT">Connecticut</option> -<option value="DE">Delaware</option> -<option value="DC">District of Columbia</option> -<option value="FM">Federated States of Micronesia</option> -<option value="FL">Florida</option> -<option value="GA">Georgia</option> -<option value="GU">Guam</option> -<option value="HI">Hawaii</option> -<option value="ID">Idaho</option> -<option value="IL" selected="selected">Illinois</option> -<option value="IN">Indiana</option> -<option value="IA">Iowa</option> -<option value="KS">Kansas</option> -<option value="KY">Kentucky</option> -<option value="LA">Louisiana</option> -<option value="ME">Maine</option> -<option value="MH">Marshall Islands</option> -<option value="MD">Maryland</option> -<option value="MA">Massachusetts</option> -<option value="MI">Michigan</option> -<option value="MN">Minnesota</option> -<option value="MS">Mississippi</option> -<option value="MO">Missouri</option> -<option value="MT">Montana</option> -<option value="NE">Nebraska</option> -<option value="NV">Nevada</option> -<option value="NH">New Hampshire</option> -<option value="NJ">New Jersey</option> -<option value="NM">New Mexico</option> -<option value="NY">New York</option> -<option value="NC">North Carolina</option> -<option value="ND">North Dakota</option> -<option value="MP">Northern Mariana Islands</option> -<option value="OH">Ohio</option> -<option value="OK">Oklahoma</option> -<option value="OR">Oregon</option> -<option value="PW">Palau</option> -<option value="PA">Pennsylvania</option> -<option value="PR">Puerto Rico</option> -<option value="RI">Rhode Island</option> -<option value="SC">South Carolina</option> -<option value="SD">South Dakota</option> -<option value="TN">Tennessee</option> -<option value="TX">Texas</option> -<option value="UT">Utah</option> -<option value="VT">Vermont</option> -<option value="VI">Virgin Islands</option> -<option value="VA">Virginia</option> -<option value="WA">Washington</option> -<option value="WV">West Virginia</option> -<option value="WI">Wisconsin</option> -<option value="WY">Wyoming</option> -</select> - -# USSocialSecurityNumberField ################################################# ->>> from django.contrib.localflavor.us.forms import USSocialSecurityNumberField ->>> f = USSocialSecurityNumberField() ->>> f.clean('987-65-4330') -u'987-65-4330' ->>> f.clean('987654330') -u'987-65-4330' ->>> f.clean('078-05-1120') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid U.S. Social Security number in XXX-XX-XXXX format.'] - -# UKPostcodeField ############################################################# - -UKPostcodeField validates that the data is a valid UK postcode. ->>> from django.contrib.localflavor.uk.forms import UKPostcodeField ->>> f = UKPostcodeField() ->>> f.clean('BT32 4PX') -u'BT32 4PX' ->>> f.clean('GIR 0AA') -u'GIR 0AA' ->>> f.clean('BT324PX') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean('1NV 4L1D') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = UKPostcodeField(required=False) ->>> f.clean('BT32 4PX') -u'BT32 4PX' ->>> f.clean('GIR 0AA') -u'GIR 0AA' ->>> f.clean('1NV 4L1D') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean('BT324PX') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# FRZipCodeField ############################################################# - -FRZipCodeField validates that the data is a valid FR zipcode. ->>> from django.contrib.localflavor.fr.forms import FRZipCodeField ->>> f = FRZipCodeField() ->>> f.clean('75001') -u'75001' ->>> f.clean('93200') -u'93200' ->>> f.clean('2A200') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean('980001') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = FRZipCodeField(required=False) ->>> f.clean('75001') -u'75001' ->>> f.clean('93200') -u'93200' ->>> f.clean('2A200') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean('980001') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - - -# FRPhoneNumberField ########################################################## - -FRPhoneNumberField validates that the data is a valid french phone number. -It's normalized to 0X XX XX XX XX format. Dots are valid too. ->>> from django.contrib.localflavor.fr.forms import FRPhoneNumberField ->>> f = FRPhoneNumberField() ->>> f.clean('01 55 44 58 64') -u'01 55 44 58 64' ->>> f.clean('0155445864') -u'01 55 44 58 64' ->>> f.clean('01 5544 5864') -u'01 55 44 58 64' ->>> f.clean('01 55.44.58.64') -u'01 55 44 58 64' ->>> f.clean('01.55.44.58.64') -u'01 55 44 58 64' ->>> f.clean('01,55,44,58,64') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] ->>> f.clean('555 015 544') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = FRPhoneNumberField(required=False) ->>> f.clean('01 55 44 58 64') -u'01 55 44 58 64' ->>> f.clean('0155445864') -u'01 55 44 58 64' ->>> f.clean('01 5544 5864') -u'01 55 44 58 64' ->>> f.clean('01 55.44.58.64') -u'01 55 44 58 64' ->>> f.clean('01.55.44.58.64') -u'01 55 44 58 64' ->>> f.clean('01,55,44,58,64') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] ->>> f.clean('555 015 544') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# FRDepartmentSelect ############################################################### - -FRDepartmentSelect is a Select widget that uses a list of french departments -including DOM TOM ->>> from django.contrib.localflavor.fr.forms import FRDepartmentSelect ->>> w = FRDepartmentSelect() ->>> print w.render('dep', 'Paris') -<select name="dep"> -<option value="01">01 - Ain</option> -<option value="02">02 - Aisne</option> -<option value="03">03 - Allier</option> -<option value="04">04 - Alpes-de-Haute-Provence</option> -<option value="05">05 - Hautes-Alpes</option> -<option value="06">06 - Alpes-Maritimes</option> -<option value="07">07 - Ardeche</option> -<option value="08">08 - Ardennes</option> -<option value="09">09 - Ariege</option> -<option value="10">10 - Aube</option> -<option value="11">11 - Aude</option> -<option value="12">12 - Aveyron</option> -<option value="13">13 - Bouches-du-Rhone</option> -<option value="14">14 - Calvados</option> -<option value="15">15 - Cantal</option> -<option value="16">16 - Charente</option> -<option value="17">17 - Charente-Maritime</option> -<option value="18">18 - Cher</option> -<option value="19">19 - Correze</option> -<option value="21">21 - Cote-d'Or</option> -<option value="22">22 - Cotes-d'Armor</option> -<option value="23">23 - Creuse</option> -<option value="24">24 - Dordogne</option> -<option value="25">25 - Doubs</option> -<option value="26">26 - Drome</option> -<option value="27">27 - Eure</option> -<option value="28">28 - Eure-et-Loire</option> -<option value="29">29 - Finistere</option> -<option value="2A">2A - Corse-du-Sud</option> -<option value="2B">2B - Haute-Corse</option> -<option value="30">30 - Gard</option> -<option value="31">31 - Haute-Garonne</option> -<option value="32">32 - Gers</option> -<option value="33">33 - Gironde</option> -<option value="34">34 - Herault</option> -<option value="35">35 - Ille-et-Vilaine</option> -<option value="36">36 - Indre</option> -<option value="37">37 - Indre-et-Loire</option> -<option value="38">38 - Isere</option> -<option value="39">39 - Jura</option> -<option value="40">40 - Landes</option> -<option value="41">41 - Loir-et-Cher</option> -<option value="42">42 - Loire</option> -<option value="43">43 - Haute-Loire</option> -<option value="44">44 - Loire-Atlantique</option> -<option value="45">45 - Loiret</option> -<option value="46">46 - Lot</option> -<option value="47">47 - Lot-et-Garonne</option> -<option value="48">48 - Lozere</option> -<option value="49">49 - Maine-et-Loire</option> -<option value="50">50 - Manche</option> -<option value="51">51 - Marne</option> -<option value="52">52 - Haute-Marne</option> -<option value="53">53 - Mayenne</option> -<option value="54">54 - Meurthe-et-Moselle</option> -<option value="55">55 - Meuse</option> -<option value="56">56 - Morbihan</option> -<option value="57">57 - Moselle</option> -<option value="58">58 - Nievre</option> -<option value="59">59 - Nord</option> -<option value="60">60 - Oise</option> -<option value="61">61 - Orne</option> -<option value="62">62 - Pas-de-Calais</option> -<option value="63">63 - Puy-de-Dome</option> -<option value="64">64 - Pyrenees-Atlantiques</option> -<option value="65">65 - Hautes-Pyrenees</option> -<option value="66">66 - Pyrenees-Orientales</option> -<option value="67">67 - Bas-Rhin</option> -<option value="68">68 - Haut-Rhin</option> -<option value="69">69 - Rhone</option> -<option value="70">70 - Haute-Saone</option> -<option value="71">71 - Saone-et-Loire</option> -<option value="72">72 - Sarthe</option> -<option value="73">73 - Savoie</option> -<option value="74">74 - Haute-Savoie</option> -<option value="75">75 - Paris</option> -<option value="76">76 - Seine-Maritime</option> -<option value="77">77 - Seine-et-Marne</option> -<option value="78">78 - Yvelines</option> -<option value="79">79 - Deux-Sevres</option> -<option value="80">80 - Somme</option> -<option value="81">81 - Tarn</option> -<option value="82">82 - Tarn-et-Garonne</option> -<option value="83">83 - Var</option> -<option value="84">84 - Vaucluse</option> -<option value="85">85 - Vendee</option> -<option value="86">86 - Vienne</option> -<option value="87">87 - Haute-Vienne</option> -<option value="88">88 - Vosges</option> -<option value="89">89 - Yonne</option> -<option value="90">90 - Territoire de Belfort</option> -<option value="91">91 - Essonne</option> -<option value="92">92 - Hauts-de-Seine</option> -<option value="93">93 - Seine-Saint-Denis</option> -<option value="94">94 - Val-de-Marne</option> -<option value="95">95 - Val-d'Oise</option> -<option value="2A">2A - Corse du sud</option> -<option value="2B">2B - Haute Corse</option> -<option value="971">971 - Guadeloupe</option> -<option value="972">972 - Martinique</option> -<option value="973">973 - Guyane</option> -<option value="974">974 - La Reunion</option> -<option value="975">975 - Saint-Pierre-et-Miquelon</option> -<option value="976">976 - Mayotte</option> -<option value="984">984 - Terres Australes et Antarctiques</option> -<option value="986">986 - Wallis et Futuna</option> -<option value="987">987 - Polynesie Francaise</option> -<option value="988">988 - Nouvelle-Caledonie</option> -</select> - -# JPPostalCodeField ############################################################### - -A form field that validates its input is a Japanese postcode. - -Accepts 7 digits(with/out hyphen). ->>> from django.contrib.localflavor.jp.forms import JPPostalCodeField ->>> f = JPPostalCodeField() ->>> f.clean('251-0032') -u'2510032' ->>> f.clean('2510032') -u'2510032' ->>> f.clean('2510-032') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] ->>> f.clean('251a0032') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] ->>> f.clean('a51-0032') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] ->>> f.clean('25100321') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = JPPostalCodeField(required=False) ->>> f.clean('251-0032') -u'2510032' ->>> f.clean('2510032') -u'2510032' ->>> f.clean('2510-032') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] ->>> f.clean('') -u'' ->>> f.clean(None) -u'' - -# JPPrefectureSelect ############################################################### - -A Select widget that uses a list of Japanese prefectures as its choices. ->>> from django.contrib.localflavor.jp.forms import JPPrefectureSelect ->>> w = JPPrefectureSelect() ->>> print w.render('prefecture', 'kanagawa') -<select name="prefecture"> -<option value="hokkaido">Hokkaido</option> -<option value="aomori">Aomori</option> -<option value="iwate">Iwate</option> -<option value="miyagi">Miyagi</option> -<option value="akita">Akita</option> -<option value="yamagata">Yamagata</option> -<option value="fukushima">Fukushima</option> -<option value="ibaraki">Ibaraki</option> -<option value="tochigi">Tochigi</option> -<option value="gunma">Gunma</option> -<option value="saitama">Saitama</option> -<option value="chiba">Chiba</option> -<option value="tokyo">Tokyo</option> -<option value="kanagawa" selected="selected">Kanagawa</option> -<option value="yamanashi">Yamanashi</option> -<option value="nagano">Nagano</option> -<option value="niigata">Niigata</option> -<option value="toyama">Toyama</option> -<option value="ishikawa">Ishikawa</option> -<option value="fukui">Fukui</option> -<option value="gifu">Gifu</option> -<option value="shizuoka">Shizuoka</option> -<option value="aichi">Aichi</option> -<option value="mie">Mie</option> -<option value="shiga">Shiga</option> -<option value="kyoto">Kyoto</option> -<option value="osaka">Osaka</option> -<option value="hyogo">Hyogo</option> -<option value="nara">Nara</option> -<option value="wakayama">Wakayama</option> -<option value="tottori">Tottori</option> -<option value="shimane">Shimane</option> -<option value="okayama">Okayama</option> -<option value="hiroshima">Hiroshima</option> -<option value="yamaguchi">Yamaguchi</option> -<option value="tokushima">Tokushima</option> -<option value="kagawa">Kagawa</option> -<option value="ehime">Ehime</option> -<option value="kochi">Kochi</option> -<option value="fukuoka">Fukuoka</option> -<option value="saga">Saga</option> -<option value="nagasaki">Nagasaki</option> -<option value="kumamoto">Kumamoto</option> -<option value="oita">Oita</option> -<option value="miyazaki">Miyazaki</option> -<option value="kagoshima">Kagoshima</option> -<option value="okinawa">Okinawa</option> -</select> - -# ITZipCodeField ############################################################# - ->>> from django.contrib.localflavor.it.forms import ITZipCodeField ->>> f = ITZipCodeField() ->>> f.clean('00100') -u'00100' ->>> f.clean(' 00100') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid zip code.'] - -# ITRegionSelect ############################################################# - ->>> from django.contrib.localflavor.it.forms import ITRegionSelect ->>> w = ITRegionSelect() ->>> w.render('regions', 'PMN') -u'<select name="regions">\n<option value="ABR">Abruzzo</option>\n<option value="BAS">Basilicata</option>\n<option value="CAL">Calabria</option>\n<option value="CAM">Campania</option>\n<option value="EMR">Emilia-Romagna</option>\n<option value="FVG">Friuli-Venezia Giulia</option>\n<option value="LAZ">Lazio</option>\n<option value="LIG">Liguria</option>\n<option value="LOM">Lombardia</option>\n<option value="MAR">Marche</option>\n<option value="MOL">Molise</option>\n<option value="PMN" selected="selected">Piemonte</option>\n<option value="PUG">Puglia</option>\n<option value="SAR">Sardegna</option>\n<option value="SIC">Sicilia</option>\n<option value="TOS">Toscana</option>\n<option value="TAA">Trentino-Alto Adige</option>\n<option value="UMB">Umbria</option>\n<option value="VAO">Valle d\u2019Aosta</option>\n<option value="VEN">Veneto</option>\n</select>' - -# ITSocialSecurityNumberField ################################################# - ->>> from django.contrib.localflavor.it.forms import ITSocialSecurityNumberField ->>> f = ITSocialSecurityNumberField() ->>> f.clean('LVSGDU99T71H501L') -u'LVSGDU99T71H501L' ->>> f.clean('LBRRME11A01L736W') -u'LBRRME11A01L736W' ->>> f.clean('lbrrme11a01l736w') -u'LBRRME11A01L736W' ->>> f.clean('LBR RME 11A01 L736W') -u'LBRRME11A01L736W' ->>> f.clean('LBRRME11A01L736A') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Social Security number.'] ->>> f.clean('%BRRME11A01L736W') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Social Security number.'] - -# ITVatNumberField ########################################################### - ->>> from django.contrib.localflavor.it.forms import ITVatNumberField ->>> f = ITVatNumberField() ->>> f.clean('07973780013') -u'07973780013' ->>> f.clean('7973780013') -u'07973780013' ->>> f.clean(7973780013) -u'07973780013' ->>> f.clean('07973780014') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid VAT number.'] ->>> f.clean('A7973780013') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid VAT number.'] - -# FIZipCodeField ############################################################# - -FIZipCodeField validates that the data is a valid FI zipcode. ->>> from django.contrib.localflavor.fi.forms import FIZipCodeField ->>> f = FIZipCodeField() ->>> f.clean('20540') -u'20540' ->>> f.clean('20101') -u'20101' ->>> f.clean('20s40') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean('205401') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = FIZipCodeField(required=False) ->>> f.clean('20540') -u'20540' ->>> f.clean('20101') -u'20101' ->>> f.clean('20s40') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean('205401') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# FIMunicipalitySelect ############################################################### - -A Select widget that uses a list of Finnish municipalities as its choices. ->>> from django.contrib.localflavor.fi.forms import FIMunicipalitySelect ->>> w = FIMunicipalitySelect() ->>> unicode(w.render('municipalities', 'turku')) -u'<select name="municipalities">\n<option value="akaa">Akaa</option>\n<option value="alaharma">Alah\xe4rm\xe4</option>\n<option value="alajarvi">Alaj\xe4rvi</option>\n<option value="alastaro">Alastaro</option>\n<option value="alavieska">Alavieska</option>\n<option value="alavus">Alavus</option>\n<option value="anjalankoski">Anjalankoski</option>\n<option value="artjarvi">Artj\xe4rvi</option>\n<option value="asikkala">Asikkala</option>\n<option value="askainen">Askainen</option>\n<option value="askola">Askola</option>\n<option value="aura">Aura</option>\n<option value="brando">Br\xe4nd\xf6</option>\n<option value="dragsfjard">Dragsfj\xe4rd</option>\n<option value="eckero">Ecker\xf6</option>\n<option value="elimaki">Elim\xe4ki</option>\n<option value="eno">Eno</option>\n<option value="enonkoski">Enonkoski</option>\n<option value="enontekio">Enonteki\xf6</option>\n<option value="espoo">Espoo</option>\n<option value="eura">Eura</option>\n<option value="eurajoki">Eurajoki</option>\n<option value="evijarvi">Evij\xe4rvi</option>\n<option value="finstrom">Finstr\xf6m</option>\n<option value="forssa">Forssa</option>\n<option value="foglo">F\xf6gl\xf6</option>\n<option value="geta">Geta</option>\n<option value="haapajarvi">Haapaj\xe4rvi</option>\n<option value="haapavesi">Haapavesi</option>\n<option value="hailuoto">Hailuoto</option>\n<option value="halikko">Halikko</option>\n<option value="halsua">Halsua</option>\n<option value="hamina">Hamina</option>\n<option value="hammarland">Hammarland</option>\n<option value="hankasalmi">Hankasalmi</option>\n<option value="hanko">Hanko</option>\n<option value="harjavalta">Harjavalta</option>\n<option value="hartola">Hartola</option>\n<option value="hattula">Hattula</option>\n<option value="hauho">Hauho</option>\n<option value="haukipudas">Haukipudas</option>\n<option value="hausjarvi">Hausj\xe4rvi</option>\n<option value="heinola">Heinola</option>\n<option value="heinavesi">Hein\xe4vesi</option>\n<option value="helsinki">Helsinki</option>\n<option value="himanka">Himanka</option>\n<option value="hirvensalmi">Hirvensalmi</option>\n<option value="hollola">Hollola</option>\n<option value="honkajoki">Honkajoki</option>\n<option value="houtskari">Houtskari</option>\n<option value="huittinen">Huittinen</option>\n<option value="humppila">Humppila</option>\n<option value="hyrynsalmi">Hyrynsalmi</option>\n<option value="hyvinkaa">Hyvink\xe4\xe4</option>\n<option value="hameenkoski">H\xe4meenkoski</option>\n<option value="hameenkyro">H\xe4meenkyr\xf6</option>\n<option value="hameenlinna">H\xe4meenlinna</option>\n<option value="ii">Ii</option>\n<option value="iisalmi">Iisalmi</option>\n<option value="iitti">Iitti</option>\n<option value="ikaalinen">Ikaalinen</option>\n<option value="ilmajoki">Ilmajoki</option>\n<option value="ilomantsi">Ilomantsi</option>\n<option value="imatra">Imatra</option>\n<option value="inari">Inari</option>\n<option value="inio">Ini\xf6</option>\n<option value="inkoo">Inkoo</option>\n<option value="isojoki">Isojoki</option>\n<option value="isokyro">Isokyr\xf6</option>\n<option value="jaala">Jaala</option>\n<option value="jalasjarvi">Jalasj\xe4rvi</option>\n<option value="janakkala">Janakkala</option>\n<option value="joensuu">Joensuu</option>\n<option value="jokioinen">Jokioinen</option>\n<option value="jomala">Jomala</option>\n<option value="joroinen">Joroinen</option>\n<option value="joutsa">Joutsa</option>\n<option value="joutseno">Joutseno</option>\n<option value="juankoski">Juankoski</option>\n<option value="jurva">Jurva</option>\n<option value="juuka">Juuka</option>\n<option value="juupajoki">Juupajoki</option>\n<option value="juva">Juva</option>\n<option value="jyvaskyla">Jyv\xe4skyl\xe4</option>\n<option value="jyvaskylan_mlk">Jyv\xe4skyl\xe4n maalaiskunta</option>\n<option value="jamijarvi">J\xe4mij\xe4rvi</option>\n<option value="jamsa">J\xe4ms\xe4</option>\n<option value="jamsankoski">J\xe4ms\xe4nkoski</option>\n<option value="jarvenpaa">J\xe4rvenp\xe4\xe4</option>\n<option value="kaarina">Kaarina</option>\n<option value="kaavi">Kaavi</option>\n<option value="kajaani">Kajaani</option>\n<option value="kalajoki">Kalajoki</option>\n<option value="kalvola">Kalvola</option>\n<option value="kangasala">Kangasala</option>\n<option value="kangasniemi">Kangasniemi</option>\n<option value="kankaanpaa">Kankaanp\xe4\xe4</option>\n<option value="kannonkoski">Kannonkoski</option>\n<option value="kannus">Kannus</option>\n<option value="karijoki">Karijoki</option>\n<option value="karjaa">Karjaa</option>\n<option value="karjalohja">Karjalohja</option>\n<option value="karkkila">Karkkila</option>\n<option value="karstula">Karstula</option>\n<option value="karttula">Karttula</option>\n<option value="karvia">Karvia</option>\n<option value="kaskinen">Kaskinen</option>\n<option value="kauhajoki">Kauhajoki</option>\n<option value="kauhava">Kauhava</option>\n<option value="kauniainen">Kauniainen</option>\n<option value="kaustinen">Kaustinen</option>\n<option value="keitele">Keitele</option>\n<option value="kemi">Kemi</option>\n<option value="kemijarvi">Kemij\xe4rvi</option>\n<option value="keminmaa">Keminmaa</option>\n<option value="kemio">Kemi\xf6</option>\n<option value="kempele">Kempele</option>\n<option value="kerava">Kerava</option>\n<option value="kerimaki">Kerim\xe4ki</option>\n<option value="kestila">Kestil\xe4</option>\n<option value="kesalahti">Kes\xe4lahti</option>\n<option value="keuruu">Keuruu</option>\n<option value="kihnio">Kihni\xf6</option>\n<option value="kiikala">Kiikala</option>\n<option value="kiikoinen">Kiikoinen</option>\n<option value="kiiminki">Kiiminki</option>\n<option value="kinnula">Kinnula</option>\n<option value="kirkkonummi">Kirkkonummi</option>\n<option value="kisko">Kisko</option>\n<option value="kitee">Kitee</option>\n<option value="kittila">Kittil\xe4</option>\n<option value="kiukainen">Kiukainen</option>\n<option value="kiuruvesi">Kiuruvesi</option>\n<option value="kivijarvi">Kivij\xe4rvi</option>\n<option value="kokemaki">Kokem\xe4ki</option>\n<option value="kokkola">Kokkola</option>\n<option value="kolari">Kolari</option>\n<option value="konnevesi">Konnevesi</option>\n<option value="kontiolahti">Kontiolahti</option>\n<option value="korpilahti">Korpilahti</option>\n<option value="korppoo">Korppoo</option>\n<option value="korsnas">Korsn\xe4s</option>\n<option value="kortesjarvi">Kortesj\xe4rvi</option>\n<option value="koskitl">KoskiTl</option>\n<option value="kotka">Kotka</option>\n<option value="kouvola">Kouvola</option>\n<option value="kristiinankaupunki">Kristiinankaupunki</option>\n<option value="kruunupyy">Kruunupyy</option>\n<option value="kuhmalahti">Kuhmalahti</option>\n<option value="kuhmo">Kuhmo</option>\n<option value="kuhmoinen">Kuhmoinen</option>\n<option value="kumlinge">Kumlinge</option>\n<option value="kuopio">Kuopio</option>\n<option value="kuortane">Kuortane</option>\n<option value="kurikka">Kurikka</option>\n<option value="kuru">Kuru</option>\n<option value="kustavi">Kustavi</option>\n<option value="kuusamo">Kuusamo</option>\n<option value="kuusankoski">Kuusankoski</option>\n<option value="kuusjoki">Kuusjoki</option>\n<option value="kylmakoski">Kylm\xe4koski</option>\n<option value="kyyjarvi">Kyyj\xe4rvi</option>\n<option value="kalvia">K\xe4lvi\xe4</option>\n<option value="karkola">K\xe4rk\xf6l\xe4</option>\n<option value="karsamaki">K\xe4rs\xe4m\xe4ki</option>\n<option value="kokar">K\xf6kar</option>\n<option value="koylio">K\xf6yli\xf6</option>\n<option value="lahti">Lahti</option>\n<option value="laihia">Laihia</option>\n<option value="laitila">Laitila</option>\n<option value="lammi">Lammi</option>\n<option value="lapinjarvi">Lapinj\xe4rvi</option>\n<option value="lapinlahti">Lapinlahti</option>\n<option value="lappajarvi">Lappaj\xe4rvi</option>\n<option value="lappeenranta">Lappeenranta</option>\n<option value="lappi">Lappi</option>\n<option value="lapua">Lapua</option>\n<option value="laukaa">Laukaa</option>\n<option value="lavia">Lavia</option>\n<option value="lehtimaki">Lehtim\xe4ki</option>\n<option value="leivonmaki">Leivonm\xe4ki</option>\n<option value="lemi">Lemi</option>\n<option value="lemland">Lemland</option>\n<option value="lempaala">Lemp\xe4\xe4l\xe4</option>\n<option value="lemu">Lemu</option>\n<option value="leppavirta">Lepp\xe4virta</option>\n<option value="lestijarvi">Lestij\xe4rvi</option>\n<option value="lieksa">Lieksa</option>\n<option value="lieto">Lieto</option>\n<option value="liljendal">Liljendal</option>\n<option value="liminka">Liminka</option>\n<option value="liperi">Liperi</option>\n<option value="lohja">Lohja</option>\n<option value="lohtaja">Lohtaja</option>\n<option value="loimaa">Loimaa</option>\n<option value="loppi">Loppi</option>\n<option value="loviisa">Loviisa</option>\n<option value="luhanka">Luhanka</option>\n<option value="lumijoki">Lumijoki</option>\n<option value="lumparland">Lumparland</option>\n<option value="luoto">Luoto</option>\n<option value="luumaki">Luum\xe4ki</option>\n<option value="luvia">Luvia</option>\n<option value="maalahti">Maalahti</option>\n<option value="maaninka">Maaninka</option>\n<option value="maarianhamina">Maarianhamina</option>\n<option value="marttila">Marttila</option>\n<option value="masku">Masku</option>\n<option value="mellila">Mellil\xe4</option>\n<option value="merijarvi">Merij\xe4rvi</option>\n<option value="merikarvia">Merikarvia</option>\n<option value="merimasku">Merimasku</option>\n<option value="miehikkala">Miehikk\xe4l\xe4</option>\n<option value="mikkeli">Mikkeli</option>\n<option value="mouhijarvi">Mouhij\xe4rvi</option>\n<option value="muhos">Muhos</option>\n<option value="multia">Multia</option>\n<option value="muonio">Muonio</option>\n<option value="mustasaari">Mustasaari</option>\n<option value="muurame">Muurame</option>\n<option value="muurla">Muurla</option>\n<option value="mynamaki">Myn\xe4m\xe4ki</option>\n<option value="myrskyla">Myrskyl\xe4</option>\n<option value="mantsala">M\xe4nts\xe4l\xe4</option>\n<option value="mantta">M\xe4ntt\xe4</option>\n<option value="mantyharju">M\xe4ntyharju</option>\n<option value="naantali">Naantali</option>\n<option value="nakkila">Nakkila</option>\n<option value="nastola">Nastola</option>\n<option value="nauvo">Nauvo</option>\n<option value="nilsia">Nilsi\xe4</option>\n<option value="nivala">Nivala</option>\n<option value="nokia">Nokia</option>\n<option value="noormarkku">Noormarkku</option>\n<option value="nousiainen">Nousiainen</option>\n<option value="nummi-pusula">Nummi-Pusula</option>\n<option value="nurmes">Nurmes</option>\n<option value="nurmijarvi">Nurmij\xe4rvi</option>\n<option value="nurmo">Nurmo</option>\n<option value="narpio">N\xe4rpi\xf6</option>\n<option value="oravainen">Oravainen</option>\n<option value="orimattila">Orimattila</option>\n<option value="oripaa">Orip\xe4\xe4</option>\n<option value="orivesi">Orivesi</option>\n<option value="oulainen">Oulainen</option>\n<option value="oulu">Oulu</option>\n<option value="oulunsalo">Oulunsalo</option>\n<option value="outokumpu">Outokumpu</option>\n<option value="padasjoki">Padasjoki</option>\n<option value="paimio">Paimio</option>\n<option value="paltamo">Paltamo</option>\n<option value="parainen">Parainen</option>\n<option value="parikkala">Parikkala</option>\n<option value="parkano">Parkano</option>\n<option value="pedersore">Peders\xf6re</option>\n<option value="pelkosenniemi">Pelkosenniemi</option>\n<option value="pello">Pello</option>\n<option value="perho">Perho</option>\n<option value="pernaja">Pernaja</option>\n<option value="pernio">Perni\xf6</option>\n<option value="pertteli">Pertteli</option>\n<option value="pertunmaa">Pertunmaa</option>\n<option value="petajavesi">Pet\xe4j\xe4vesi</option>\n<option value="pieksamaki">Pieks\xe4m\xe4ki</option>\n<option value="pielavesi">Pielavesi</option>\n<option value="pietarsaari">Pietarsaari</option>\n<option value="pihtipudas">Pihtipudas</option>\n<option value="piikkio">Piikki\xf6</option>\n<option value="piippola">Piippola</option>\n<option value="pirkkala">Pirkkala</option>\n<option value="pohja">Pohja</option>\n<option value="polvijarvi">Polvij\xe4rvi</option>\n<option value="pomarkku">Pomarkku</option>\n<option value="pori">Pori</option>\n<option value="pornainen">Pornainen</option>\n<option value="porvoo">Porvoo</option>\n<option value="posio">Posio</option>\n<option value="pudasjarvi">Pudasj\xe4rvi</option>\n<option value="pukkila">Pukkila</option>\n<option value="pulkkila">Pulkkila</option>\n<option value="punkaharju">Punkaharju</option>\n<option value="punkalaidun">Punkalaidun</option>\n<option value="puolanka">Puolanka</option>\n<option value="puumala">Puumala</option>\n<option value="pyhtaa">Pyht\xe4\xe4</option>\n<option value="pyhajoki">Pyh\xe4joki</option>\n<option value="pyhajarvi">Pyh\xe4j\xe4rvi</option>\n<option value="pyhanta">Pyh\xe4nt\xe4</option>\n<option value="pyharanta">Pyh\xe4ranta</option>\n<option value="pyhaselka">Pyh\xe4selk\xe4</option>\n<option value="pylkonmaki">Pylk\xf6nm\xe4ki</option>\n<option value="palkane">P\xe4lk\xe4ne</option>\n<option value="poytya">P\xf6yty\xe4</option>\n<option value="raahe">Raahe</option>\n<option value="raisio">Raisio</option>\n<option value="rantasalmi">Rantasalmi</option>\n<option value="rantsila">Rantsila</option>\n<option value="ranua">Ranua</option>\n<option value="rauma">Rauma</option>\n<option value="rautalampi">Rautalampi</option>\n<option value="rautavaara">Rautavaara</option>\n<option value="rautjarvi">Rautj\xe4rvi</option>\n<option value="reisjarvi">Reisj\xe4rvi</option>\n<option value="renko">Renko</option>\n<option value="riihimaki">Riihim\xe4ki</option>\n<option value="ristiina">Ristiina</option>\n<option value="ristijarvi">Ristij\xe4rvi</option>\n<option value="rovaniemi">Rovaniemi</option>\n<option value="ruokolahti">Ruokolahti</option>\n<option value="ruotsinpyhtaa">Ruotsinpyht\xe4\xe4</option>\n<option value="ruovesi">Ruovesi</option>\n<option value="rusko">Rusko</option>\n<option value="rymattyla">Rym\xe4ttyl\xe4</option>\n<option value="raakkyla">R\xe4\xe4kkyl\xe4</option>\n<option value="saarijarvi">Saarij\xe4rvi</option>\n<option value="salla">Salla</option>\n<option value="salo">Salo</option>\n<option value="saltvik">Saltvik</option>\n<option value="sammatti">Sammatti</option>\n<option value="sauvo">Sauvo</option>\n<option value="savitaipale">Savitaipale</option>\n<option value="savonlinna">Savonlinna</option>\n<option value="savonranta">Savonranta</option>\n<option value="savukoski">Savukoski</option>\n<option value="seinajoki">Sein\xe4joki</option>\n<option value="sievi">Sievi</option>\n<option value="siikainen">Siikainen</option>\n<option value="siikajoki">Siikajoki</option>\n<option value="siilinjarvi">Siilinj\xe4rvi</option>\n<option value="simo">Simo</option>\n<option value="sipoo">Sipoo</option>\n<option value="siuntio">Siuntio</option>\n<option value="sodankyla">Sodankyl\xe4</option>\n<option value="soini">Soini</option>\n<option value="somero">Somero</option>\n<option value="sonkajarvi">Sonkaj\xe4rvi</option>\n<option value="sotkamo">Sotkamo</option>\n<option value="sottunga">Sottunga</option>\n<option value="sulkava">Sulkava</option>\n<option value="sund">Sund</option>\n<option value="suomenniemi">Suomenniemi</option>\n<option value="suomusjarvi">Suomusj\xe4rvi</option>\n<option value="suomussalmi">Suomussalmi</option>\n<option value="suonenjoki">Suonenjoki</option>\n<option value="sysma">Sysm\xe4</option>\n<option value="sakyla">S\xe4kyl\xe4</option>\n<option value="sarkisalo">S\xe4rkisalo</option>\n<option value="taipalsaari">Taipalsaari</option>\n<option value="taivalkoski">Taivalkoski</option>\n<option value="taivassalo">Taivassalo</option>\n<option value="tammela">Tammela</option>\n<option value="tammisaari">Tammisaari</option>\n<option value="tampere">Tampere</option>\n<option value="tarvasjoki">Tarvasjoki</option>\n<option value="tervo">Tervo</option>\n<option value="tervola">Tervola</option>\n<option value="teuva">Teuva</option>\n<option value="tohmajarvi">Tohmaj\xe4rvi</option>\n<option value="toholampi">Toholampi</option>\n<option value="toivakka">Toivakka</option>\n<option value="tornio">Tornio</option>\n<option value="turku" selected="selected">Turku</option>\n<option value="tuulos">Tuulos</option>\n<option value="tuusniemi">Tuusniemi</option>\n<option value="tuusula">Tuusula</option>\n<option value="tyrnava">Tyrn\xe4v\xe4</option>\n<option value="toysa">T\xf6ys\xe4</option>\n<option value="ullava">Ullava</option>\n<option value="ulvila">Ulvila</option>\n<option value="urjala">Urjala</option>\n<option value="utajarvi">Utaj\xe4rvi</option>\n<option value="utsjoki">Utsjoki</option>\n<option value="uurainen">Uurainen</option>\n<option value="uusikaarlepyy">Uusikaarlepyy</option>\n<option value="uusikaupunki">Uusikaupunki</option>\n<option value="vaala">Vaala</option>\n<option value="vaasa">Vaasa</option>\n<option value="vahto">Vahto</option>\n<option value="valkeakoski">Valkeakoski</option>\n<option value="valkeala">Valkeala</option>\n<option value="valtimo">Valtimo</option>\n<option value="vammala">Vammala</option>\n<option value="vampula">Vampula</option>\n<option value="vantaa">Vantaa</option>\n<option value="varkaus">Varkaus</option>\n<option value="varpaisjarvi">Varpaisj\xe4rvi</option>\n<option value="vehmaa">Vehmaa</option>\n<option value="velkua">Velkua</option>\n<option value="vesanto">Vesanto</option>\n<option value="vesilahti">Vesilahti</option>\n<option value="veteli">Veteli</option>\n<option value="vierema">Vierem\xe4</option>\n<option value="vihanti">Vihanti</option>\n<option value="vihti">Vihti</option>\n<option value="viitasaari">Viitasaari</option>\n<option value="vilppula">Vilppula</option>\n<option value="vimpeli">Vimpeli</option>\n<option value="virolahti">Virolahti</option>\n<option value="virrat">Virrat</option>\n<option value="vardo">V\xe5rd\xf6</option>\n<option value="vahakyro">V\xe4h\xe4kyr\xf6</option>\n<option value="vastanfjard">V\xe4stanfj\xe4rd</option>\n<option value="voyri-maksamaa">V\xf6yri-Maksamaa</option>\n<option value="yliharma">Ylih\xe4rm\xe4</option>\n<option value="yli-ii">Yli-Ii</option>\n<option value="ylikiiminki">Ylikiiminki</option>\n<option value="ylistaro">Ylistaro</option>\n<option value="ylitornio">Ylitornio</option>\n<option value="ylivieska">Ylivieska</option>\n<option value="ylamaa">Yl\xe4maa</option>\n<option value="ylane">Yl\xe4ne</option>\n<option value="ylojarvi">Yl\xf6j\xe4rvi</option>\n<option value="ypaja">Yp\xe4j\xe4</option>\n<option value="aetsa">\xc4ets\xe4</option>\n<option value="ahtari">\xc4ht\xe4ri</option>\n<option value="aanekoski">\xc4\xe4nekoski</option>\n</select>' - -# FISocialSecurityNumber -############################################################## - ->>> from django.contrib.localflavor.fi.forms import FISocialSecurityNumber ->>> f = FISocialSecurityNumber() ->>> f.clean('010101-0101') -u'010101-0101' ->>> f.clean('010101+0101') -u'010101+0101' ->>> f.clean('010101A0101') -u'010101A0101' ->>> f.clean('101010-0102') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Finnish social security number.'] ->>> f.clean('10a010-0101') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Finnish social security number.'] ->>> f.clean('101010-0\xe401') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Finnish social security number.'] ->>> f.clean('101010b0101') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Finnish social security number.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f = FISocialSecurityNumber(required=False) ->>> f.clean('010101-0101') -u'010101-0101' ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# BRZipCodeField ############################################################ ->>> from django.contrib.localflavor.br.forms import BRZipCodeField ->>> f = BRZipCodeField() ->>> f.clean('12345-123') -u'12345-123' ->>> f.clean('12345_123') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('1234-123') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('abcde-abc') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('12345-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('-123') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = BRZipCodeField(required=False) ->>> f.clean(None) -u'' ->>> f.clean('') -u'' ->>> f.clean('-123') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('12345-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('abcde-abc') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('1234-123') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('12345_123') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] ->>> f.clean('12345-123') -u'12345-123' - -# BRCNPJField ############################################################ - ->>> from django.contrib.localflavor.br.forms import BRCNPJField ->>> f = BRCNPJField(required=True) ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('12-345-678/9012-10') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CNPJ number.'] ->>> f.clean('12.345.678/9012-10') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CNPJ number.'] ->>> f.clean('12345678/9012-10') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CNPJ number.'] ->>> f.clean('64.132.916/0001-88') -'64.132.916/0001-88' ->>> f.clean('64-132-916/0001-88') -'64-132-916/0001-88' ->>> f.clean('64132916/0001-88') -'64132916/0001-88' ->>> f.clean('64.132.916/0001-XX') -Traceback (most recent call last): -... -ValidationError: [u'This field requires only numbers.'] ->>> f = BRCNPJField(required=False) ->>> f.clean('') -u'' - -# BRCPFField ################################################################# - ->>> from django.contrib.localflavor.br.forms import BRCPFField ->>> f = BRCPFField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('489.294.654-54') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CPF number.'] ->>> f.clean('295.669.575-98') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CPF number.'] ->>> f.clean('539.315.127-22') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CPF number.'] ->>> f.clean('663.256.017-26') -u'663.256.017-26' ->>> f.clean('66325601726') -u'66325601726' ->>> f.clean('375.788.573-20') -u'375.788.573-20' ->>> f.clean('84828509895') -u'84828509895' ->>> f.clean('375.788.573-XX') -Traceback (most recent call last): -... -ValidationError: [u'This field requires only numbers.'] ->>> f.clean('375.788.573-000') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 14 characters (it has 15).'] ->>> f.clean('123.456.78') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 11 characters (it has 10).'] ->>> f.clean('123456789555') -Traceback (most recent call last): -... -ValidationError: [u'This field requires at most 11 digits or 14 characters.'] ->>> f = BRCPFField(required=False) ->>> f.clean('') -u'' ->>> f.clean(None) -u'' - -# BRPhoneNumberField ######################################################### - ->>> from django.contrib.localflavor.br.forms import BRPhoneNumberField ->>> f = BRPhoneNumberField() ->>> f.clean('41-3562-3464') -u'41-3562-3464' ->>> f.clean('4135623464') -u'41-3562-3464' ->>> f.clean('41 3562-3464') -u'41-3562-3464' ->>> f.clean('41 3562 3464') -u'41-3562-3464' ->>> f.clean('(41) 3562 3464') -u'41-3562-3464' ->>> f.clean('41.3562.3464') -u'41-3562-3464' ->>> f.clean('41.3562-3464') -u'41-3562-3464' ->>> f.clean(' (41) 3562.3464') -u'41-3562-3464' ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = BRPhoneNumberField(required=False) ->>> f.clean('') -u'' ->>> f.clean(None) -u'' ->>> f.clean(' (41) 3562.3464') -u'41-3562-3464' ->>> f.clean('41.3562-3464') -u'41-3562-3464' ->>> f.clean('(41) 3562 3464') -u'41-3562-3464' ->>> f.clean('4135623464') -u'41-3562-3464' ->>> f.clean('41 3562-3464') -u'41-3562-3464' - -# BRStateSelect ############################################################## - ->>> from django.contrib.localflavor.br.forms import BRStateSelect ->>> w = BRStateSelect() ->>> w.render('states', 'PR') -u'<select name="states">\n<option value="AC">Acre</option>\n<option value="AL">Alagoas</option>\n<option value="AP">Amap\xe1</option>\n<option value="AM">Amazonas</option>\n<option value="BA">Bahia</option>\n<option value="CE">Cear\xe1</option>\n<option value="DF">Distrito Federal</option>\n<option value="ES">Esp\xedrito Santo</option>\n<option value="GO">Goi\xe1s</option>\n<option value="MA">Maranh\xe3o</option>\n<option value="MT">Mato Grosso</option>\n<option value="MS">Mato Grosso do Sul</option>\n<option value="MG">Minas Gerais</option>\n<option value="PA">Par\xe1</option>\n<option value="PB">Para\xedba</option>\n<option value="PR" selected="selected">Paran\xe1</option>\n<option value="PE">Pernambuco</option>\n<option value="PI">Piau\xed</option>\n<option value="RJ">Rio de Janeiro</option>\n<option value="RN">Rio Grande do Norte</option>\n<option value="RS">Rio Grande do Sul</option>\n<option value="RO">Rond\xf4nia</option>\n<option value="RR">Roraima</option>\n<option value="SC">Santa Catarina</option>\n<option value="SP">S\xe3o Paulo</option>\n<option value="SE">Sergipe</option>\n<option value="TO">Tocantins</option>\n</select>' - -# BRStateChoiceField ######################################################### ->>> from django.contrib.localflavor.br.forms import BRStateChoiceField ->>> f = BRStateChoiceField() ->>> ', '.join([f.clean(s) for s, _ in f.widget.choices]) -u'AC, AL, AP, AM, BA, CE, DF, ES, GO, MA, MT, MS, MG, PA, PB, PR, PE, PI, RJ, RN, RS, RO, RR, SC, SP, SE, TO' ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('pr') -Traceback (most recent call last): -... -ValidationError: [u'Select a valid brazilian state. That state is not one of the available states.'] - -# DEZipCodeField ############################################################## - ->>> from django.contrib.localflavor.de.forms import DEZipCodeField ->>> f = DEZipCodeField() ->>> f.clean('99423') -u'99423' ->>> f.clean(' 99423') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX.'] - -# DEStateSelect ############################################################# - ->>> from django.contrib.localflavor.de.forms import DEStateSelect ->>> w = DEStateSelect() ->>> w.render('states', 'TH') -u'<select name="states">\n<option value="BW">Baden-Wuerttemberg</option>\n<option value="BY">Bavaria</option>\n<option value="BE">Berlin</option>\n<option value="BB">Brandenburg</option>\n<option value="HB">Bremen</option>\n<option value="HH">Hamburg</option>\n<option value="HE">Hessen</option>\n<option value="MV">Mecklenburg-Western Pomerania</option>\n<option value="NI">Lower Saxony</option>\n<option value="NW">North Rhine-Westphalia</option>\n<option value="RP">Rhineland-Palatinate</option>\n<option value="SL">Saarland</option>\n<option value="SN">Saxony</option>\n<option value="ST">Saxony-Anhalt</option>\n<option value="SH">Schleswig-Holstein</option>\n<option value="TH" selected="selected">Thuringia</option>\n</select>' - -# DEIdentityCardNumberField ################################################# - ->>> from django.contrib.localflavor.de.forms import DEIdentityCardNumberField ->>> f = DEIdentityCardNumberField() ->>> f.clean('7549313035D-6004103-0903042-0') -u'7549313035D-6004103-0903042-0' ->>> f.clean('9786324830D 6104243 0910271 2') -u'9786324830D-6104243-0910271-2' ->>> f.clean('0434657485D-6407276-0508137-9') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.'] - -# CHZipCodeField ############################################################ - ->>> from django.contrib.localflavor.ch.forms import CHZipCodeField ->>> f = CHZipCodeField() ->>> f.clean('800x') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXX.'] ->>> f.clean('80 00') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXX.'] ->>> f.clean('8000') -u'8000' - -# CHPhoneNumberField ######################################################## - ->>> from django.contrib.localflavor.ch.forms import CHPhoneNumberField ->>> f = CHPhoneNumberField() ->>> f.clean('01234567890') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.'] ->>> f.clean('1234567890') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.'] ->>> f.clean('0123456789') -u'012 345 67 89' - -# CHIdentityCardNumberField ################################################# - ->>> from django.contrib.localflavor.ch.forms import CHIdentityCardNumberField ->>> f = CHIdentityCardNumberField() ->>> f.clean('C1234567<0') -u'C1234567<0' ->>> f.clean('C1234567<1') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] ->>> f.clean('2123456700') -u'2123456700' ->>> f.clean('2123456701') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] - -# CHStateSelect ############################################################# - ->>> from django.contrib.localflavor.ch.forms import CHStateSelect ->>> w = CHStateSelect() ->>> w.render('state', 'AG') -u'<select name="state">\n<option value="AG" selected="selected">Aargau</option>\n<option value="AI">Appenzell Innerrhoden</option>\n<option value="AR">Appenzell Ausserrhoden</option>\n<option value="BS">Basel-Stadt</option>\n<option value="BL">Basel-Land</option>\n<option value="BE">Berne</option>\n<option value="FR">Fribourg</option>\n<option value="GE">Geneva</option>\n<option value="GL">Glarus</option>\n<option value="GR">Graubuenden</option>\n<option value="JU">Jura</option>\n<option value="LU">Lucerne</option>\n<option value="NE">Neuchatel</option>\n<option value="NW">Nidwalden</option>\n<option value="OW">Obwalden</option>\n<option value="SH">Schaffhausen</option>\n<option value="SZ">Schwyz</option>\n<option value="SO">Solothurn</option>\n<option value="SG">St. Gallen</option>\n<option value="TG">Thurgau</option>\n<option value="TI">Ticino</option>\n<option value="UR">Uri</option>\n<option value="VS">Valais</option>\n<option value="VD">Vaud</option>\n<option value="ZG">Zug</option>\n<option value="ZH">Zurich</option>\n</select>' - -## AUPostCodeField ########################################################## - -A field that accepts a four digit Australian post code. - ->>> from django.contrib.localflavor.au.forms import AUPostCodeField ->>> f = AUPostCodeField() ->>> f.clean('1234') -u'1234' ->>> f.clean('2000') -u'2000' ->>> f.clean('abcd') -Traceback (most recent call last): -... -ValidationError: [u'Enter a 4 digit post code.'] ->>> f.clean('20001') -Traceback (most recent call last): -... -ValidationError: [u'Enter a 4 digit post code.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = AUPostCodeField(required=False) ->>> f.clean('1234') -u'1234' ->>> f.clean('2000') -u'2000' ->>> f.clean('abcd') -Traceback (most recent call last): -... -ValidationError: [u'Enter a 4 digit post code.'] ->>> f.clean('20001') -Traceback (most recent call last): -... -ValidationError: [u'Enter a 4 digit post code.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -## AUPhoneNumberField ######################################################## - -A field that accepts a 10 digit Australian phone number. -llows spaces and parentheses around area code. - ->>> from django.contrib.localflavor.au.forms import AUPhoneNumberField ->>> f = AUPhoneNumberField() ->>> f.clean('1234567890') -u'1234567890' ->>> f.clean('0213456789') -u'0213456789' ->>> f.clean('02 13 45 67 89') -u'0213456789' ->>> f.clean('(02) 1345 6789') -u'0213456789' ->>> f.clean('(02) 1345-6789') -u'0213456789' ->>> f.clean('(02)1345-6789') -u'0213456789' ->>> f.clean('0408 123 456') -u'0408123456' ->>> f.clean('123') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must contain 10 digits.'] ->>> f.clean('1800DJANGO') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must contain 10 digits.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = AUPhoneNumberField(required=False) ->>> f.clean('1234567890') -u'1234567890' ->>> f.clean('0213456789') -u'0213456789' ->>> f.clean('02 13 45 67 89') -u'0213456789' ->>> f.clean('(02) 1345 6789') -u'0213456789' ->>> f.clean('(02) 1345-6789') -u'0213456789' ->>> f.clean('(02)1345-6789') -u'0213456789' ->>> f.clean('0408 123 456') -u'0408123456' ->>> f.clean('123') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must contain 10 digits.'] ->>> f.clean('1800DJANGO') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must contain 10 digits.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -## AUStateSelect ############################################################# - -AUStateSelect is a Select widget that uses a list of Australian -states/territories as its choices. - ->>> from django.contrib.localflavor.au.forms import AUStateSelect ->>> f = AUStateSelect() ->>> print f.render('state', 'NSW') -<select name="state"> -<option value="ACT">Australian Capital Territory</option> -<option value="NSW" selected="selected">New South Wales</option> -<option value="NT">Northern Territory</option> -<option value="QLD">Queensland</option> -<option value="SA">South Australia</option> -<option value="TAS">Tasmania</option> -<option value="VIC">Victoria</option> -<option value="WA">Western Australia</option> -</select> - -## ISIdNumberField ############################################################# - ->>> from django.contrib.localflavor.is_.forms import * ->>> f = ISIdNumberField() ->>> f.clean('2308803449') -u'230880-3449' ->>> f.clean('230880-3449') -u'230880-3449' ->>> f.clean('230880 3449') -u'230880-3449' ->>> f.clean('230880343') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 10 characters (it has 9).'] ->>> f.clean('230880343234') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 11 characters (it has 12).'] ->>> f.clean('abcdefghijk') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('2308803439') -Traceback (most recent call last): -... -ValidationError: [u'The Icelandic identification number is not valid.'] ->>> f.clean('2308803440') -u'230880-3440' ->>> f = ISIdNumberField(required=False) ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -## ISPhoneNumberField ############################################################# - ->>> from django.contrib.localflavor.is_.forms import * ->>> f = ISPhoneNumberField() ->>> f.clean('1234567') -u'1234567' ->>> f.clean('123 4567') -u'1234567' ->>> f.clean('123-4567') -u'1234567' ->>> f.clean('123-456') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] ->>> f.clean('123456') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 7 characters (it has 6).'] ->>> f.clean('123456555') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] ->>> f.clean('abcdefg') -Traceback (most recent call last): -ValidationError: [u'Enter a valid value.'] ->>> f.clean(' 1234567 ') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] ->>> f.clean(' 12367 ') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] - ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f = ISPhoneNumberField(required=False) ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -## ISPostalCodeSelect ############################################################# - ->>> from django.contrib.localflavor.is_.forms import * ->>> f = ISPostalCodeSelect() - ->>> f.render('foo', 'bar') -u'<select name="foo">\n<option value="101">101 Reykjav\xedk</option>\n<option value="103">103 Reykjav\xedk</option>\n<option value="104">104 Reykjav\xedk</option>\n<option value="105">105 Reykjav\xedk</option>\n<option value="107">107 Reykjav\xedk</option>\n<option value="108">108 Reykjav\xedk</option>\n<option value="109">109 Reykjav\xedk</option>\n<option value="110">110 Reykjav\xedk</option>\n<option value="111">111 Reykjav\xedk</option>\n<option value="112">112 Reykjav\xedk</option>\n<option value="113">113 Reykjav\xedk</option>\n<option value="116">116 Kjalarnes</option>\n<option value="121">121 Reykjav\xedk</option>\n<option value="123">123 Reykjav\xedk</option>\n<option value="124">124 Reykjav\xedk</option>\n<option value="125">125 Reykjav\xedk</option>\n<option value="127">127 Reykjav\xedk</option>\n<option value="128">128 Reykjav\xedk</option>\n<option value="129">129 Reykjav\xedk</option>\n<option value="130">130 Reykjav\xedk</option>\n<option value="132">132 Reykjav\xedk</option>\n<option value="150">150 Reykjav\xedk</option>\n<option value="155">155 Reykjav\xedk</option>\n<option value="170">170 Seltjarnarnes</option>\n<option value="172">172 Seltjarnarnes</option>\n<option value="190">190 Vogar</option>\n<option value="200">200 K\xf3pavogur</option>\n<option value="201">201 K\xf3pavogur</option>\n<option value="202">202 K\xf3pavogur</option>\n<option value="203">203 K\xf3pavogur</option>\n<option value="210">210 Gar\xf0ab\xe6r</option>\n<option value="212">212 Gar\xf0ab\xe6r</option>\n<option value="220">220 Hafnarfj\xf6r\xf0ur</option>\n<option value="221">221 Hafnarfj\xf6r\xf0ur</option>\n<option value="222">222 Hafnarfj\xf6r\xf0ur</option>\n<option value="225">225 \xc1lftanes</option>\n<option value="230">230 Reykjanesb\xe6r</option>\n<option value="232">232 Reykjanesb\xe6r</option>\n<option value="233">233 Reykjanesb\xe6r</option>\n<option value="235">235 Keflav\xedkurflugv\xf6llur</option>\n<option value="240">240 Grindav\xedk</option>\n<option value="245">245 Sandger\xf0i</option>\n<option value="250">250 Gar\xf0ur</option>\n<option value="260">260 Reykjanesb\xe6r</option>\n<option value="270">270 Mosfellsb\xe6r</option>\n<option value="300">300 Akranes</option>\n<option value="301">301 Akranes</option>\n<option value="302">302 Akranes</option>\n<option value="310">310 Borgarnes</option>\n<option value="311">311 Borgarnes</option>\n<option value="320">320 Reykholt \xed Borgarfir\xf0i</option>\n<option value="340">340 Stykkish\xf3lmur</option>\n<option value="345">345 Flatey \xe1 Brei\xf0afir\xf0i</option>\n<option value="350">350 Grundarfj\xf6r\xf0ur</option>\n<option value="355">355 \xd3lafsv\xedk</option>\n<option value="356">356 Sn\xe6fellsb\xe6r</option>\n<option value="360">360 Hellissandur</option>\n<option value="370">370 B\xfa\xf0ardalur</option>\n<option value="371">371 B\xfa\xf0ardalur</option>\n<option value="380">380 Reykh\xf3lahreppur</option>\n<option value="400">400 \xcdsafj\xf6r\xf0ur</option>\n<option value="401">401 \xcdsafj\xf6r\xf0ur</option>\n<option value="410">410 Hn\xedfsdalur</option>\n<option value="415">415 Bolungarv\xedk</option>\n<option value="420">420 S\xfa\xf0av\xedk</option>\n<option value="425">425 Flateyri</option>\n<option value="430">430 Su\xf0ureyri</option>\n<option value="450">450 Patreksfj\xf6r\xf0ur</option>\n<option value="451">451 Patreksfj\xf6r\xf0ur</option>\n<option value="460">460 T\xe1lknafj\xf6r\xf0ur</option>\n<option value="465">465 B\xedldudalur</option>\n<option value="470">470 \xdeingeyri</option>\n<option value="471">471 \xdeingeyri</option>\n<option value="500">500 Sta\xf0ur</option>\n<option value="510">510 H\xf3lmav\xedk</option>\n<option value="512">512 H\xf3lmav\xedk</option>\n<option value="520">520 Drangsnes</option>\n<option value="522">522 Kj\xf6rvogur</option>\n<option value="523">523 B\xe6r</option>\n<option value="524">524 Nor\xf0urfj\xf6r\xf0ur</option>\n<option value="530">530 Hvammstangi</option>\n<option value="531">531 Hvammstangi</option>\n<option value="540">540 Bl\xf6ndu\xf3s</option>\n<option value="541">541 Bl\xf6ndu\xf3s</option>\n<option value="545">545 Skagastr\xf6nd</option>\n<option value="550">550 Sau\xf0\xe1rkr\xf3kur</option>\n<option value="551">551 Sau\xf0\xe1rkr\xf3kur</option>\n<option value="560">560 Varmahl\xed\xf0</option>\n<option value="565">565 Hofs\xf3s</option>\n<option value="566">566 Hofs\xf3s</option>\n<option value="570">570 Flj\xf3t</option>\n<option value="580">580 Siglufj\xf6r\xf0ur</option>\n<option value="600">600 Akureyri</option>\n<option value="601">601 Akureyri</option>\n<option value="602">602 Akureyri</option>\n<option value="603">603 Akureyri</option>\n<option value="610">610 Greniv\xedk</option>\n<option value="611">611 Gr\xedmsey</option>\n<option value="620">620 Dalv\xedk</option>\n<option value="621">621 Dalv\xedk</option>\n<option value="625">625 \xd3lafsfj\xf6r\xf0ur</option>\n<option value="630">630 Hr\xedsey</option>\n<option value="640">640 H\xfasav\xedk</option>\n<option value="641">641 H\xfasav\xedk</option>\n<option value="645">645 Fossh\xf3ll</option>\n<option value="650">650 Laugar</option>\n<option value="660">660 M\xfdvatn</option>\n<option value="670">670 K\xf3pasker</option>\n<option value="671">671 K\xf3pasker</option>\n<option value="675">675 Raufarh\xf6fn</option>\n<option value="680">680 \xde\xf3rsh\xf6fn</option>\n<option value="681">681 \xde\xf3rsh\xf6fn</option>\n<option value="685">685 Bakkafj\xf6r\xf0ur</option>\n<option value="690">690 Vopnafj\xf6r\xf0ur</option>\n<option value="700">700 Egilssta\xf0ir</option>\n<option value="701">701 Egilssta\xf0ir</option>\n<option value="710">710 Sey\xf0isfj\xf6r\xf0ur</option>\n<option value="715">715 Mj\xf3ifj\xf6r\xf0ur</option>\n<option value="720">720 Borgarfj\xf6r\xf0ur eystri</option>\n<option value="730">730 Rey\xf0arfj\xf6r\xf0ur</option>\n<option value="735">735 Eskifj\xf6r\xf0ur</option>\n<option value="740">740 Neskaupsta\xf0ur</option>\n<option value="750">750 F\xe1skr\xfa\xf0sfj\xf6r\xf0ur</option>\n<option value="755">755 St\xf6\xf0varfj\xf6r\xf0ur</option>\n<option value="760">760 Brei\xf0dalsv\xedk</option>\n<option value="765">765 Dj\xfapivogur</option>\n<option value="780">780 H\xf6fn \xed Hornafir\xf0i</option>\n<option value="781">781 H\xf6fn \xed Hornafir\xf0i</option>\n<option value="785">785 \xd6r\xe6fi</option>\n<option value="800">800 Selfoss</option>\n<option value="801">801 Selfoss</option>\n<option value="802">802 Selfoss</option>\n<option value="810">810 Hverager\xf0i</option>\n<option value="815">815 \xdeorl\xe1ksh\xf6fn</option>\n<option value="820">820 Eyrarbakki</option>\n<option value="825">825 Stokkseyri</option>\n<option value="840">840 Laugarvatn</option>\n<option value="845">845 Fl\xfa\xf0ir</option>\n<option value="850">850 Hella</option>\n<option value="851">851 Hella</option>\n<option value="860">860 Hvolsv\xf6llur</option>\n<option value="861">861 Hvolsv\xf6llur</option>\n<option value="870">870 V\xedk</option>\n<option value="871">871 V\xedk</option>\n<option value="880">880 Kirkjub\xe6jarklaustur</option>\n<option value="900">900 Vestmannaeyjar</option>\n<option value="902">902 Vestmannaeyjar</option>\n</select>' - -## CLRutField ############################################################# - -CLRutField is a Field that checks the validity of the Chilean -personal identification number (RUT). It has two modes relaxed (default) and -strict. - ->>> from django.contrib.localflavor.cl.forms import CLRutField ->>> rut = CLRutField() - ->>> rut.clean('11-6') -u'11-6' ->>> rut.clean('116') -u'11-6' - -# valid format, bad verifier. ->>> rut.clean('11.111.111-0') -Traceback (most recent call last): -... -ValidationError: [u'The Chilean RUT is not valid.'] ->>> rut.clean('111') -Traceback (most recent call last): -... -ValidationError: [u'The Chilean RUT is not valid.'] - ->>> rut.clean('767484100') -u'76.748.410-0' ->>> rut.clean('78.412.790-7') -u'78.412.790-7' ->>> rut.clean('8.334.6043') -u'8.334.604-3' ->>> rut.clean('76793310-K') -u'76.793.310-K' - -Strict RUT usage (does not allow imposible values) ->>> rut = CLRutField(strict=True) - ->>> rut.clean('11-6') -Traceback (most recent call last): -... -ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] - -# valid format, bad verifier. ->>> rut.clean('11.111.111-0') -Traceback (most recent call last): -... -ValidationError: [u'The Chilean RUT is not valid.'] - -# Correct input, invalid format. ->>> rut.clean('767484100') -Traceback (most recent call last): -... -ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] ->>> rut.clean('78.412.790-7') -u'78.412.790-7' ->>> rut.clean('8.334.6043') -Traceback (most recent call last): -... -ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] ->>> rut.clean('76793310-K') -Traceback (most recent call last): -... -ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] - -## CLRegionSelect ######################################################### ->>> from django.contrib.localflavor.cl.forms import CLRegionSelect ->>> f = CLRegionSelect() - ->>> f.render('foo', 'bar') -u'<select name="foo">\n<option value="RM">Regi\xf3n Metropolitana de Santiago</option>\n<option value="I">Regi\xf3n de Tarapac\xe1</option>\n<option value="II">Regi\xf3n de Antofagasta</option>\n<option value="III">Regi\xf3n de Atacama</option>\n<option value="IV">Regi\xf3n de Coquimbo</option>\n<option value="V">Regi\xf3n de Valpara\xedso</option>\n<option value="VI">Regi\xf3n del Libertador Bernardo O'Higgins</option>\n<option value="VII">Regi\xf3n del Maule</option>\n<option value="VIII">Regi\xf3n del B\xedo B\xedo</option>\n<option value="IX">Regi\xf3n de la Araucan\xeda</option>\n<option value="X">Regi\xf3n de los Lagos</option>\n<option value="XI">Regi\xf3n de Ays\xe9n del General Carlos Ib\xe1\xf1ez del Campo</option>\n<option value="XII">Regi\xf3n de Magallanes y la Ant\xe1rtica Chilena</option>\n<option value="XIV">Regi\xf3n de Los R\xedos</option>\n<option value="XV">Regi\xf3n de Arica-Parinacota</option>\n</select>' - -# SKPostalCodeField ######################################################### - ->>> from django.contrib.localflavor.sk.forms import SKPostalCodeField ->>> f = SKPostalCodeField() ->>> f.clean('84545x') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.'] ->>> f.clean('91909') -u'91909' ->>> f.clean('917 01') -u'91701' - -# SKRegionSelect ############################################################ - ->>> from django.contrib.localflavor.sk.forms import SKRegionSelect ->>> w = SKRegionSelect() ->>> w.render('regions', 'TT') -u'<select name="regions">\n<option value="BB">Banska Bystrica region</option>\n<option value="BA">Bratislava region</option>\n<option value="KE">Kosice region</option>\n<option value="NR">Nitra region</option>\n<option value="PO">Presov region</option>\n<option value="TN">Trencin region</option>\n<option value="TT" selected="selected">Trnava region</option>\n<option value="ZA">Zilina region</option>\n</select>' - -# SKDistrictSelect ########################################################## - ->>> from django.contrib.localflavor.sk.forms import SKDistrictSelect ->>> w = SKDistrictSelect() ->>> w.render('Districts', 'RK') -u'<select name="Districts">\n<option value="BB">Banska Bystrica</option>\n<option value="BS">Banska Stiavnica</option>\n<option value="BJ">Bardejov</option>\n<option value="BN">Banovce nad Bebravou</option>\n<option value="BR">Brezno</option>\n<option value="BA1">Bratislava I</option>\n<option value="BA2">Bratislava II</option>\n<option value="BA3">Bratislava III</option>\n<option value="BA4">Bratislava IV</option>\n<option value="BA5">Bratislava V</option>\n<option value="BY">Bytca</option>\n<option value="CA">Cadca</option>\n<option value="DT">Detva</option>\n<option value="DK">Dolny Kubin</option>\n<option value="DS">Dunajska Streda</option>\n<option value="GA">Galanta</option>\n<option value="GL">Gelnica</option>\n<option value="HC">Hlohovec</option>\n<option value="HE">Humenne</option>\n<option value="IL">Ilava</option>\n<option value="KK">Kezmarok</option>\n<option value="KN">Komarno</option>\n<option value="KE1">Kosice I</option>\n<option value="KE2">Kosice II</option>\n<option value="KE3">Kosice III</option>\n<option value="KE4">Kosice IV</option>\n<option value="KEO">Kosice - okolie</option>\n<option value="KA">Krupina</option>\n<option value="KM">Kysucke Nove Mesto</option>\n<option value="LV">Levice</option>\n<option value="LE">Levoca</option>\n<option value="LM">Liptovsky Mikulas</option>\n<option value="LC">Lucenec</option>\n<option value="MA">Malacky</option>\n<option value="MT">Martin</option>\n<option value="ML">Medzilaborce</option>\n<option value="MI">Michalovce</option>\n<option value="MY">Myjava</option>\n<option value="NO">Namestovo</option>\n<option value="NR">Nitra</option>\n<option value="NM">Nove Mesto nad Vahom</option>\n<option value="NZ">Nove Zamky</option>\n<option value="PE">Partizanske</option>\n<option value="PK">Pezinok</option>\n<option value="PN">Piestany</option>\n<option value="PT">Poltar</option>\n<option value="PP">Poprad</option>\n<option value="PB">Povazska Bystrica</option>\n<option value="PO">Presov</option>\n<option value="PD">Prievidza</option>\n<option value="PU">Puchov</option>\n<option value="RA">Revuca</option>\n<option value="RS">Rimavska Sobota</option>\n<option value="RV">Roznava</option>\n<option value="RK" selected="selected">Ruzomberok</option>\n<option value="SB">Sabinov</option>\n<option value="SC">Senec</option>\n<option value="SE">Senica</option>\n<option value="SI">Skalica</option>\n<option value="SV">Snina</option>\n<option value="SO">Sobrance</option>\n<option value="SN">Spisska Nova Ves</option>\n<option value="SL">Stara Lubovna</option>\n<option value="SP">Stropkov</option>\n<option value="SK">Svidnik</option>\n<option value="SA">Sala</option>\n<option value="TO">Topolcany</option>\n<option value="TV">Trebisov</option>\n<option value="TN">Trencin</option>\n<option value="TT">Trnava</option>\n<option value="TR">Turcianske Teplice</option>\n<option value="TS">Tvrdosin</option>\n<option value="VK">Velky Krtis</option>\n<option value="VT">Vranov nad Toplou</option>\n<option value="ZM">Zlate Moravce</option>\n<option value="ZV">Zvolen</option>\n<option value="ZC">Zarnovica</option>\n<option value="ZH">Ziar nad Hronom</option>\n<option value="ZA">Zilina</option>\n</select>' - -# PLVoivodeshipSelect ########################################################## - ->>> from django.contrib.localflavor.pl.forms import PLVoivodeshipSelect ->>> f = PLVoivodeshipSelect() ->>> f.render('voivodeships','pomerania') -u'<select name="voivodeships">\n<option value="lower_silesia">Lower Silesia</option>\n<option value="kuyavia-pomerania">Kuyavia-Pomerania</option>\n<option value="lublin">Lublin</option>\n<option value="lubusz">Lubusz</option>\n<option value="lodz">Lodz</option>\n<option value="lesser_poland">Lesser Poland</option>\n<option value="masovia">Masovia</option>\n<option value="opole">Opole</option>\n<option value="subcarpatia">Subcarpatia</option>\n<option value="podlasie">Podlasie</option>\n<option value="pomerania" selected="selected">Pomerania</option>\n<option value="silesia">Silesia</option>\n<option value="swietokrzyskie">Swietokrzyskie</option>\n<option value="warmia-masuria">Warmia-Masuria</option>\n<option value="greater_poland">Greater Poland</option>\n<option value="west_pomerania">West Pomerania</option>\n</select>' - -# PLAdministrativeUnitSelect ########################################################## - ->>> from django.contrib.localflavor.pl.forms import PLAdministrativeUnitSelect ->>> f = PLAdministrativeUnitSelect() ->>> f.render('administrativeunit','katowice') -u'<select name="administrativeunit">\n<option value="wroclaw">Wroc\u0142aw</option>\n<option value="jeleniagora">Jelenia G\xf3ra</option>\n<option value="legnica">Legnica</option>\n<option value="boleslawiecki">boles\u0142awiecki</option>\n<option value="dzierzoniowski">dzier\u017coniowski</option>\n<option value="glogowski">g\u0142ogowski</option>\n<option value="gorowski">g\xf3rowski</option>\n<option value="jaworski">jaworski</option>\n<option value="jeleniogorski">jeleniog\xf3rski</option>\n<option value="kamiennogorski">kamiennog\xf3rski</option>\n<option value="klodzki">k\u0142odzki</option>\n<option value="legnicki">legnicki</option>\n<option value="lubanski">luba\u0144ski</option>\n<option value="lubinski">lubi\u0144ski</option>\n<option value="lwowecki">lw\xf3wecki</option>\n<option value="milicki">milicki</option>\n<option value="olesnicki">ole\u015bnicki</option>\n<option value="olawski">o\u0142awski</option>\n<option value="polkowicki">polkowicki</option>\n<option value="strzelinski">strzeli\u0144ski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="trzebnicki">trzebnicki</option>\n<option value="walbrzyski">wa\u0142brzyski</option>\n<option value="wolowski">wo\u0142owski</option>\n<option value="wroclawski">wroc\u0142awski</option>\n<option value="zabkowicki">z\u0105bkowicki</option>\n<option value="zgorzelecki">zgorzelecki</option>\n<option value="zlotoryjski">z\u0142otoryjski</option>\n<option value="bydgoszcz">Bydgoszcz</option>\n<option value="torun">Toru\u0144</option>\n<option value="wloclawek">W\u0142oc\u0142awek</option>\n<option value="grudziadz">Grudzi\u0105dz</option>\n<option value="aleksandrowski">aleksandrowski</option>\n<option value="brodnicki">brodnicki</option>\n<option value="bydgoski">bydgoski</option>\n<option value="chelminski">che\u0142mi\u0144ski</option>\n<option value="golubsko-dobrzynski">golubsko-dobrzy\u0144ski</option>\n<option value="grudziadzki">grudzi\u0105dzki</option>\n<option value="inowroclawski">inowroc\u0142awski</option>\n<option value="lipnowski">lipnowski</option>\n<option value="mogilenski">mogile\u0144ski</option>\n<option value="nakielski">nakielski</option>\n<option value="radziejowski">radziejowski</option>\n<option value="rypinski">rypi\u0144ski</option>\n<option value="sepolenski">s\u0119pole\u0144ski</option>\n<option value="swiecki">\u015bwiecki</option>\n<option value="torunski">toru\u0144ski</option>\n<option value="tucholski">tucholski</option>\n<option value="wabrzeski">w\u0105brzeski</option>\n<option value="wloclawski">wroc\u0142awski</option>\n<option value="zninski">\u017ani\u0144ski</option>\n<option value="lublin">Lublin</option>\n<option value="biala-podlaska">Bia\u0142a Podlaska</option>\n<option value="chelm">Che\u0142m</option>\n<option value="zamosc">Zamo\u015b\u0107</option>\n<option value="bialski">bialski</option>\n<option value="bilgorajski">bi\u0142gorajski</option>\n<option value="chelmski">che\u0142mski</option>\n<option value="hrubieszowski">hrubieszowski</option>\n<option value="janowski">janowski</option>\n<option value="krasnostawski">krasnostawski</option>\n<option value="krasnicki">kra\u015bnicki</option>\n<option value="lubartowski">lubartowski</option>\n<option value="lubelski">lubelski</option>\n<option value="leczynski">\u0142\u0119czy\u0144ski</option>\n<option value="lukowski">\u0142ukowski</option>\n<option value="opolski">opolski</option>\n<option value="parczewski">parczewski</option>\n<option value="pulawski">pu\u0142awski</option>\n<option value="radzynski">radzy\u0144ski</option>\n<option value="rycki">rycki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wlodawski">w\u0142odawski</option>\n<option value="zamojski">zamojski</option>\n<option value="gorzow-wielkopolski">Gorz\xf3w Wielkopolski</option>\n<option value="zielona-gora">Zielona G\xf3ra</option>\n<option value="gorzowski">gorzowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="miedzyrzecki">mi\u0119dzyrzecki</option>\n<option value="nowosolski">nowosolski</option>\n<option value="slubicki">s\u0142ubicki</option>\n<option value="strzelecko-drezdenecki">strzelecko-drezdenecki</option>\n<option value="sulecinski">sule\u0144ci\u0144ski</option>\n<option value="swiebodzinski">\u015bwiebodzi\u0144ski</option>\n<option value="wschowski">wschowski</option>\n<option value="zielonogorski">zielonog\xf3rski</option>\n<option value="zaganski">\u017caga\u0144ski</option>\n<option value="zarski">\u017carski</option>\n<option value="lodz">\u0141\xf3d\u017a</option>\n<option value="piotrkow-trybunalski">Piotrk\xf3w Trybunalski</option>\n<option value="skierniewice">Skierniewice</option>\n<option value="belchatowski">be\u0142chatowski</option>\n<option value="brzezinski">brzezi\u0144ski</option>\n<option value="kutnowski">kutnowski</option>\n<option value="laski">\u0142aski</option>\n<option value="leczycki">\u0142\u0119czycki</option>\n<option value="lowicki">\u0142owicki</option>\n<option value="lodzki wschodni">\u0142\xf3dzki wschodni</option>\n<option value="opoczynski">opoczy\u0144ski</option>\n<option value="pabianicki">pabianicki</option>\n<option value="pajeczanski">paj\u0119cza\u0144ski</option>\n<option value="piotrkowski">piotrkowski</option>\n<option value="poddebicki">podd\u0119bicki</option>\n<option value="radomszczanski">radomszcza\u0144ski</option>\n<option value="rawski">rawski</option>\n<option value="sieradzki">sieradzki</option>\n<option value="skierniewicki">skierniewicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wielunski">wielu\u0144ski</option>\n<option value="wieruszowski">wieruszowski</option>\n<option value="zdunskowolski">zdu\u0144skowolski</option>\n<option value="zgierski">zgierski</option>\n<option value="krakow">Krak\xf3w</option>\n<option value="tarnow">Tarn\xf3w</option>\n<option value="nowy-sacz">Nowy S\u0105cz</option>\n<option value="bochenski">boche\u0144ski</option>\n<option value="brzeski">brzeski</option>\n<option value="chrzanowski">chrzanowski</option>\n<option value="dabrowski">d\u0105browski</option>\n<option value="gorlicki">gorlicki</option>\n<option value="krakowski">krakowski</option>\n<option value="limanowski">limanowski</option>\n<option value="miechowski">miechowski</option>\n<option value="myslenicki">my\u015blenicki</option>\n<option value="nowosadecki">nowos\u0105decki</option>\n<option value="nowotarski">nowotarski</option>\n<option value="olkuski">olkuski</option>\n<option value="oswiecimski">o\u015bwi\u0119cimski</option>\n<option value="proszowicki">proszowicki</option>\n<option value="suski">suski</option>\n<option value="tarnowski">tarnowski</option>\n<option value="tatrzanski">tatrza\u0144ski</option>\n<option value="wadowicki">wadowicki</option>\n<option value="wielicki">wielicki</option>\n<option value="warszawa">Warszawa</option>\n<option value="ostroleka">Ostro\u0142\u0119ka</option>\n<option value="plock">P\u0142ock</option>\n<option value="radom">Radom</option>\n<option value="siedlce">Siedlce</option>\n<option value="bialobrzeski">bia\u0142obrzeski</option>\n<option value="ciechanowski">ciechanowski</option>\n<option value="garwolinski">garwoli\u0144ski</option>\n<option value="gostyninski">gostyni\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="grojecki">gr\xf3jecki</option>\n<option value="kozienicki">kozenicki</option>\n<option value="legionowski">legionowski</option>\n<option value="lipski">lipski</option>\n<option value="losicki">\u0142osicki</option>\n<option value="makowski">makowski</option>\n<option value="minski">mi\u0144ski</option>\n<option value="mlawski">m\u0142awski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="ostrolecki">ostro\u0142\u0119cki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="otwocki">otwocki</option>\n<option value="piaseczynski">piaseczy\u0144ski</option>\n<option value="plocki">p\u0142ocki</option>\n<option value="plonski">p\u0142o\u0144ski</option>\n<option value="pruszkowski">pruszkowski</option>\n<option value="przasnyski">przasnyski</option>\n<option value="przysuski">przysuski</option>\n<option value="pultuski">pu\u0142tuski</option>\n<option value="radomski">radomski</option>\n<option value="siedlecki">siedlecki</option>\n<option value="sierpecki">sierpecki</option>\n<option value="sochaczewski">sochaczewski</option>\n<option value="sokolowski">soko\u0142owski</option>\n<option value="szydlowiecki">szyd\u0142owiecki</option>\n<option value="warszawski-zachodni">warszawski zachodni</option>\n<option value="wegrowski">w\u0119growski</option>\n<option value="wolominski">wo\u0142omi\u0144ski</option>\n<option value="wyszkowski">wyszkowski</option>\n<option value="zwolenski">zwole\u0144ski</option>\n<option value="zurominski">\u017curomi\u0144ski</option>\n<option value="zyrardowski">\u017cyrardowski</option>\n<option value="opole">Opole</option>\n<option value="brzeski">brzeski</option>\n<option value="glubczycki">g\u0142ubczyski</option>\n<option value="kedzierzynsko-kozielski">k\u0119dzierzy\u0144ski-kozielski</option>\n<option value="kluczborski">kluczborski</option>\n<option value="krapkowicki">krapkowicki</option>\n<option value="namyslowski">namys\u0142owski</option>\n<option value="nyski">nyski</option>\n<option value="oleski">oleski</option>\n<option value="opolski">opolski</option>\n<option value="prudnicki">prudnicki</option>\n<option value="strzelecki">strzelecki</option>\n<option value="rzeszow">Rzesz\xf3w</option>\n<option value="krosno">Krosno</option>\n<option value="przemysl">Przemy\u015bl</option>\n<option value="tarnobrzeg">Tarnobrzeg</option>\n<option value="bieszczadzki">bieszczadzki</option>\n<option value="brzozowski">brzozowski</option>\n<option value="debicki">d\u0119bicki</option>\n<option value="jaroslawski">jaros\u0142awski</option>\n<option value="jasielski">jasielski</option>\n<option value="kolbuszowski">kolbuszowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="leski">leski</option>\n<option value="lezajski">le\u017cajski</option>\n<option value="lubaczowski">lubaczowski</option>\n<option value="lancucki">\u0142a\u0144cucki</option>\n<option value="mielecki">mielecki</option>\n<option value="nizanski">ni\u017ca\u0144ski</option>\n<option value="przemyski">przemyski</option>\n<option value="przeworski">przeworski</option>\n<option value="ropczycko-sedziszowski">ropczycko-s\u0119dziszowski</option>\n<option value="rzeszowski">rzeszowski</option>\n<option value="sanocki">sanocki</option>\n<option value="stalowowolski">stalowowolski</option>\n<option value="strzyzowski">strzy\u017cowski</option>\n<option value="tarnobrzeski">tarnobrzeski</option>\n<option value="bialystok">Bia\u0142ystok</option>\n<option value="lomza">\u0141om\u017ca</option>\n<option value="suwalki">Suwa\u0142ki</option>\n<option value="augustowski">augustowski</option>\n<option value="bialostocki">bia\u0142ostocki</option>\n<option value="bielski">bielski</option>\n<option value="grajewski">grajewski</option>\n<option value="hajnowski">hajnowski</option>\n<option value="kolnenski">kolne\u0144ski</option>\n<option value="\u0142omzynski">\u0142om\u017cy\u0144ski</option>\n<option value="moniecki">moniecki</option>\n<option value="sejnenski">sejne\u0144ski</option>\n<option value="siemiatycki">siematycki</option>\n<option value="sokolski">sok\xf3lski</option>\n<option value="suwalski">suwalski</option>\n<option value="wysokomazowiecki">wysokomazowiecki</option>\n<option value="zambrowski">zambrowski</option>\n<option value="gdansk">Gda\u0144sk</option>\n<option value="gdynia">Gdynia</option>\n<option value="slupsk">S\u0142upsk</option>\n<option value="sopot">Sopot</option>\n<option value="bytowski">bytowski</option>\n<option value="chojnicki">chojnicki</option>\n<option value="czluchowski">cz\u0142uchowski</option>\n<option value="kartuski">kartuski</option>\n<option value="koscierski">ko\u015bcierski</option>\n<option value="kwidzynski">kwidzy\u0144ski</option>\n<option value="leborski">l\u0119borski</option>\n<option value="malborski">malborski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="gdanski">gda\u0144ski</option>\n<option value="pucki">pucki</option>\n<option value="slupski">s\u0142upski</option>\n<option value="starogardzki">starogardzki</option>\n<option value="sztumski">sztumski</option>\n<option value="tczewski">tczewski</option>\n<option value="wejherowski">wejcherowski</option>\n<option value="katowice" selected="selected">Katowice</option>\n<option value="bielsko-biala">Bielsko-Bia\u0142a</option>\n<option value="bytom">Bytom</option>\n<option value="chorzow">Chorz\xf3w</option>\n<option value="czestochowa">Cz\u0119stochowa</option>\n<option value="dabrowa-gornicza">D\u0105browa G\xf3rnicza</option>\n<option value="gliwice">Gliwice</option>\n<option value="jastrzebie-zdroj">Jastrz\u0119bie Zdr\xf3j</option>\n<option value="jaworzno">Jaworzno</option>\n<option value="myslowice">Mys\u0142owice</option>\n<option value="piekary-slaskie">Piekary \u015al\u0105skie</option>\n<option value="ruda-slaska">Ruda \u015al\u0105ska</option>\n<option value="rybnik">Rybnik</option>\n<option value="siemianowice-slaskie">Siemianowice \u015al\u0105skie</option>\n<option value="sosnowiec">Sosnowiec</option>\n<option value="swietochlowice">\u015awi\u0119toch\u0142owice</option>\n<option value="tychy">Tychy</option>\n<option value="zabrze">Zabrze</option>\n<option value="zory">\u017bory</option>\n<option value="bedzinski">b\u0119dzi\u0144ski</option>\n<option value="bielski">bielski</option>\n<option value="bierunsko-ledzinski">bieru\u0144sko-l\u0119dzi\u0144ski</option>\n<option value="cieszynski">cieszy\u0144ski</option>\n<option value="czestochowski">cz\u0119stochowski</option>\n<option value="gliwicki">gliwicki</option>\n<option value="klobucki">k\u0142obucki</option>\n<option value="lubliniecki">lubliniecki</option>\n<option value="mikolowski">miko\u0142owski</option>\n<option value="myszkowski">myszkowski</option>\n<option value="pszczynski">pszczy\u0144ski</option>\n<option value="raciborski">raciborski</option>\n<option value="rybnicki">rybnicki</option>\n<option value="tarnogorski">tarnog\xf3rski</option>\n<option value="wodzislawski">wodzis\u0142awski</option>\n<option value="zawiercianski">zawiercia\u0144ski</option>\n<option value="zywiecki">\u017cywiecki</option>\n<option value="kielce">Kielce</option>\n<option value="buski">buski</option>\n<option value="jedrzejowski">j\u0119drzejowski</option>\n<option value="kazimierski">kazimierski</option>\n<option value="kielecki">kielecki</option>\n<option value="konecki">konecki</option>\n<option value="opatowski">opatowski</option>\n<option value="ostrowiecki">ostrowiecki</option>\n<option value="pinczowski">pi\u0144czowski</option>\n<option value="sandomierski">sandomierski</option>\n<option value="skarzyski">skar\u017cyski</option>\n<option value="starachowicki">starachowicki</option>\n<option value="staszowski">staszowski</option>\n<option value="wloszczowski">w\u0142oszczowski</option>\n<option value="olsztyn">Olsztyn</option>\n<option value="elblag">Elbl\u0105g</option>\n<option value="bartoszycki">bartoszycki</option>\n<option value="braniewski">braniewski</option>\n<option value="dzialdowski">dzia\u0142dowski</option>\n<option value="elblaski">elbl\u0105ski</option>\n<option value="elcki">e\u0142cki</option>\n<option value="gizycki">gi\u017cycki</option>\n<option value="goldapski">go\u0142dapski</option>\n<option value="ilawski">i\u0142awski</option>\n<option value="ketrzynski">k\u0119trzy\u0144ski</option>\n<option value="lidzbarski">lidzbarski</option>\n<option value="mragowski">mr\u0105gowski</option>\n<option value="nidzicki">nidzicki</option>\n<option value="nowomiejski">nowomiejski</option>\n<option value="olecki">olecki</option>\n<option value="olsztynski">olszty\u0144ski</option>\n<option value="ostrodzki">ostr\xf3dzki</option>\n<option value="piski">piski</option>\n<option value="szczycienski">szczycie\u0144ski</option>\n<option value="wegorzewski">w\u0119gorzewski</option>\n<option value="poznan">Pozna\u0144</option>\n<option value="kalisz">Kalisz</option>\n<option value="konin">Konin</option>\n<option value="leszno">Leszno</option>\n<option value="chodzieski">chodziejski</option>\n<option value="czarnkowsko-trzcianecki">czarnkowsko-trzcianecki</option>\n<option value="gnieznienski">gnie\u017anie\u0144ski</option>\n<option value="gostynski">gosty\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="jarocinski">jaroci\u0144ski</option>\n<option value="kaliski">kaliski</option>\n<option value="kepinski">k\u0119pi\u0144ski</option>\n<option value="kolski">kolski</option>\n<option value="koninski">koni\u0144ski</option>\n<option value="koscianski">ko\u015bcia\u0144ski</option>\n<option value="krotoszynski">krotoszy\u0144ski</option>\n<option value="leszczynski">leszczy\u0144ski</option>\n<option value="miedzychodzki">mi\u0119dzychodzki</option>\n<option value="nowotomyski">nowotomyski</option>\n<option value="obornicki">obornicki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="ostrzeszowski">ostrzeszowski</option>\n<option value="pilski">pilski</option>\n<option value="pleszewski">pleszewski</option>\n<option value="poznanski">pozna\u0144ski</option>\n<option value="rawicki">rawicki</option>\n<option value="slupecki">s\u0142upecki</option>\n<option value="szamotulski">szamotulski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="sremski">\u015bremski</option>\n<option value="turecki">turecki</option>\n<option value="wagrowiecki">w\u0105growiecki</option>\n<option value="wolsztynski">wolszty\u0144ski</option>\n<option value="wrzesinski">wrzesi\u0144ski</option>\n<option value="zlotowski">z\u0142otowski</option>\n<option value="bialogardzki">bia\u0142ogardzki</option>\n<option value="choszczenski">choszcze\u0144ski</option>\n<option value="drawski">drawski</option>\n<option value="goleniowski">goleniowski</option>\n<option value="gryficki">gryficki</option>\n<option value="gryfinski">gryfi\u0144ski</option>\n<option value="kamienski">kamie\u0144ski</option>\n<option value="kolobrzeski">ko\u0142obrzeski</option>\n<option value="koszalinski">koszali\u0144ski</option>\n<option value="lobeski">\u0142obeski</option>\n<option value="mysliborski">my\u015bliborski</option>\n<option value="policki">policki</option>\n<option value="pyrzycki">pyrzycki</option>\n<option value="slawienski">s\u0142awie\u0144ski</option>\n<option value="stargardzki">stargardzki</option>\n<option value="szczecinecki">szczecinecki</option>\n<option value="swidwinski">\u015bwidwi\u0144ski</option>\n<option value="walecki">wa\u0142ecki</option>\n</select>' - -# PLPostalCodeField ############################################################## - ->>> from django.contrib.localflavor.pl.forms import PLPostalCodeField ->>> f = PLPostalCodeField() ->>> f.clean('43--434') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XX-XXX.'] ->>> f.clean('41-403') -u'41-403' - -# PLTaxNumberField ############################################################### - ->>> from django.contrib.localflavor.pl.forms import PLTaxNumberField ->>> f = PLTaxNumberField() ->>> f.clean('43-343-234-323') -Traceback (most recent call last): -... -ValidationError: [u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'] ->>> f.clean('43-34-234-323') -u'43-34-234-323' ->>> f.clean('433-344-24-23') -u'433-344-24-23' - -# PLNationalIdentificationNumberField ############################################ - ->>> from django.contrib.localflavor.pl.forms import PLNationalIdentificationNumberField ->>> f = PLNationalIdentificationNumberField() ->>> f.clean('80071610614') -u'80071610614' ->>> f.clean('80071610610') -Traceback (most recent call last): -... -ValidationError: [u'Wrong checksum for the National Identification Number.'] ->>> f.clean('80') -Traceback (most recent call last): -... -ValidationError: [u'National Identification Number consists of 11 digits.'] ->>> f.clean('800716106AA') -Traceback (most recent call last): -... -ValidationError: [u'National Identification Number consists of 11 digits.'] - -# NLPhoneNumberField ######################################################## - ->>> from django.contrib.localflavor.nl.forms import NLPhoneNumberField ->>> f = NLPhoneNumberField(required=False) ->>> f.clean('') -u'' ->>> f.clean('012-3456789') -'012-3456789' ->>> f.clean('0123456789') -'0123456789' ->>> f.clean('+31-12-3456789') -'+31-12-3456789' ->>> f.clean('(0123) 456789') -'(0123) 456789' ->>> f.clean('foo') -Traceback (most recent call last): - ... -ValidationError: [u'Enter a valid phone number'] - -# NLZipCodeField ############################################################ - ->>> from django.contrib.localflavor.nl.forms import NLZipCodeField ->>> f = NLZipCodeField(required=False) ->>> f.clean('') -u'' ->>> f.clean('1234ab') -u'1234 AB' ->>> f.clean('1234 ab') -u'1234 AB' ->>> f.clean('1234 AB') -u'1234 AB' ->>> f.clean('0123AB') -Traceback (most recent call last): - ... -ValidationError: [u'Enter a valid postal code'] ->>> f.clean('foo') -Traceback (most recent call last): - ... -ValidationError: [u'Enter a valid postal code'] - -# NLSoFiNumberField ######################################################### - ->>> from django.contrib.localflavor.nl.forms import NLSoFiNumberField ->>> f = NLSoFiNumberField(required=False) ->>> f.clean('') -u'' ->>> f.clean('123456782') -'123456782' ->>> f.clean('000000000') -Traceback (most recent call last): - ... -ValidationError: [u'Enter a valid SoFi number'] ->>> f.clean('123456789') -Traceback (most recent call last): - ... -ValidationError: [u'Enter a valid SoFi number'] ->>> f.clean('foo') -Traceback (most recent call last): - ... -ValidationError: [u'Enter a valid SoFi number'] - -# NLProvinceSelect ########################################################## - ->>> from django.contrib.localflavor.nl.forms import NLProvinceSelect ->>> s = NLProvinceSelect() ->>> s.render('provinces', 'OV') -u'<select name="provinces">\n<option value="DR">Drente</option>\n<option value="FL">Flevoland</option>\n<option value="FR">Friesland</option>\n<option value="GL">Gelderland</option>\n<option value="GR">Groningen</option>\n<option value="LB">Limburg</option>\n<option value="NB">Noord-Brabant</option>\n<option value="NH">Noord-Holland</option>\n<option value="OV" selected="selected">Overijssel</option>\n<option value="UT">Utrecht</option>\n<option value="ZE">Zeeland</option>\n<option value="ZH">Zuid-Holland</option>\n</select>' - -# ARProvinceField ############################################################# - ->>> from django.contrib.localflavor.ar.forms import ARProvinceSelect ->>> f = ARProvinceSelect() ->>> f.render('provincias', 'A') -u'<select name="provincias">\n<option value="B">Buenos Aires</option>\n<option value="K">Catamarca</option>\n<option value="H">Chaco</option>\n<option value="U">Chubut</option>\n<option value="C">Ciudad Aut\xf3noma de Buenos Aires</option>\n<option value="X">C\xf3rdoba</option>\n<option value="W">Corrientes</option>\n<option value="E">Entre R\xedos</option>\n<option value="P">Formosa</option>\n<option value="Y">Jujuy</option>\n<option value="L">La Pampa</option>\n<option value="F">La Rioja</option>\n<option value="M">Mendoza</option>\n<option value="N">Misiones</option>\n<option value="Q">Neuqu\xe9n</option>\n<option value="R">R\xedo Negro</option>\n<option value="A" selected="selected">Salta</option>\n<option value="J">San Juan</option>\n<option value="D">San Luis</option>\n<option value="Z">Santa Cruz</option>\n<option value="S">Santa Fe</option>\n<option value="G">Santiago del Estero</option>\n<option value="V">Tierra del Fuego, Ant\xe1rtida e Islas del Atl\xe1ntico Sur</option>\n<option value="T">Tucum\xe1n</option>\n</select>' - -# ARPostalCodeField ########################################################### - ->>> from django.contrib.localflavor.ar.forms import ARPostalCodeField ->>> f = ARPostalCodeField() ->>> f.clean('5000') -u'5000' ->>> f.clean('C1064AAB') -u'C1064AAB' ->>> f.clean('c1064AAB') -u'C1064AAB' ->>> f.clean('C1064aab') -u'C1064AAB' ->>> f.clean(u'4400') -u'4400' ->>> f.clean(u'C1064AAB') -u'C1064AAB' ->>> f.clean('C1064AABB') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] ->>> f.clean('C1064AA') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean('C106AAB') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean('106AAB') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean('500') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 4 characters (it has 3).'] ->>> f.clean('5PPP') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(u'') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = ARPostalCodeField(required=False) ->>> f.clean('5000') -u'5000' ->>> f.clean('C1064AAB') -u'C1064AAB' ->>> f.clean('c1064AAB') -u'C1064AAB' ->>> f.clean('C1064aab') -u'C1064AAB' ->>> f.clean(u'4400') -u'4400' ->>> f.clean(u'C1064AAB') -u'C1064AAB' ->>> f.clean('C1064AABB') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] ->>> f.clean('C1064AA') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean('C106AAB') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean('106AAB') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean('500') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 4 characters (it has 3).'] ->>> f.clean('5PPP') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' ->>> f.clean(u'') -u'' - -# ARDNIField ################################################################## - ->>> from django.contrib.localflavor.ar.forms import ARDNIField ->>> f = ARDNIField() ->>> f.clean('20123456') -u'20123456' ->>> f.clean('20.123.456') -u'20123456' ->>> f.clean('9123456') -u'9123456' ->>> f.clean('9.123.456') -u'9123456' ->>> f.clean(u'20123456') -u'20123456' ->>> f.clean(u'20.123.456') -u'20123456' ->>> f.clean('20.123456') -u'20123456' ->>> f.clean('101234566') -Traceback (most recent call last): -... -ValidationError: [u'This field requires 7 or 8 digits.'] ->>> f.clean('W0123456') -Traceback (most recent call last): -... -ValidationError: [u'This field requires only numbers.'] ->>> f.clean('10,123,456') -Traceback (most recent call last): -... -ValidationError: [u'This field requires only numbers.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(u'') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = ARDNIField(required=False) ->>> f.clean('20123456') -u'20123456' ->>> f.clean('20.123.456') -u'20123456' ->>> f.clean('9123456') -u'9123456' ->>> f.clean('9.123.456') -u'9123456' ->>> f.clean(u'20123456') -u'20123456' ->>> f.clean(u'20.123.456') -u'20123456' ->>> f.clean('20.123456') -u'20123456' ->>> f.clean('101234566') -Traceback (most recent call last): -... -ValidationError: [u'This field requires 7 or 8 digits.'] ->>> f.clean('W0123456') -Traceback (most recent call last): -... -ValidationError: [u'This field requires only numbers.'] ->>> f.clean('10,123,456') -Traceback (most recent call last): -... -ValidationError: [u'This field requires only numbers.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' ->>> f.clean(u'') -u'' - -# ARCUITField ################################################################# - ->>> from django.contrib.localflavor.ar.forms import ARCUITField ->>> f = ARCUITField() ->>> f.clean('20-10123456-9') -u'20-10123456-9' ->>> f.clean(u'20-10123456-9') -u'20-10123456-9' ->>> f.clean('27-10345678-4') -u'27-10345678-4' ->>> f.clean('20101234569') -u'20-10123456-9' ->>> f.clean('27103456784') -u'27-10345678-4' ->>> f.clean('2-10123456-9') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('210123456-9') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('20-10123456') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('20-10123456-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('20-10123456-5') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CUIT.'] ->>> f.clean(u'2-10123456-9') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('27-10345678-1') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CUIT.'] ->>> f.clean(u'27-10345678-1') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CUIT.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(u'') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = ARCUITField(required=False) ->>> f.clean('20-10123456-9') -u'20-10123456-9' ->>> f.clean(u'20-10123456-9') -u'20-10123456-9' ->>> f.clean('27-10345678-4') -u'27-10345678-4' ->>> f.clean('20101234569') -u'20-10123456-9' ->>> f.clean('27103456784') -u'27-10345678-4' ->>> f.clean('2-10123456-9') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('210123456-9') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('20-10123456') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('20-10123456-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('20-10123456-5') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CUIT.'] ->>> f.clean(u'2-10123456-9') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] ->>> f.clean('27-10345678-1') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CUIT.'] ->>> f.clean(u'27-10345678-1') -Traceback (most recent call last): -... -ValidationError: [u'Invalid CUIT.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' ->>> f.clean(u'') -u'' - -# CAPostalCodeField ############################################################## - -CAPostalCodeField validates that the data is a six-character Canadian postal code. ->>> from django.contrib.localflavor.ca.forms import CAPostalCodeField ->>> f = CAPostalCodeField() ->>> f.clean('T2S 2H7') -u'T2S 2H7' ->>> f.clean('T2S 2H') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean('2T6 H8I') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean('T2S2H') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean(90210) -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f = CAPostalCodeField(required=False) ->>> f.clean('T2S 2H7') -u'T2S 2H7' ->>> f.clean('T2S2H7') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean('T2S 2H') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean('2T6 H8I') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean('T2S2H') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean(90210) -Traceback (most recent call last): -... -ValidationError: [u'Enter a postal code in the format XXX XXX.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# CAPhoneNumberField ########################################################## - -CAPhoneNumberField validates that the data is a valid Canadian phone number, -including the area code. It's normalized to XXX-XXX-XXXX format. -Note: This test is exactly the same as the USPhoneNumberField except using a real -Candian area code - ->>> from django.contrib.localflavor.ca.forms import CAPhoneNumberField ->>> f = CAPhoneNumberField() ->>> f.clean('403-555-1212') -u'403-555-1212' ->>> f.clean('4035551212') -u'403-555-1212' ->>> f.clean('403 555-1212') -u'403-555-1212' ->>> f.clean('(403) 555-1212') -u'403-555-1212' ->>> f.clean('403 555 1212') -u'403-555-1212' ->>> f.clean('403.555.1212') -u'403-555-1212' ->>> f.clean('403.555-1212') -u'403-555-1212' ->>> f.clean(' (403) 555.1212 ') -u'403-555-1212' ->>> f.clean('555-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean('403-55-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = CAPhoneNumberField(required=False) ->>> f.clean('403-555-1212') -u'403-555-1212' ->>> f.clean('4035551212') -u'403-555-1212' ->>> f.clean('403 555-1212') -u'403-555-1212' ->>> f.clean('(403) 555-1212') -u'403-555-1212' ->>> f.clean('403 555 1212') -u'403-555-1212' ->>> f.clean('403.555.1212') -u'403-555-1212' ->>> f.clean('403.555-1212') -u'403-555-1212' ->>> f.clean(' (403) 555.1212 ') -u'403-555-1212' ->>> f.clean('555-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean('403-55-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# CAProvinceField ################################################################ - -CAProvinceField validates that the data is either an abbreviation or name of a -Canadian province. ->>> from django.contrib.localflavor.ca.forms import CAProvinceField ->>> f = CAProvinceField() ->>> f.clean('ab') -u'AB' ->>> f.clean('BC') -u'BC' ->>> f.clean('nova scotia') -u'NS' ->>> f.clean(' manitoba ') -u'MB' ->>> f.clean('T2S 2H7') -Traceback (most recent call last): -... -ValidationError: [u'Enter a Canadian province or territory.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = CAProvinceField(required=False) ->>> f.clean('ab') -u'AB' ->>> f.clean('BC') -u'BC' ->>> f.clean('nova scotia') -u'NS' ->>> f.clean(' manitoba ') -u'MB' ->>> f.clean('T2S 2H7') -Traceback (most recent call last): -... -ValidationError: [u'Enter a Canadian province or territory.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# CAProvinceSelect ############################################################### - -CAProvinceSelect is a Select widget that uses a list of Canadian provinces/territories -as its choices. ->>> from django.contrib.localflavor.ca.forms import CAProvinceSelect ->>> w = CAProvinceSelect() ->>> print w.render('province', 'AB') -<select name="province"> -<option value="AB" selected="selected">Alberta</option> -<option value="BC">British Columbia</option> -<option value="MB">Manitoba</option> -<option value="NB">New Brunswick</option> -<option value="NF">Newfoundland and Labrador</option> -<option value="NT">Northwest Territories</option> -<option value="NS">Nova Scotia</option> -<option value="NU">Nunavut</option> -<option value="ON">Ontario</option> -<option value="PE">Prince Edward Island</option> -<option value="QC">Quebec</option> -<option value="SK">Saskatchewan</option> -<option value="YK">Yukon</option> -</select> - -# CASocialInsuranceNumberField ################################################# ->>> from django.contrib.localflavor.ca.forms import CASocialInsuranceNumberField ->>> f = CASocialInsuranceNumberField() ->>> f.clean('046-454-286') -u'046-454-286' ->>> f.clean('046-454-287') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'] ->>> f.clean('046 454 286') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'] ->>> f.clean('046-44-286') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'] - -## Generic DateField ########################################################## - ->>> from django.contrib.localflavor.generic.forms import * - -A DateField that uses generic dd/mm/yy dates instead of mm/dd/yy where -appropriate. - ->>> import datetime ->>> f = DateField() ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) -datetime.date(2006, 10, 25) ->>> f.clean('2006-10-25') -datetime.date(2006, 10, 25) ->>> f.clean('25/10/2006') -datetime.date(2006, 10, 25) ->>> f.clean('25/10/06') -datetime.date(2006, 10, 25) ->>> f.clean('Oct 25 2006') -datetime.date(2006, 10, 25) ->>> f.clean('October 25 2006') -datetime.date(2006, 10, 25) ->>> f.clean('October 25, 2006') -datetime.date(2006, 10, 25) ->>> f.clean('25 October 2006') -datetime.date(2006, 10, 25) ->>> f.clean('25 October, 2006') -datetime.date(2006, 10, 25) ->>> f.clean('2006-4-31') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('200a-10-25') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('10/25/06') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = DateField(required=False) ->>> f.clean(None) ->>> repr(f.clean(None)) -'None' ->>> f.clean('') ->>> repr(f.clean('')) -'None' - -DateField accepts an optional input_formats parameter: ->>> f = DateField(input_formats=['%Y %m %d']) ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.date(2006, 10, 25) ->>> f.clean('2006 10 25') -datetime.date(2006, 10, 25) - -The input_formats parameter overrides all default input formats, -so the default formats won't work unless you specify them: ->>> f.clean('2006-10-25') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('25/10/2006') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('25/10/06') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] - -## Generic DateTimeField ###################################################### - -A DateField that uses generic dd/mm/yy dates instead of mm/dd/yy where -appropriate. - ->>> import datetime ->>> f = DateTimeField() ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) -datetime.datetime(2006, 10, 25, 14, 30, 59) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) -datetime.datetime(2006, 10, 25, 14, 30, 59, 200) ->>> f.clean('2006-10-25 14:30:45') -datetime.datetime(2006, 10, 25, 14, 30, 45) ->>> f.clean('2006-10-25 14:30:00') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('2006-10-25 14:30') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('2006-10-25') -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean('25/10/2006 14:30:45') -datetime.datetime(2006, 10, 25, 14, 30, 45) ->>> f.clean('25/10/2006 14:30:00') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('25/10/2006 14:30') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('25/10/2006') -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean('25/10/06 14:30:45') -datetime.datetime(2006, 10, 25, 14, 30, 45) ->>> f.clean('25/10/06 14:30:00') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('25/10/06 14:30') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('25/10/06') -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean('hello') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date/time.'] ->>> f.clean('2006-10-25 4:30 p.m.') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date/time.'] - -DateField accepts an optional input_formats parameter: ->>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p']) ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) -datetime.datetime(2006, 10, 25, 14, 30, 59) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) -datetime.datetime(2006, 10, 25, 14, 30, 59, 200) ->>> f.clean('2006 10 25 2:30 PM') -datetime.datetime(2006, 10, 25, 14, 30) - -The input_formats parameter overrides all default input formats, -so the default formats won't work unless you specify them: ->>> f.clean('2006-10-25 14:30:45') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date/time.'] - ->>> f = DateTimeField(required=False) ->>> f.clean(None) ->>> repr(f.clean(None)) -'None' ->>> f.clean('') ->>> repr(f.clean('')) -'None' - -""" diff --git a/tests/regressiontests/forms/localflavor/__init__.py b/tests/regressiontests/forms/localflavor/__init__.py new file mode 100644 index 0000000000..40a96afc6f --- /dev/null +++ b/tests/regressiontests/forms/localflavor/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/regressiontests/forms/localflavor/ar.py b/tests/regressiontests/forms/localflavor/ar.py new file mode 100644 index 0000000000..e1c827c4a0 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/ar.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ AR form fields. + +tests = r""" +# ARProvinceField ############################################################# + +>>> from django.contrib.localflavor.ar.forms import ARProvinceSelect +>>> f = ARProvinceSelect() +>>> f.render('provincias', 'A') +u'<select name="provincias">\n<option value="B">Buenos Aires</option>\n<option value="K">Catamarca</option>\n<option value="H">Chaco</option>\n<option value="U">Chubut</option>\n<option value="C">Ciudad Aut\xf3noma de Buenos Aires</option>\n<option value="X">C\xf3rdoba</option>\n<option value="W">Corrientes</option>\n<option value="E">Entre R\xedos</option>\n<option value="P">Formosa</option>\n<option value="Y">Jujuy</option>\n<option value="L">La Pampa</option>\n<option value="F">La Rioja</option>\n<option value="M">Mendoza</option>\n<option value="N">Misiones</option>\n<option value="Q">Neuqu\xe9n</option>\n<option value="R">R\xedo Negro</option>\n<option value="A" selected="selected">Salta</option>\n<option value="J">San Juan</option>\n<option value="D">San Luis</option>\n<option value="Z">Santa Cruz</option>\n<option value="S">Santa Fe</option>\n<option value="G">Santiago del Estero</option>\n<option value="V">Tierra del Fuego, Ant\xe1rtida e Islas del Atl\xe1ntico Sur</option>\n<option value="T">Tucum\xe1n</option>\n</select>' + +# ARPostalCodeField ########################################################### + +>>> from django.contrib.localflavor.ar.forms import ARPostalCodeField +>>> f = ARPostalCodeField() +>>> f.clean('5000') +u'5000' +>>> f.clean('C1064AAB') +u'C1064AAB' +>>> f.clean('c1064AAB') +u'C1064AAB' +>>> f.clean('C1064aab') +u'C1064AAB' +>>> f.clean(u'4400') +u'4400' +>>> f.clean(u'C1064AAB') +u'C1064AAB' +>>> f.clean('C1064AABB') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +>>> f.clean('C1064AA') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean('C106AAB') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean('106AAB') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean('500') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 4 characters (it has 3).'] +>>> f.clean('5PPP') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(u'') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = ARPostalCodeField(required=False) +>>> f.clean('5000') +u'5000' +>>> f.clean('C1064AAB') +u'C1064AAB' +>>> f.clean('c1064AAB') +u'C1064AAB' +>>> f.clean('C1064aab') +u'C1064AAB' +>>> f.clean(u'4400') +u'4400' +>>> f.clean(u'C1064AAB') +u'C1064AAB' +>>> f.clean('C1064AABB') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +>>> f.clean('C1064AA') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean('C106AAB') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean('106AAB') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean('500') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 4 characters (it has 3).'] +>>> f.clean('5PPP') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +>>> f.clean(u'') +u'' + +# ARDNIField ################################################################## + +>>> from django.contrib.localflavor.ar.forms import ARDNIField +>>> f = ARDNIField() +>>> f.clean('20123456') +u'20123456' +>>> f.clean('20.123.456') +u'20123456' +>>> f.clean('9123456') +u'9123456' +>>> f.clean('9.123.456') +u'9123456' +>>> f.clean(u'20123456') +u'20123456' +>>> f.clean(u'20.123.456') +u'20123456' +>>> f.clean('20.123456') +u'20123456' +>>> f.clean('101234566') +Traceback (most recent call last): +... +ValidationError: [u'This field requires 7 or 8 digits.'] +>>> f.clean('W0123456') +Traceback (most recent call last): +... +ValidationError: [u'This field requires only numbers.'] +>>> f.clean('10,123,456') +Traceback (most recent call last): +... +ValidationError: [u'This field requires only numbers.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(u'') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = ARDNIField(required=False) +>>> f.clean('20123456') +u'20123456' +>>> f.clean('20.123.456') +u'20123456' +>>> f.clean('9123456') +u'9123456' +>>> f.clean('9.123.456') +u'9123456' +>>> f.clean(u'20123456') +u'20123456' +>>> f.clean(u'20.123.456') +u'20123456' +>>> f.clean('20.123456') +u'20123456' +>>> f.clean('101234566') +Traceback (most recent call last): +... +ValidationError: [u'This field requires 7 or 8 digits.'] +>>> f.clean('W0123456') +Traceback (most recent call last): +... +ValidationError: [u'This field requires only numbers.'] +>>> f.clean('10,123,456') +Traceback (most recent call last): +... +ValidationError: [u'This field requires only numbers.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +>>> f.clean(u'') +u'' + +# ARCUITField ################################################################# + +>>> from django.contrib.localflavor.ar.forms import ARCUITField +>>> f = ARCUITField() +>>> f.clean('20-10123456-9') +u'20-10123456-9' +>>> f.clean(u'20-10123456-9') +u'20-10123456-9' +>>> f.clean('27-10345678-4') +u'27-10345678-4' +>>> f.clean('20101234569') +u'20-10123456-9' +>>> f.clean('27103456784') +u'27-10345678-4' +>>> f.clean('2-10123456-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('210123456-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('20-10123456') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('20-10123456-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('20-10123456-5') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CUIT.'] +>>> f.clean(u'2-10123456-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('27-10345678-1') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CUIT.'] +>>> f.clean(u'27-10345678-1') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CUIT.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(u'') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = ARCUITField(required=False) +>>> f.clean('20-10123456-9') +u'20-10123456-9' +>>> f.clean(u'20-10123456-9') +u'20-10123456-9' +>>> f.clean('27-10345678-4') +u'27-10345678-4' +>>> f.clean('20101234569') +u'20-10123456-9' +>>> f.clean('27103456784') +u'27-10345678-4' +>>> f.clean('2-10123456-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('210123456-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('20-10123456') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('20-10123456-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('20-10123456-5') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CUIT.'] +>>> f.clean(u'2-10123456-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'] +>>> f.clean('27-10345678-1') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CUIT.'] +>>> f.clean(u'27-10345678-1') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CUIT.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +>>> f.clean(u'') +u'' +""" diff --git a/tests/regressiontests/forms/localflavor/au.py b/tests/regressiontests/forms/localflavor/au.py new file mode 100644 index 0000000000..fd4c0d6980 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/au.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ AU form fields. + +tests = r""" +## AUPostCodeField ########################################################## + +A field that accepts a four digit Australian post code. + +>>> from django.contrib.localflavor.au.forms import AUPostCodeField +>>> f = AUPostCodeField() +>>> f.clean('1234') +u'1234' +>>> f.clean('2000') +u'2000' +>>> f.clean('abcd') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean('20001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = AUPostCodeField(required=False) +>>> f.clean('1234') +u'1234' +>>> f.clean('2000') +u'2000' +>>> f.clean('abcd') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean('20001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## AUPhoneNumberField ######################################################## + +A field that accepts a 10 digit Australian phone number. +llows spaces and parentheses around area code. + +>>> from django.contrib.localflavor.au.forms import AUPhoneNumberField +>>> f = AUPhoneNumberField() +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('0213456789') +u'0213456789' +>>> f.clean('02 13 45 67 89') +u'0213456789' +>>> f.clean('(02) 1345 6789') +u'0213456789' +>>> f.clean('(02) 1345-6789') +u'0213456789' +>>> f.clean('(02)1345-6789') +u'0213456789' +>>> f.clean('0408 123 456') +u'0408123456' +>>> f.clean('123') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean('1800DJANGO') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = AUPhoneNumberField(required=False) +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('0213456789') +u'0213456789' +>>> f.clean('02 13 45 67 89') +u'0213456789' +>>> f.clean('(02) 1345 6789') +u'0213456789' +>>> f.clean('(02) 1345-6789') +u'0213456789' +>>> f.clean('(02)1345-6789') +u'0213456789' +>>> f.clean('0408 123 456') +u'0408123456' +>>> f.clean('123') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean('1800DJANGO') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## AUStateSelect ############################################################# + +AUStateSelect is a Select widget that uses a list of Australian +states/territories as its choices. + +>>> from django.contrib.localflavor.au.forms import AUStateSelect +>>> f = AUStateSelect() +>>> print f.render('state', 'NSW') +<select name="state"> +<option value="ACT">Australian Capital Territory</option> +<option value="NSW" selected="selected">New South Wales</option> +<option value="NT">Northern Territory</option> +<option value="QLD">Queensland</option> +<option value="SA">South Australia</option> +<option value="TAS">Tasmania</option> +<option value="VIC">Victoria</option> +<option value="WA">Western Australia</option> +</select> +""" diff --git a/tests/regressiontests/forms/localflavor/br.py b/tests/regressiontests/forms/localflavor/br.py new file mode 100644 index 0000000000..757f382d64 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/br.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ BR form fields. + +tests = r""" +# BRZipCodeField ############################################################ +>>> from django.contrib.localflavor.br.forms import BRZipCodeField +>>> f = BRZipCodeField() +>>> f.clean('12345-123') +u'12345-123' +>>> f.clean('12345_123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('1234-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('abcde-abc') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = BRZipCodeField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +>>> f.clean('-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('abcde-abc') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('1234-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345_123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345-123') +u'12345-123' + +# BRCNPJField ############################################################ + +>>> from django.contrib.localflavor.br.forms import BRCNPJField +>>> f = BRCNPJField(required=True) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('12-345-678/9012-10') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CNPJ number.'] +>>> f.clean('12.345.678/9012-10') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CNPJ number.'] +>>> f.clean('12345678/9012-10') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CNPJ number.'] +>>> f.clean('64.132.916/0001-88') +'64.132.916/0001-88' +>>> f.clean('64-132-916/0001-88') +'64-132-916/0001-88' +>>> f.clean('64132916/0001-88') +'64132916/0001-88' +>>> f.clean('64.132.916/0001-XX') +Traceback (most recent call last): +... +ValidationError: [u'This field requires only numbers.'] +>>> f = BRCNPJField(required=False) +>>> f.clean('') +u'' + +# BRCPFField ################################################################# + +>>> from django.contrib.localflavor.br.forms import BRCPFField +>>> f = BRCPFField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('489.294.654-54') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CPF number.'] +>>> f.clean('295.669.575-98') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CPF number.'] +>>> f.clean('539.315.127-22') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CPF number.'] +>>> f.clean('663.256.017-26') +u'663.256.017-26' +>>> f.clean('66325601726') +u'66325601726' +>>> f.clean('375.788.573-20') +u'375.788.573-20' +>>> f.clean('84828509895') +u'84828509895' +>>> f.clean('375.788.573-XX') +Traceback (most recent call last): +... +ValidationError: [u'This field requires only numbers.'] +>>> f.clean('375.788.573-000') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 14 characters (it has 15).'] +>>> f.clean('123.456.78') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 11 characters (it has 10).'] +>>> f.clean('123456789555') +Traceback (most recent call last): +... +ValidationError: [u'This field requires at most 11 digits or 14 characters.'] +>>> f = BRCPFField(required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' + +# BRPhoneNumberField ######################################################### + +>>> from django.contrib.localflavor.br.forms import BRPhoneNumberField +>>> f = BRPhoneNumberField() +>>> f.clean('41-3562-3464') +u'41-3562-3464' +>>> f.clean('4135623464') +u'41-3562-3464' +>>> f.clean('41 3562-3464') +u'41-3562-3464' +>>> f.clean('41 3562 3464') +u'41-3562-3464' +>>> f.clean('(41) 3562 3464') +u'41-3562-3464' +>>> f.clean('41.3562.3464') +u'41-3562-3464' +>>> f.clean('41.3562-3464') +u'41-3562-3464' +>>> f.clean(' (41) 3562.3464') +u'41-3562-3464' +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = BRPhoneNumberField(required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' +>>> f.clean(' (41) 3562.3464') +u'41-3562-3464' +>>> f.clean('41.3562-3464') +u'41-3562-3464' +>>> f.clean('(41) 3562 3464') +u'41-3562-3464' +>>> f.clean('4135623464') +u'41-3562-3464' +>>> f.clean('41 3562-3464') +u'41-3562-3464' + +# BRStateSelect ############################################################## + +>>> from django.contrib.localflavor.br.forms import BRStateSelect +>>> w = BRStateSelect() +>>> w.render('states', 'PR') +u'<select name="states">\n<option value="AC">Acre</option>\n<option value="AL">Alagoas</option>\n<option value="AP">Amap\xe1</option>\n<option value="AM">Amazonas</option>\n<option value="BA">Bahia</option>\n<option value="CE">Cear\xe1</option>\n<option value="DF">Distrito Federal</option>\n<option value="ES">Esp\xedrito Santo</option>\n<option value="GO">Goi\xe1s</option>\n<option value="MA">Maranh\xe3o</option>\n<option value="MT">Mato Grosso</option>\n<option value="MS">Mato Grosso do Sul</option>\n<option value="MG">Minas Gerais</option>\n<option value="PA">Par\xe1</option>\n<option value="PB">Para\xedba</option>\n<option value="PR" selected="selected">Paran\xe1</option>\n<option value="PE">Pernambuco</option>\n<option value="PI">Piau\xed</option>\n<option value="RJ">Rio de Janeiro</option>\n<option value="RN">Rio Grande do Norte</option>\n<option value="RS">Rio Grande do Sul</option>\n<option value="RO">Rond\xf4nia</option>\n<option value="RR">Roraima</option>\n<option value="SC">Santa Catarina</option>\n<option value="SP">S\xe3o Paulo</option>\n<option value="SE">Sergipe</option>\n<option value="TO">Tocantins</option>\n</select>' + +# BRStateChoiceField ######################################################### +>>> from django.contrib.localflavor.br.forms import BRStateChoiceField +>>> f = BRStateChoiceField() +>>> ', '.join([f.clean(s) for s, _ in f.widget.choices]) +u'AC, AL, AP, AM, BA, CE, DF, ES, GO, MA, MT, MS, MG, PA, PB, PR, PE, PI, RJ, RN, RS, RO, RR, SC, SP, SE, TO' +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('pr') +Traceback (most recent call last): +... +ValidationError: [u'Select a valid brazilian state. That state is not one of the available states.'] +""" diff --git a/tests/regressiontests/forms/localflavor/ca.py b/tests/regressiontests/forms/localflavor/ca.py new file mode 100644 index 0000000000..baeb2ad9a8 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/ca.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ CA form fields. + +tests = r""" +# CAPostalCodeField ############################################################## + +CAPostalCodeField validates that the data is a six-character Canadian postal code. +>>> from django.contrib.localflavor.ca.forms import CAPostalCodeField +>>> f = CAPostalCodeField() +>>> f.clean('T2S 2H7') +u'T2S 2H7' +>>> f.clean('T2S 2H') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean('2T6 H8I') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean('T2S2H') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean(90210) +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f = CAPostalCodeField(required=False) +>>> f.clean('T2S 2H7') +u'T2S 2H7' +>>> f.clean('T2S2H7') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean('T2S 2H') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean('2T6 H8I') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean('T2S2H') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean(90210) +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXX XXX.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# CAPhoneNumberField ########################################################## + +CAPhoneNumberField validates that the data is a valid Canadian phone number, +including the area code. It's normalized to XXX-XXX-XXXX format. +Note: This test is exactly the same as the USPhoneNumberField except using a real +Candian area code + +>>> from django.contrib.localflavor.ca.forms import CAPhoneNumberField +>>> f = CAPhoneNumberField() +>>> f.clean('403-555-1212') +u'403-555-1212' +>>> f.clean('4035551212') +u'403-555-1212' +>>> f.clean('403 555-1212') +u'403-555-1212' +>>> f.clean('(403) 555-1212') +u'403-555-1212' +>>> f.clean('403 555 1212') +u'403-555-1212' +>>> f.clean('403.555.1212') +u'403-555-1212' +>>> f.clean('403.555-1212') +u'403-555-1212' +>>> f.clean(' (403) 555.1212 ') +u'403-555-1212' +>>> f.clean('555-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean('403-55-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = CAPhoneNumberField(required=False) +>>> f.clean('403-555-1212') +u'403-555-1212' +>>> f.clean('4035551212') +u'403-555-1212' +>>> f.clean('403 555-1212') +u'403-555-1212' +>>> f.clean('(403) 555-1212') +u'403-555-1212' +>>> f.clean('403 555 1212') +u'403-555-1212' +>>> f.clean('403.555.1212') +u'403-555-1212' +>>> f.clean('403.555-1212') +u'403-555-1212' +>>> f.clean(' (403) 555.1212 ') +u'403-555-1212' +>>> f.clean('555-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean('403-55-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# CAProvinceField ################################################################ + +CAProvinceField validates that the data is either an abbreviation or name of a +Canadian province. +>>> from django.contrib.localflavor.ca.forms import CAProvinceField +>>> f = CAProvinceField() +>>> f.clean('ab') +u'AB' +>>> f.clean('BC') +u'BC' +>>> f.clean('nova scotia') +u'NS' +>>> f.clean(' manitoba ') +u'MB' +>>> f.clean('T2S 2H7') +Traceback (most recent call last): +... +ValidationError: [u'Enter a Canadian province or territory.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = CAProvinceField(required=False) +>>> f.clean('ab') +u'AB' +>>> f.clean('BC') +u'BC' +>>> f.clean('nova scotia') +u'NS' +>>> f.clean(' manitoba ') +u'MB' +>>> f.clean('T2S 2H7') +Traceback (most recent call last): +... +ValidationError: [u'Enter a Canadian province or territory.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# CAProvinceSelect ############################################################### + +CAProvinceSelect is a Select widget that uses a list of Canadian provinces/territories +as its choices. +>>> from django.contrib.localflavor.ca.forms import CAProvinceSelect +>>> w = CAProvinceSelect() +>>> print w.render('province', 'AB') +<select name="province"> +<option value="AB" selected="selected">Alberta</option> +<option value="BC">British Columbia</option> +<option value="MB">Manitoba</option> +<option value="NB">New Brunswick</option> +<option value="NF">Newfoundland and Labrador</option> +<option value="NT">Northwest Territories</option> +<option value="NS">Nova Scotia</option> +<option value="NU">Nunavut</option> +<option value="ON">Ontario</option> +<option value="PE">Prince Edward Island</option> +<option value="QC">Quebec</option> +<option value="SK">Saskatchewan</option> +<option value="YK">Yukon</option> +</select> + +# CASocialInsuranceNumberField ################################################# +>>> from django.contrib.localflavor.ca.forms import CASocialInsuranceNumberField +>>> f = CASocialInsuranceNumberField() +>>> f.clean('046-454-286') +u'046-454-286' +>>> f.clean('046-454-287') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'] +>>> f.clean('046 454 286') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'] +>>> f.clean('046-44-286') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'] +""" diff --git a/tests/regressiontests/forms/localflavor/ch.py b/tests/regressiontests/forms/localflavor/ch.py new file mode 100644 index 0000000000..b4f8e7d667 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/ch.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ CH form fields. + +tests = r""" +# CHZipCodeField ############################################################ + +>>> from django.contrib.localflavor.ch.forms import CHZipCodeField +>>> f = CHZipCodeField() +>>> f.clean('800x') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXX.'] +>>> f.clean('80 00') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXX.'] +>>> f.clean('8000') +u'8000' + +# CHPhoneNumberField ######################################################## + +>>> from django.contrib.localflavor.ch.forms import CHPhoneNumberField +>>> f = CHPhoneNumberField() +>>> f.clean('01234567890') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.'] +>>> f.clean('1234567890') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.'] +>>> f.clean('0123456789') +u'012 345 67 89' + +# CHIdentityCardNumberField ################################################# + +>>> from django.contrib.localflavor.ch.forms import CHIdentityCardNumberField +>>> f = CHIdentityCardNumberField() +>>> f.clean('C1234567<0') +u'C1234567<0' +>>> f.clean('C1234567<1') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] +>>> f.clean('2123456700') +u'2123456700' +>>> f.clean('2123456701') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] + +# CHStateSelect ############################################################# + +>>> from django.contrib.localflavor.ch.forms import CHStateSelect +>>> w = CHStateSelect() +>>> w.render('state', 'AG') +u'<select name="state">\n<option value="AG" selected="selected">Aargau</option>\n<option value="AI">Appenzell Innerrhoden</option>\n<option value="AR">Appenzell Ausserrhoden</option>\n<option value="BS">Basel-Stadt</option>\n<option value="BL">Basel-Land</option>\n<option value="BE">Berne</option>\n<option value="FR">Fribourg</option>\n<option value="GE">Geneva</option>\n<option value="GL">Glarus</option>\n<option value="GR">Graubuenden</option>\n<option value="JU">Jura</option>\n<option value="LU">Lucerne</option>\n<option value="NE">Neuchatel</option>\n<option value="NW">Nidwalden</option>\n<option value="OW">Obwalden</option>\n<option value="SH">Schaffhausen</option>\n<option value="SZ">Schwyz</option>\n<option value="SO">Solothurn</option>\n<option value="SG">St. Gallen</option>\n<option value="TG">Thurgau</option>\n<option value="TI">Ticino</option>\n<option value="UR">Uri</option>\n<option value="VS">Valais</option>\n<option value="VD">Vaud</option>\n<option value="ZG">Zug</option>\n<option value="ZH">Zurich</option>\n</select>' +""" diff --git a/tests/regressiontests/forms/localflavor/cl.py b/tests/regressiontests/forms/localflavor/cl.py new file mode 100644 index 0000000000..407f6610e4 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/cl.py @@ -0,0 +1,74 @@ + +# Tests for the contrib/localflavor/ CL form fields. + +tests = r""" +## CLRutField ############################################################# + +CLRutField is a Field that checks the validity of the Chilean +personal identification number (RUT). It has two modes relaxed (default) and +strict. + +>>> from django.contrib.localflavor.cl.forms import CLRutField +>>> rut = CLRutField() + +>>> rut.clean('11-6') +u'11-6' +>>> rut.clean('116') +u'11-6' + +# valid format, bad verifier. +>>> rut.clean('11.111.111-0') +Traceback (most recent call last): +... +ValidationError: [u'The Chilean RUT is not valid.'] +>>> rut.clean('111') +Traceback (most recent call last): +... +ValidationError: [u'The Chilean RUT is not valid.'] + +>>> rut.clean('767484100') +u'76.748.410-0' +>>> rut.clean('78.412.790-7') +u'78.412.790-7' +>>> rut.clean('8.334.6043') +u'8.334.604-3' +>>> rut.clean('76793310-K') +u'76.793.310-K' + +Strict RUT usage (does not allow imposible values) +>>> rut = CLRutField(strict=True) + +>>> rut.clean('11-6') +Traceback (most recent call last): +... +ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] + +# valid format, bad verifier. +>>> rut.clean('11.111.111-0') +Traceback (most recent call last): +... +ValidationError: [u'The Chilean RUT is not valid.'] + +# Correct input, invalid format. +>>> rut.clean('767484100') +Traceback (most recent call last): +... +ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] +>>> rut.clean('78.412.790-7') +u'78.412.790-7' +>>> rut.clean('8.334.6043') +Traceback (most recent call last): +... +ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] +>>> rut.clean('76793310-K') +Traceback (most recent call last): +... +ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] + +## CLRegionSelect ######################################################### +>>> from django.contrib.localflavor.cl.forms import CLRegionSelect +>>> f = CLRegionSelect() + +>>> f.render('foo', 'bar') +u'<select name="foo">\n<option value="RM">Regi\xf3n Metropolitana de Santiago</option>\n<option value="I">Regi\xf3n de Tarapac\xe1</option>\n<option value="II">Regi\xf3n de Antofagasta</option>\n<option value="III">Regi\xf3n de Atacama</option>\n<option value="IV">Regi\xf3n de Coquimbo</option>\n<option value="V">Regi\xf3n de Valpara\xedso</option>\n<option value="VI">Regi\xf3n del Libertador Bernardo O'Higgins</option>\n<option value="VII">Regi\xf3n del Maule</option>\n<option value="VIII">Regi\xf3n del B\xedo B\xedo</option>\n<option value="IX">Regi\xf3n de la Araucan\xeda</option>\n<option value="X">Regi\xf3n de los Lagos</option>\n<option value="XI">Regi\xf3n de Ays\xe9n del General Carlos Ib\xe1\xf1ez del Campo</option>\n<option value="XII">Regi\xf3n de Magallanes y la Ant\xe1rtica Chilena</option>\n<option value="XIV">Regi\xf3n de Los R\xedos</option>\n<option value="XV">Regi\xf3n de Arica-Parinacota</option>\n</select>' +""" diff --git a/tests/regressiontests/forms/localflavor/de.py b/tests/regressiontests/forms/localflavor/de.py new file mode 100644 index 0000000000..e02323fc62 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/de.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ DE form fields. + +tests = r""" +# DEZipCodeField ############################################################## + +>>> from django.contrib.localflavor.de.forms import DEZipCodeField +>>> f = DEZipCodeField() +>>> f.clean('99423') +u'99423' +>>> f.clean(' 99423') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] + +# DEStateSelect ############################################################# + +>>> from django.contrib.localflavor.de.forms import DEStateSelect +>>> w = DEStateSelect() +>>> w.render('states', 'TH') +u'<select name="states">\n<option value="BW">Baden-Wuerttemberg</option>\n<option value="BY">Bavaria</option>\n<option value="BE">Berlin</option>\n<option value="BB">Brandenburg</option>\n<option value="HB">Bremen</option>\n<option value="HH">Hamburg</option>\n<option value="HE">Hessen</option>\n<option value="MV">Mecklenburg-Western Pomerania</option>\n<option value="NI">Lower Saxony</option>\n<option value="NW">North Rhine-Westphalia</option>\n<option value="RP">Rhineland-Palatinate</option>\n<option value="SL">Saarland</option>\n<option value="SN">Saxony</option>\n<option value="ST">Saxony-Anhalt</option>\n<option value="SH">Schleswig-Holstein</option>\n<option value="TH" selected="selected">Thuringia</option>\n</select>' + +# DEIdentityCardNumberField ################################################# + +>>> from django.contrib.localflavor.de.forms import DEIdentityCardNumberField +>>> f = DEIdentityCardNumberField() +>>> f.clean('7549313035D-6004103-0903042-0') +u'7549313035D-6004103-0903042-0' +>>> f.clean('9786324830D 6104243 0910271 2') +u'9786324830D-6104243-0910271-2' +>>> f.clean('0434657485D-6407276-0508137-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.'] +""" diff --git a/tests/regressiontests/forms/localflavor/fi.py b/tests/regressiontests/forms/localflavor/fi.py new file mode 100644 index 0000000000..f29fcc3c69 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/fi.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ FI form fields. + +tests = r""" +# FIZipCodeField ############################################################# + +FIZipCodeField validates that the data is a valid FI zipcode. +>>> from django.contrib.localflavor.fi.forms import FIZipCodeField +>>> f = FIZipCodeField() +>>> f.clean('20540') +u'20540' +>>> f.clean('20101') +u'20101' +>>> f.clean('20s40') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('205401') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = FIZipCodeField(required=False) +>>> f.clean('20540') +u'20540' +>>> f.clean('20101') +u'20101' +>>> f.clean('20s40') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('205401') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# FIMunicipalitySelect ############################################################### + +A Select widget that uses a list of Finnish municipalities as its choices. +>>> from django.contrib.localflavor.fi.forms import FIMunicipalitySelect +>>> w = FIMunicipalitySelect() +>>> unicode(w.render('municipalities', 'turku')) +u'<select name="municipalities">\n<option value="akaa">Akaa</option>\n<option value="alaharma">Alah\xe4rm\xe4</option>\n<option value="alajarvi">Alaj\xe4rvi</option>\n<option value="alastaro">Alastaro</option>\n<option value="alavieska">Alavieska</option>\n<option value="alavus">Alavus</option>\n<option value="anjalankoski">Anjalankoski</option>\n<option value="artjarvi">Artj\xe4rvi</option>\n<option value="asikkala">Asikkala</option>\n<option value="askainen">Askainen</option>\n<option value="askola">Askola</option>\n<option value="aura">Aura</option>\n<option value="brando">Br\xe4nd\xf6</option>\n<option value="dragsfjard">Dragsfj\xe4rd</option>\n<option value="eckero">Ecker\xf6</option>\n<option value="elimaki">Elim\xe4ki</option>\n<option value="eno">Eno</option>\n<option value="enonkoski">Enonkoski</option>\n<option value="enontekio">Enonteki\xf6</option>\n<option value="espoo">Espoo</option>\n<option value="eura">Eura</option>\n<option value="eurajoki">Eurajoki</option>\n<option value="evijarvi">Evij\xe4rvi</option>\n<option value="finstrom">Finstr\xf6m</option>\n<option value="forssa">Forssa</option>\n<option value="foglo">F\xf6gl\xf6</option>\n<option value="geta">Geta</option>\n<option value="haapajarvi">Haapaj\xe4rvi</option>\n<option value="haapavesi">Haapavesi</option>\n<option value="hailuoto">Hailuoto</option>\n<option value="halikko">Halikko</option>\n<option value="halsua">Halsua</option>\n<option value="hamina">Hamina</option>\n<option value="hammarland">Hammarland</option>\n<option value="hankasalmi">Hankasalmi</option>\n<option value="hanko">Hanko</option>\n<option value="harjavalta">Harjavalta</option>\n<option value="hartola">Hartola</option>\n<option value="hattula">Hattula</option>\n<option value="hauho">Hauho</option>\n<option value="haukipudas">Haukipudas</option>\n<option value="hausjarvi">Hausj\xe4rvi</option>\n<option value="heinola">Heinola</option>\n<option value="heinavesi">Hein\xe4vesi</option>\n<option value="helsinki">Helsinki</option>\n<option value="himanka">Himanka</option>\n<option value="hirvensalmi">Hirvensalmi</option>\n<option value="hollola">Hollola</option>\n<option value="honkajoki">Honkajoki</option>\n<option value="houtskari">Houtskari</option>\n<option value="huittinen">Huittinen</option>\n<option value="humppila">Humppila</option>\n<option value="hyrynsalmi">Hyrynsalmi</option>\n<option value="hyvinkaa">Hyvink\xe4\xe4</option>\n<option value="hameenkoski">H\xe4meenkoski</option>\n<option value="hameenkyro">H\xe4meenkyr\xf6</option>\n<option value="hameenlinna">H\xe4meenlinna</option>\n<option value="ii">Ii</option>\n<option value="iisalmi">Iisalmi</option>\n<option value="iitti">Iitti</option>\n<option value="ikaalinen">Ikaalinen</option>\n<option value="ilmajoki">Ilmajoki</option>\n<option value="ilomantsi">Ilomantsi</option>\n<option value="imatra">Imatra</option>\n<option value="inari">Inari</option>\n<option value="inio">Ini\xf6</option>\n<option value="inkoo">Inkoo</option>\n<option value="isojoki">Isojoki</option>\n<option value="isokyro">Isokyr\xf6</option>\n<option value="jaala">Jaala</option>\n<option value="jalasjarvi">Jalasj\xe4rvi</option>\n<option value="janakkala">Janakkala</option>\n<option value="joensuu">Joensuu</option>\n<option value="jokioinen">Jokioinen</option>\n<option value="jomala">Jomala</option>\n<option value="joroinen">Joroinen</option>\n<option value="joutsa">Joutsa</option>\n<option value="joutseno">Joutseno</option>\n<option value="juankoski">Juankoski</option>\n<option value="jurva">Jurva</option>\n<option value="juuka">Juuka</option>\n<option value="juupajoki">Juupajoki</option>\n<option value="juva">Juva</option>\n<option value="jyvaskyla">Jyv\xe4skyl\xe4</option>\n<option value="jyvaskylan_mlk">Jyv\xe4skyl\xe4n maalaiskunta</option>\n<option value="jamijarvi">J\xe4mij\xe4rvi</option>\n<option value="jamsa">J\xe4ms\xe4</option>\n<option value="jamsankoski">J\xe4ms\xe4nkoski</option>\n<option value="jarvenpaa">J\xe4rvenp\xe4\xe4</option>\n<option value="kaarina">Kaarina</option>\n<option value="kaavi">Kaavi</option>\n<option value="kajaani">Kajaani</option>\n<option value="kalajoki">Kalajoki</option>\n<option value="kalvola">Kalvola</option>\n<option value="kangasala">Kangasala</option>\n<option value="kangasniemi">Kangasniemi</option>\n<option value="kankaanpaa">Kankaanp\xe4\xe4</option>\n<option value="kannonkoski">Kannonkoski</option>\n<option value="kannus">Kannus</option>\n<option value="karijoki">Karijoki</option>\n<option value="karjaa">Karjaa</option>\n<option value="karjalohja">Karjalohja</option>\n<option value="karkkila">Karkkila</option>\n<option value="karstula">Karstula</option>\n<option value="karttula">Karttula</option>\n<option value="karvia">Karvia</option>\n<option value="kaskinen">Kaskinen</option>\n<option value="kauhajoki">Kauhajoki</option>\n<option value="kauhava">Kauhava</option>\n<option value="kauniainen">Kauniainen</option>\n<option value="kaustinen">Kaustinen</option>\n<option value="keitele">Keitele</option>\n<option value="kemi">Kemi</option>\n<option value="kemijarvi">Kemij\xe4rvi</option>\n<option value="keminmaa">Keminmaa</option>\n<option value="kemio">Kemi\xf6</option>\n<option value="kempele">Kempele</option>\n<option value="kerava">Kerava</option>\n<option value="kerimaki">Kerim\xe4ki</option>\n<option value="kestila">Kestil\xe4</option>\n<option value="kesalahti">Kes\xe4lahti</option>\n<option value="keuruu">Keuruu</option>\n<option value="kihnio">Kihni\xf6</option>\n<option value="kiikala">Kiikala</option>\n<option value="kiikoinen">Kiikoinen</option>\n<option value="kiiminki">Kiiminki</option>\n<option value="kinnula">Kinnula</option>\n<option value="kirkkonummi">Kirkkonummi</option>\n<option value="kisko">Kisko</option>\n<option value="kitee">Kitee</option>\n<option value="kittila">Kittil\xe4</option>\n<option value="kiukainen">Kiukainen</option>\n<option value="kiuruvesi">Kiuruvesi</option>\n<option value="kivijarvi">Kivij\xe4rvi</option>\n<option value="kokemaki">Kokem\xe4ki</option>\n<option value="kokkola">Kokkola</option>\n<option value="kolari">Kolari</option>\n<option value="konnevesi">Konnevesi</option>\n<option value="kontiolahti">Kontiolahti</option>\n<option value="korpilahti">Korpilahti</option>\n<option value="korppoo">Korppoo</option>\n<option value="korsnas">Korsn\xe4s</option>\n<option value="kortesjarvi">Kortesj\xe4rvi</option>\n<option value="koskitl">KoskiTl</option>\n<option value="kotka">Kotka</option>\n<option value="kouvola">Kouvola</option>\n<option value="kristiinankaupunki">Kristiinankaupunki</option>\n<option value="kruunupyy">Kruunupyy</option>\n<option value="kuhmalahti">Kuhmalahti</option>\n<option value="kuhmo">Kuhmo</option>\n<option value="kuhmoinen">Kuhmoinen</option>\n<option value="kumlinge">Kumlinge</option>\n<option value="kuopio">Kuopio</option>\n<option value="kuortane">Kuortane</option>\n<option value="kurikka">Kurikka</option>\n<option value="kuru">Kuru</option>\n<option value="kustavi">Kustavi</option>\n<option value="kuusamo">Kuusamo</option>\n<option value="kuusankoski">Kuusankoski</option>\n<option value="kuusjoki">Kuusjoki</option>\n<option value="kylmakoski">Kylm\xe4koski</option>\n<option value="kyyjarvi">Kyyj\xe4rvi</option>\n<option value="kalvia">K\xe4lvi\xe4</option>\n<option value="karkola">K\xe4rk\xf6l\xe4</option>\n<option value="karsamaki">K\xe4rs\xe4m\xe4ki</option>\n<option value="kokar">K\xf6kar</option>\n<option value="koylio">K\xf6yli\xf6</option>\n<option value="lahti">Lahti</option>\n<option value="laihia">Laihia</option>\n<option value="laitila">Laitila</option>\n<option value="lammi">Lammi</option>\n<option value="lapinjarvi">Lapinj\xe4rvi</option>\n<option value="lapinlahti">Lapinlahti</option>\n<option value="lappajarvi">Lappaj\xe4rvi</option>\n<option value="lappeenranta">Lappeenranta</option>\n<option value="lappi">Lappi</option>\n<option value="lapua">Lapua</option>\n<option value="laukaa">Laukaa</option>\n<option value="lavia">Lavia</option>\n<option value="lehtimaki">Lehtim\xe4ki</option>\n<option value="leivonmaki">Leivonm\xe4ki</option>\n<option value="lemi">Lemi</option>\n<option value="lemland">Lemland</option>\n<option value="lempaala">Lemp\xe4\xe4l\xe4</option>\n<option value="lemu">Lemu</option>\n<option value="leppavirta">Lepp\xe4virta</option>\n<option value="lestijarvi">Lestij\xe4rvi</option>\n<option value="lieksa">Lieksa</option>\n<option value="lieto">Lieto</option>\n<option value="liljendal">Liljendal</option>\n<option value="liminka">Liminka</option>\n<option value="liperi">Liperi</option>\n<option value="lohja">Lohja</option>\n<option value="lohtaja">Lohtaja</option>\n<option value="loimaa">Loimaa</option>\n<option value="loppi">Loppi</option>\n<option value="loviisa">Loviisa</option>\n<option value="luhanka">Luhanka</option>\n<option value="lumijoki">Lumijoki</option>\n<option value="lumparland">Lumparland</option>\n<option value="luoto">Luoto</option>\n<option value="luumaki">Luum\xe4ki</option>\n<option value="luvia">Luvia</option>\n<option value="maalahti">Maalahti</option>\n<option value="maaninka">Maaninka</option>\n<option value="maarianhamina">Maarianhamina</option>\n<option value="marttila">Marttila</option>\n<option value="masku">Masku</option>\n<option value="mellila">Mellil\xe4</option>\n<option value="merijarvi">Merij\xe4rvi</option>\n<option value="merikarvia">Merikarvia</option>\n<option value="merimasku">Merimasku</option>\n<option value="miehikkala">Miehikk\xe4l\xe4</option>\n<option value="mikkeli">Mikkeli</option>\n<option value="mouhijarvi">Mouhij\xe4rvi</option>\n<option value="muhos">Muhos</option>\n<option value="multia">Multia</option>\n<option value="muonio">Muonio</option>\n<option value="mustasaari">Mustasaari</option>\n<option value="muurame">Muurame</option>\n<option value="muurla">Muurla</option>\n<option value="mynamaki">Myn\xe4m\xe4ki</option>\n<option value="myrskyla">Myrskyl\xe4</option>\n<option value="mantsala">M\xe4nts\xe4l\xe4</option>\n<option value="mantta">M\xe4ntt\xe4</option>\n<option value="mantyharju">M\xe4ntyharju</option>\n<option value="naantali">Naantali</option>\n<option value="nakkila">Nakkila</option>\n<option value="nastola">Nastola</option>\n<option value="nauvo">Nauvo</option>\n<option value="nilsia">Nilsi\xe4</option>\n<option value="nivala">Nivala</option>\n<option value="nokia">Nokia</option>\n<option value="noormarkku">Noormarkku</option>\n<option value="nousiainen">Nousiainen</option>\n<option value="nummi-pusula">Nummi-Pusula</option>\n<option value="nurmes">Nurmes</option>\n<option value="nurmijarvi">Nurmij\xe4rvi</option>\n<option value="nurmo">Nurmo</option>\n<option value="narpio">N\xe4rpi\xf6</option>\n<option value="oravainen">Oravainen</option>\n<option value="orimattila">Orimattila</option>\n<option value="oripaa">Orip\xe4\xe4</option>\n<option value="orivesi">Orivesi</option>\n<option value="oulainen">Oulainen</option>\n<option value="oulu">Oulu</option>\n<option value="oulunsalo">Oulunsalo</option>\n<option value="outokumpu">Outokumpu</option>\n<option value="padasjoki">Padasjoki</option>\n<option value="paimio">Paimio</option>\n<option value="paltamo">Paltamo</option>\n<option value="parainen">Parainen</option>\n<option value="parikkala">Parikkala</option>\n<option value="parkano">Parkano</option>\n<option value="pedersore">Peders\xf6re</option>\n<option value="pelkosenniemi">Pelkosenniemi</option>\n<option value="pello">Pello</option>\n<option value="perho">Perho</option>\n<option value="pernaja">Pernaja</option>\n<option value="pernio">Perni\xf6</option>\n<option value="pertteli">Pertteli</option>\n<option value="pertunmaa">Pertunmaa</option>\n<option value="petajavesi">Pet\xe4j\xe4vesi</option>\n<option value="pieksamaki">Pieks\xe4m\xe4ki</option>\n<option value="pielavesi">Pielavesi</option>\n<option value="pietarsaari">Pietarsaari</option>\n<option value="pihtipudas">Pihtipudas</option>\n<option value="piikkio">Piikki\xf6</option>\n<option value="piippola">Piippola</option>\n<option value="pirkkala">Pirkkala</option>\n<option value="pohja">Pohja</option>\n<option value="polvijarvi">Polvij\xe4rvi</option>\n<option value="pomarkku">Pomarkku</option>\n<option value="pori">Pori</option>\n<option value="pornainen">Pornainen</option>\n<option value="porvoo">Porvoo</option>\n<option value="posio">Posio</option>\n<option value="pudasjarvi">Pudasj\xe4rvi</option>\n<option value="pukkila">Pukkila</option>\n<option value="pulkkila">Pulkkila</option>\n<option value="punkaharju">Punkaharju</option>\n<option value="punkalaidun">Punkalaidun</option>\n<option value="puolanka">Puolanka</option>\n<option value="puumala">Puumala</option>\n<option value="pyhtaa">Pyht\xe4\xe4</option>\n<option value="pyhajoki">Pyh\xe4joki</option>\n<option value="pyhajarvi">Pyh\xe4j\xe4rvi</option>\n<option value="pyhanta">Pyh\xe4nt\xe4</option>\n<option value="pyharanta">Pyh\xe4ranta</option>\n<option value="pyhaselka">Pyh\xe4selk\xe4</option>\n<option value="pylkonmaki">Pylk\xf6nm\xe4ki</option>\n<option value="palkane">P\xe4lk\xe4ne</option>\n<option value="poytya">P\xf6yty\xe4</option>\n<option value="raahe">Raahe</option>\n<option value="raisio">Raisio</option>\n<option value="rantasalmi">Rantasalmi</option>\n<option value="rantsila">Rantsila</option>\n<option value="ranua">Ranua</option>\n<option value="rauma">Rauma</option>\n<option value="rautalampi">Rautalampi</option>\n<option value="rautavaara">Rautavaara</option>\n<option value="rautjarvi">Rautj\xe4rvi</option>\n<option value="reisjarvi">Reisj\xe4rvi</option>\n<option value="renko">Renko</option>\n<option value="riihimaki">Riihim\xe4ki</option>\n<option value="ristiina">Ristiina</option>\n<option value="ristijarvi">Ristij\xe4rvi</option>\n<option value="rovaniemi">Rovaniemi</option>\n<option value="ruokolahti">Ruokolahti</option>\n<option value="ruotsinpyhtaa">Ruotsinpyht\xe4\xe4</option>\n<option value="ruovesi">Ruovesi</option>\n<option value="rusko">Rusko</option>\n<option value="rymattyla">Rym\xe4ttyl\xe4</option>\n<option value="raakkyla">R\xe4\xe4kkyl\xe4</option>\n<option value="saarijarvi">Saarij\xe4rvi</option>\n<option value="salla">Salla</option>\n<option value="salo">Salo</option>\n<option value="saltvik">Saltvik</option>\n<option value="sammatti">Sammatti</option>\n<option value="sauvo">Sauvo</option>\n<option value="savitaipale">Savitaipale</option>\n<option value="savonlinna">Savonlinna</option>\n<option value="savonranta">Savonranta</option>\n<option value="savukoski">Savukoski</option>\n<option value="seinajoki">Sein\xe4joki</option>\n<option value="sievi">Sievi</option>\n<option value="siikainen">Siikainen</option>\n<option value="siikajoki">Siikajoki</option>\n<option value="siilinjarvi">Siilinj\xe4rvi</option>\n<option value="simo">Simo</option>\n<option value="sipoo">Sipoo</option>\n<option value="siuntio">Siuntio</option>\n<option value="sodankyla">Sodankyl\xe4</option>\n<option value="soini">Soini</option>\n<option value="somero">Somero</option>\n<option value="sonkajarvi">Sonkaj\xe4rvi</option>\n<option value="sotkamo">Sotkamo</option>\n<option value="sottunga">Sottunga</option>\n<option value="sulkava">Sulkava</option>\n<option value="sund">Sund</option>\n<option value="suomenniemi">Suomenniemi</option>\n<option value="suomusjarvi">Suomusj\xe4rvi</option>\n<option value="suomussalmi">Suomussalmi</option>\n<option value="suonenjoki">Suonenjoki</option>\n<option value="sysma">Sysm\xe4</option>\n<option value="sakyla">S\xe4kyl\xe4</option>\n<option value="sarkisalo">S\xe4rkisalo</option>\n<option value="taipalsaari">Taipalsaari</option>\n<option value="taivalkoski">Taivalkoski</option>\n<option value="taivassalo">Taivassalo</option>\n<option value="tammela">Tammela</option>\n<option value="tammisaari">Tammisaari</option>\n<option value="tampere">Tampere</option>\n<option value="tarvasjoki">Tarvasjoki</option>\n<option value="tervo">Tervo</option>\n<option value="tervola">Tervola</option>\n<option value="teuva">Teuva</option>\n<option value="tohmajarvi">Tohmaj\xe4rvi</option>\n<option value="toholampi">Toholampi</option>\n<option value="toivakka">Toivakka</option>\n<option value="tornio">Tornio</option>\n<option value="turku" selected="selected">Turku</option>\n<option value="tuulos">Tuulos</option>\n<option value="tuusniemi">Tuusniemi</option>\n<option value="tuusula">Tuusula</option>\n<option value="tyrnava">Tyrn\xe4v\xe4</option>\n<option value="toysa">T\xf6ys\xe4</option>\n<option value="ullava">Ullava</option>\n<option value="ulvila">Ulvila</option>\n<option value="urjala">Urjala</option>\n<option value="utajarvi">Utaj\xe4rvi</option>\n<option value="utsjoki">Utsjoki</option>\n<option value="uurainen">Uurainen</option>\n<option value="uusikaarlepyy">Uusikaarlepyy</option>\n<option value="uusikaupunki">Uusikaupunki</option>\n<option value="vaala">Vaala</option>\n<option value="vaasa">Vaasa</option>\n<option value="vahto">Vahto</option>\n<option value="valkeakoski">Valkeakoski</option>\n<option value="valkeala">Valkeala</option>\n<option value="valtimo">Valtimo</option>\n<option value="vammala">Vammala</option>\n<option value="vampula">Vampula</option>\n<option value="vantaa">Vantaa</option>\n<option value="varkaus">Varkaus</option>\n<option value="varpaisjarvi">Varpaisj\xe4rvi</option>\n<option value="vehmaa">Vehmaa</option>\n<option value="velkua">Velkua</option>\n<option value="vesanto">Vesanto</option>\n<option value="vesilahti">Vesilahti</option>\n<option value="veteli">Veteli</option>\n<option value="vierema">Vierem\xe4</option>\n<option value="vihanti">Vihanti</option>\n<option value="vihti">Vihti</option>\n<option value="viitasaari">Viitasaari</option>\n<option value="vilppula">Vilppula</option>\n<option value="vimpeli">Vimpeli</option>\n<option value="virolahti">Virolahti</option>\n<option value="virrat">Virrat</option>\n<option value="vardo">V\xe5rd\xf6</option>\n<option value="vahakyro">V\xe4h\xe4kyr\xf6</option>\n<option value="vastanfjard">V\xe4stanfj\xe4rd</option>\n<option value="voyri-maksamaa">V\xf6yri-Maksamaa</option>\n<option value="yliharma">Ylih\xe4rm\xe4</option>\n<option value="yli-ii">Yli-Ii</option>\n<option value="ylikiiminki">Ylikiiminki</option>\n<option value="ylistaro">Ylistaro</option>\n<option value="ylitornio">Ylitornio</option>\n<option value="ylivieska">Ylivieska</option>\n<option value="ylamaa">Yl\xe4maa</option>\n<option value="ylane">Yl\xe4ne</option>\n<option value="ylojarvi">Yl\xf6j\xe4rvi</option>\n<option value="ypaja">Yp\xe4j\xe4</option>\n<option value="aetsa">\xc4ets\xe4</option>\n<option value="ahtari">\xc4ht\xe4ri</option>\n<option value="aanekoski">\xc4\xe4nekoski</option>\n</select>' + +# FISocialSecurityNumber ############################################################## + +>>> from django.contrib.localflavor.fi.forms import FISocialSecurityNumber +>>> f = FISocialSecurityNumber() +>>> f.clean('010101-0101') +u'010101-0101' +>>> f.clean('010101+0101') +u'010101+0101' +>>> f.clean('010101A0101') +u'010101A0101' +>>> f.clean('101010-0102') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> f.clean('10a010-0101') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> f.clean('101010-0\xe401') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> f.clean('101010b0101') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f = FISocialSecurityNumber(required=False) +>>> f.clean('010101-0101') +u'010101-0101' +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +""" diff --git a/tests/regressiontests/forms/localflavor/fr.py b/tests/regressiontests/forms/localflavor/fr.py new file mode 100644 index 0000000000..ea6c7899aa --- /dev/null +++ b/tests/regressiontests/forms/localflavor/fr.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ FR form fields. + +tests = r""" +# FRZipCodeField ############################################################# + +FRZipCodeField validates that the data is a valid FR zipcode. +>>> from django.contrib.localflavor.fr.forms import FRZipCodeField +>>> f = FRZipCodeField() +>>> f.clean('75001') +u'75001' +>>> f.clean('93200') +u'93200' +>>> f.clean('2A200') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('980001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = FRZipCodeField(required=False) +>>> f.clean('75001') +u'75001' +>>> f.clean('93200') +u'93200' +>>> f.clean('2A200') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('980001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + + +# FRPhoneNumberField ########################################################## + +FRPhoneNumberField validates that the data is a valid french phone number. +It's normalized to 0X XX XX XX XX format. Dots are valid too. +>>> from django.contrib.localflavor.fr.forms import FRPhoneNumberField +>>> f = FRPhoneNumberField() +>>> f.clean('01 55 44 58 64') +u'01 55 44 58 64' +>>> f.clean('0155445864') +u'01 55 44 58 64' +>>> f.clean('01 5544 5864') +u'01 55 44 58 64' +>>> f.clean('01 55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01.55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01,55,44,58,64') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean('555 015 544') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = FRPhoneNumberField(required=False) +>>> f.clean('01 55 44 58 64') +u'01 55 44 58 64' +>>> f.clean('0155445864') +u'01 55 44 58 64' +>>> f.clean('01 5544 5864') +u'01 55 44 58 64' +>>> f.clean('01 55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01.55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01,55,44,58,64') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean('555 015 544') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# FRDepartmentSelect ############################################################### + +FRDepartmentSelect is a Select widget that uses a list of french departments +including DOM TOM +>>> from django.contrib.localflavor.fr.forms import FRDepartmentSelect +>>> w = FRDepartmentSelect() +>>> print w.render('dep', 'Paris') +<select name="dep"> +<option value="01">01 - Ain</option> +<option value="02">02 - Aisne</option> +<option value="03">03 - Allier</option> +<option value="04">04 - Alpes-de-Haute-Provence</option> +<option value="05">05 - Hautes-Alpes</option> +<option value="06">06 - Alpes-Maritimes</option> +<option value="07">07 - Ardeche</option> +<option value="08">08 - Ardennes</option> +<option value="09">09 - Ariege</option> +<option value="10">10 - Aube</option> +<option value="11">11 - Aude</option> +<option value="12">12 - Aveyron</option> +<option value="13">13 - Bouches-du-Rhone</option> +<option value="14">14 - Calvados</option> +<option value="15">15 - Cantal</option> +<option value="16">16 - Charente</option> +<option value="17">17 - Charente-Maritime</option> +<option value="18">18 - Cher</option> +<option value="19">19 - Correze</option> +<option value="21">21 - Cote-d'Or</option> +<option value="22">22 - Cotes-d'Armor</option> +<option value="23">23 - Creuse</option> +<option value="24">24 - Dordogne</option> +<option value="25">25 - Doubs</option> +<option value="26">26 - Drome</option> +<option value="27">27 - Eure</option> +<option value="28">28 - Eure-et-Loire</option> +<option value="29">29 - Finistere</option> +<option value="2A">2A - Corse-du-Sud</option> +<option value="2B">2B - Haute-Corse</option> +<option value="30">30 - Gard</option> +<option value="31">31 - Haute-Garonne</option> +<option value="32">32 - Gers</option> +<option value="33">33 - Gironde</option> +<option value="34">34 - Herault</option> +<option value="35">35 - Ille-et-Vilaine</option> +<option value="36">36 - Indre</option> +<option value="37">37 - Indre-et-Loire</option> +<option value="38">38 - Isere</option> +<option value="39">39 - Jura</option> +<option value="40">40 - Landes</option> +<option value="41">41 - Loir-et-Cher</option> +<option value="42">42 - Loire</option> +<option value="43">43 - Haute-Loire</option> +<option value="44">44 - Loire-Atlantique</option> +<option value="45">45 - Loiret</option> +<option value="46">46 - Lot</option> +<option value="47">47 - Lot-et-Garonne</option> +<option value="48">48 - Lozere</option> +<option value="49">49 - Maine-et-Loire</option> +<option value="50">50 - Manche</option> +<option value="51">51 - Marne</option> +<option value="52">52 - Haute-Marne</option> +<option value="53">53 - Mayenne</option> +<option value="54">54 - Meurthe-et-Moselle</option> +<option value="55">55 - Meuse</option> +<option value="56">56 - Morbihan</option> +<option value="57">57 - Moselle</option> +<option value="58">58 - Nievre</option> +<option value="59">59 - Nord</option> +<option value="60">60 - Oise</option> +<option value="61">61 - Orne</option> +<option value="62">62 - Pas-de-Calais</option> +<option value="63">63 - Puy-de-Dome</option> +<option value="64">64 - Pyrenees-Atlantiques</option> +<option value="65">65 - Hautes-Pyrenees</option> +<option value="66">66 - Pyrenees-Orientales</option> +<option value="67">67 - Bas-Rhin</option> +<option value="68">68 - Haut-Rhin</option> +<option value="69">69 - Rhone</option> +<option value="70">70 - Haute-Saone</option> +<option value="71">71 - Saone-et-Loire</option> +<option value="72">72 - Sarthe</option> +<option value="73">73 - Savoie</option> +<option value="74">74 - Haute-Savoie</option> +<option value="75">75 - Paris</option> +<option value="76">76 - Seine-Maritime</option> +<option value="77">77 - Seine-et-Marne</option> +<option value="78">78 - Yvelines</option> +<option value="79">79 - Deux-Sevres</option> +<option value="80">80 - Somme</option> +<option value="81">81 - Tarn</option> +<option value="82">82 - Tarn-et-Garonne</option> +<option value="83">83 - Var</option> +<option value="84">84 - Vaucluse</option> +<option value="85">85 - Vendee</option> +<option value="86">86 - Vienne</option> +<option value="87">87 - Haute-Vienne</option> +<option value="88">88 - Vosges</option> +<option value="89">89 - Yonne</option> +<option value="90">90 - Territoire de Belfort</option> +<option value="91">91 - Essonne</option> +<option value="92">92 - Hauts-de-Seine</option> +<option value="93">93 - Seine-Saint-Denis</option> +<option value="94">94 - Val-de-Marne</option> +<option value="95">95 - Val-d'Oise</option> +<option value="2A">2A - Corse du sud</option> +<option value="2B">2B - Haute Corse</option> +<option value="971">971 - Guadeloupe</option> +<option value="972">972 - Martinique</option> +<option value="973">973 - Guyane</option> +<option value="974">974 - La Reunion</option> +<option value="975">975 - Saint-Pierre-et-Miquelon</option> +<option value="976">976 - Mayotte</option> +<option value="984">984 - Terres Australes et Antarctiques</option> +<option value="986">986 - Wallis et Futuna</option> +<option value="987">987 - Polynesie Francaise</option> +<option value="988">988 - Nouvelle-Caledonie</option> +</select> +""" diff --git a/tests/regressiontests/forms/localflavor/generic.py b/tests/regressiontests/forms/localflavor/generic.py new file mode 100644 index 0000000000..0dbe30d709 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/generic.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ generic form fields. + +tests = r""" +## Generic DateField ########################################################## + +>>> from django.contrib.localflavor.generic.forms import * + +A DateField that uses generic dd/mm/yy dates instead of mm/dd/yy where +appropriate. + +>>> import datetime +>>> f = DateField() +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) +datetime.date(2006, 10, 25) +>>> f.clean('2006-10-25') +datetime.date(2006, 10, 25) +>>> f.clean('25/10/2006') +datetime.date(2006, 10, 25) +>>> f.clean('25/10/06') +datetime.date(2006, 10, 25) +>>> f.clean('Oct 25 2006') +datetime.date(2006, 10, 25) +>>> f.clean('October 25 2006') +datetime.date(2006, 10, 25) +>>> f.clean('October 25, 2006') +datetime.date(2006, 10, 25) +>>> f.clean('25 October 2006') +datetime.date(2006, 10, 25) +>>> f.clean('25 October, 2006') +datetime.date(2006, 10, 25) +>>> f.clean('2006-4-31') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('200a-10-25') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('10/25/06') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = DateField(required=False) +>>> f.clean(None) +>>> repr(f.clean(None)) +'None' +>>> f.clean('') +>>> repr(f.clean('')) +'None' + +DateField accepts an optional input_formats parameter: +>>> f = DateField(input_formats=['%Y %m %d']) +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.date(2006, 10, 25) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.date(2006, 10, 25) +>>> f.clean('2006 10 25') +datetime.date(2006, 10, 25) + +The input_formats parameter overrides all default input formats, +so the default formats won't work unless you specify them: +>>> f.clean('2006-10-25') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('25/10/2006') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] +>>> f.clean('25/10/06') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date.'] + +## Generic DateTimeField ###################################################### + +A DateField that uses generic dd/mm/yy dates instead of mm/dd/yy where +appropriate. + +>>> import datetime +>>> f = DateTimeField() +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) +datetime.datetime(2006, 10, 25, 14, 30, 59) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) +datetime.datetime(2006, 10, 25, 14, 30, 59, 200) +>>> f.clean('2006-10-25 14:30:45') +datetime.datetime(2006, 10, 25, 14, 30, 45) +>>> f.clean('2006-10-25 14:30:00') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('2006-10-25 14:30') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('2006-10-25') +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean('25/10/2006 14:30:45') +datetime.datetime(2006, 10, 25, 14, 30, 45) +>>> f.clean('25/10/2006 14:30:00') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('25/10/2006 14:30') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('25/10/2006') +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean('25/10/06 14:30:45') +datetime.datetime(2006, 10, 25, 14, 30, 45) +>>> f.clean('25/10/06 14:30:00') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('25/10/06 14:30') +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean('25/10/06') +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date/time.'] +>>> f.clean('2006-10-25 4:30 p.m.') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date/time.'] + +DateField accepts an optional input_formats parameter: +>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p']) +>>> f.clean(datetime.date(2006, 10, 25)) +datetime.datetime(2006, 10, 25, 0, 0) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) +datetime.datetime(2006, 10, 25, 14, 30) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) +datetime.datetime(2006, 10, 25, 14, 30, 59) +>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) +datetime.datetime(2006, 10, 25, 14, 30, 59, 200) +>>> f.clean('2006 10 25 2:30 PM') +datetime.datetime(2006, 10, 25, 14, 30) + +The input_formats parameter overrides all default input formats, +so the default formats won't work unless you specify them: +>>> f.clean('2006-10-25 14:30:45') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid date/time.'] + +>>> f = DateTimeField(required=False) +>>> f.clean(None) +>>> repr(f.clean(None)) +'None' +>>> f.clean('') +>>> repr(f.clean('')) +'None' + +""" diff --git a/tests/regressiontests/forms/localflavor/is_.py b/tests/regressiontests/forms/localflavor/is_.py new file mode 100644 index 0000000000..6851441a79 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/is_.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ IS form fields. + +tests = r""" +## ISIdNumberField ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISIdNumberField() +>>> f.clean('2308803449') +u'230880-3449' +>>> f.clean('230880-3449') +u'230880-3449' +>>> f.clean('230880 3449') +u'230880-3449' +>>> f.clean('230880343') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 10 characters (it has 9).'] +>>> f.clean('230880343234') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 11 characters (it has 12).'] +>>> f.clean('abcdefghijk') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('2308803439') +Traceback (most recent call last): +... +ValidationError: [u'The Icelandic identification number is not valid.'] +>>> f.clean('2308803440') +u'230880-3440' +>>> f = ISIdNumberField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## ISPhoneNumberField ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISPhoneNumberField() +>>> f.clean('1234567') +u'1234567' +>>> f.clean('123 4567') +u'1234567' +>>> f.clean('123-4567') +u'1234567' +>>> f.clean('123-456') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean('123456') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 7 characters (it has 6).'] +>>> f.clean('123456555') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +>>> f.clean('abcdefg') +Traceback (most recent call last): +ValidationError: [u'Enter a valid value.'] +>>> f.clean(' 1234567 ') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 8 characters (it has 9).'] +>>> f.clean(' 12367 ') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] + +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f = ISPhoneNumberField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## ISPostalCodeSelect ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISPostalCodeSelect() + +>>> f.render('foo', 'bar') +u'<select name="foo">\n<option value="101">101 Reykjav\xedk</option>\n<option value="103">103 Reykjav\xedk</option>\n<option value="104">104 Reykjav\xedk</option>\n<option value="105">105 Reykjav\xedk</option>\n<option value="107">107 Reykjav\xedk</option>\n<option value="108">108 Reykjav\xedk</option>\n<option value="109">109 Reykjav\xedk</option>\n<option value="110">110 Reykjav\xedk</option>\n<option value="111">111 Reykjav\xedk</option>\n<option value="112">112 Reykjav\xedk</option>\n<option value="113">113 Reykjav\xedk</option>\n<option value="116">116 Kjalarnes</option>\n<option value="121">121 Reykjav\xedk</option>\n<option value="123">123 Reykjav\xedk</option>\n<option value="124">124 Reykjav\xedk</option>\n<option value="125">125 Reykjav\xedk</option>\n<option value="127">127 Reykjav\xedk</option>\n<option value="128">128 Reykjav\xedk</option>\n<option value="129">129 Reykjav\xedk</option>\n<option value="130">130 Reykjav\xedk</option>\n<option value="132">132 Reykjav\xedk</option>\n<option value="150">150 Reykjav\xedk</option>\n<option value="155">155 Reykjav\xedk</option>\n<option value="170">170 Seltjarnarnes</option>\n<option value="172">172 Seltjarnarnes</option>\n<option value="190">190 Vogar</option>\n<option value="200">200 K\xf3pavogur</option>\n<option value="201">201 K\xf3pavogur</option>\n<option value="202">202 K\xf3pavogur</option>\n<option value="203">203 K\xf3pavogur</option>\n<option value="210">210 Gar\xf0ab\xe6r</option>\n<option value="212">212 Gar\xf0ab\xe6r</option>\n<option value="220">220 Hafnarfj\xf6r\xf0ur</option>\n<option value="221">221 Hafnarfj\xf6r\xf0ur</option>\n<option value="222">222 Hafnarfj\xf6r\xf0ur</option>\n<option value="225">225 \xc1lftanes</option>\n<option value="230">230 Reykjanesb\xe6r</option>\n<option value="232">232 Reykjanesb\xe6r</option>\n<option value="233">233 Reykjanesb\xe6r</option>\n<option value="235">235 Keflav\xedkurflugv\xf6llur</option>\n<option value="240">240 Grindav\xedk</option>\n<option value="245">245 Sandger\xf0i</option>\n<option value="250">250 Gar\xf0ur</option>\n<option value="260">260 Reykjanesb\xe6r</option>\n<option value="270">270 Mosfellsb\xe6r</option>\n<option value="300">300 Akranes</option>\n<option value="301">301 Akranes</option>\n<option value="302">302 Akranes</option>\n<option value="310">310 Borgarnes</option>\n<option value="311">311 Borgarnes</option>\n<option value="320">320 Reykholt \xed Borgarfir\xf0i</option>\n<option value="340">340 Stykkish\xf3lmur</option>\n<option value="345">345 Flatey \xe1 Brei\xf0afir\xf0i</option>\n<option value="350">350 Grundarfj\xf6r\xf0ur</option>\n<option value="355">355 \xd3lafsv\xedk</option>\n<option value="356">356 Sn\xe6fellsb\xe6r</option>\n<option value="360">360 Hellissandur</option>\n<option value="370">370 B\xfa\xf0ardalur</option>\n<option value="371">371 B\xfa\xf0ardalur</option>\n<option value="380">380 Reykh\xf3lahreppur</option>\n<option value="400">400 \xcdsafj\xf6r\xf0ur</option>\n<option value="401">401 \xcdsafj\xf6r\xf0ur</option>\n<option value="410">410 Hn\xedfsdalur</option>\n<option value="415">415 Bolungarv\xedk</option>\n<option value="420">420 S\xfa\xf0av\xedk</option>\n<option value="425">425 Flateyri</option>\n<option value="430">430 Su\xf0ureyri</option>\n<option value="450">450 Patreksfj\xf6r\xf0ur</option>\n<option value="451">451 Patreksfj\xf6r\xf0ur</option>\n<option value="460">460 T\xe1lknafj\xf6r\xf0ur</option>\n<option value="465">465 B\xedldudalur</option>\n<option value="470">470 \xdeingeyri</option>\n<option value="471">471 \xdeingeyri</option>\n<option value="500">500 Sta\xf0ur</option>\n<option value="510">510 H\xf3lmav\xedk</option>\n<option value="512">512 H\xf3lmav\xedk</option>\n<option value="520">520 Drangsnes</option>\n<option value="522">522 Kj\xf6rvogur</option>\n<option value="523">523 B\xe6r</option>\n<option value="524">524 Nor\xf0urfj\xf6r\xf0ur</option>\n<option value="530">530 Hvammstangi</option>\n<option value="531">531 Hvammstangi</option>\n<option value="540">540 Bl\xf6ndu\xf3s</option>\n<option value="541">541 Bl\xf6ndu\xf3s</option>\n<option value="545">545 Skagastr\xf6nd</option>\n<option value="550">550 Sau\xf0\xe1rkr\xf3kur</option>\n<option value="551">551 Sau\xf0\xe1rkr\xf3kur</option>\n<option value="560">560 Varmahl\xed\xf0</option>\n<option value="565">565 Hofs\xf3s</option>\n<option value="566">566 Hofs\xf3s</option>\n<option value="570">570 Flj\xf3t</option>\n<option value="580">580 Siglufj\xf6r\xf0ur</option>\n<option value="600">600 Akureyri</option>\n<option value="601">601 Akureyri</option>\n<option value="602">602 Akureyri</option>\n<option value="603">603 Akureyri</option>\n<option value="610">610 Greniv\xedk</option>\n<option value="611">611 Gr\xedmsey</option>\n<option value="620">620 Dalv\xedk</option>\n<option value="621">621 Dalv\xedk</option>\n<option value="625">625 \xd3lafsfj\xf6r\xf0ur</option>\n<option value="630">630 Hr\xedsey</option>\n<option value="640">640 H\xfasav\xedk</option>\n<option value="641">641 H\xfasav\xedk</option>\n<option value="645">645 Fossh\xf3ll</option>\n<option value="650">650 Laugar</option>\n<option value="660">660 M\xfdvatn</option>\n<option value="670">670 K\xf3pasker</option>\n<option value="671">671 K\xf3pasker</option>\n<option value="675">675 Raufarh\xf6fn</option>\n<option value="680">680 \xde\xf3rsh\xf6fn</option>\n<option value="681">681 \xde\xf3rsh\xf6fn</option>\n<option value="685">685 Bakkafj\xf6r\xf0ur</option>\n<option value="690">690 Vopnafj\xf6r\xf0ur</option>\n<option value="700">700 Egilssta\xf0ir</option>\n<option value="701">701 Egilssta\xf0ir</option>\n<option value="710">710 Sey\xf0isfj\xf6r\xf0ur</option>\n<option value="715">715 Mj\xf3ifj\xf6r\xf0ur</option>\n<option value="720">720 Borgarfj\xf6r\xf0ur eystri</option>\n<option value="730">730 Rey\xf0arfj\xf6r\xf0ur</option>\n<option value="735">735 Eskifj\xf6r\xf0ur</option>\n<option value="740">740 Neskaupsta\xf0ur</option>\n<option value="750">750 F\xe1skr\xfa\xf0sfj\xf6r\xf0ur</option>\n<option value="755">755 St\xf6\xf0varfj\xf6r\xf0ur</option>\n<option value="760">760 Brei\xf0dalsv\xedk</option>\n<option value="765">765 Dj\xfapivogur</option>\n<option value="780">780 H\xf6fn \xed Hornafir\xf0i</option>\n<option value="781">781 H\xf6fn \xed Hornafir\xf0i</option>\n<option value="785">785 \xd6r\xe6fi</option>\n<option value="800">800 Selfoss</option>\n<option value="801">801 Selfoss</option>\n<option value="802">802 Selfoss</option>\n<option value="810">810 Hverager\xf0i</option>\n<option value="815">815 \xdeorl\xe1ksh\xf6fn</option>\n<option value="820">820 Eyrarbakki</option>\n<option value="825">825 Stokkseyri</option>\n<option value="840">840 Laugarvatn</option>\n<option value="845">845 Fl\xfa\xf0ir</option>\n<option value="850">850 Hella</option>\n<option value="851">851 Hella</option>\n<option value="860">860 Hvolsv\xf6llur</option>\n<option value="861">861 Hvolsv\xf6llur</option>\n<option value="870">870 V\xedk</option>\n<option value="871">871 V\xedk</option>\n<option value="880">880 Kirkjub\xe6jarklaustur</option>\n<option value="900">900 Vestmannaeyjar</option>\n<option value="902">902 Vestmannaeyjar</option>\n</select>' +""" + diff --git a/tests/regressiontests/forms/localflavor/it.py b/tests/regressiontests/forms/localflavor/it.py new file mode 100644 index 0000000000..10023a7cf6 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/it.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ IT form fields. + +tests = r""" +# ITZipCodeField ############################################################# + +>>> from django.contrib.localflavor.it.forms import ITZipCodeField +>>> f = ITZipCodeField() +>>> f.clean('00100') +u'00100' +>>> f.clean(' 00100') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid zip code.'] + +# ITRegionSelect ############################################################# + +>>> from django.contrib.localflavor.it.forms import ITRegionSelect +>>> w = ITRegionSelect() +>>> w.render('regions', 'PMN') +u'<select name="regions">\n<option value="ABR">Abruzzo</option>\n<option value="BAS">Basilicata</option>\n<option value="CAL">Calabria</option>\n<option value="CAM">Campania</option>\n<option value="EMR">Emilia-Romagna</option>\n<option value="FVG">Friuli-Venezia Giulia</option>\n<option value="LAZ">Lazio</option>\n<option value="LIG">Liguria</option>\n<option value="LOM">Lombardia</option>\n<option value="MAR">Marche</option>\n<option value="MOL">Molise</option>\n<option value="PMN" selected="selected">Piemonte</option>\n<option value="PUG">Puglia</option>\n<option value="SAR">Sardegna</option>\n<option value="SIC">Sicilia</option>\n<option value="TOS">Toscana</option>\n<option value="TAA">Trentino-Alto Adige</option>\n<option value="UMB">Umbria</option>\n<option value="VAO">Valle d\u2019Aosta</option>\n<option value="VEN">Veneto</option>\n</select>' + +# ITSocialSecurityNumberField ################################################# + +>>> from django.contrib.localflavor.it.forms import ITSocialSecurityNumberField +>>> f = ITSocialSecurityNumberField() +>>> f.clean('LVSGDU99T71H501L') +u'LVSGDU99T71H501L' +>>> f.clean('LBRRME11A01L736W') +u'LBRRME11A01L736W' +>>> f.clean('lbrrme11a01l736w') +u'LBRRME11A01L736W' +>>> f.clean('LBR RME 11A01 L736W') +u'LBRRME11A01L736W' +>>> f.clean('LBRRME11A01L736A') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Social Security number.'] +>>> f.clean('%BRRME11A01L736W') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Social Security number.'] + +# ITVatNumberField ########################################################### + +>>> from django.contrib.localflavor.it.forms import ITVatNumberField +>>> f = ITVatNumberField() +>>> f.clean('07973780013') +u'07973780013' +>>> f.clean('7973780013') +u'07973780013' +>>> f.clean(7973780013) +u'07973780013' +>>> f.clean('07973780014') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid VAT number.'] +>>> f.clean('A7973780013') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid VAT number.'] +""" diff --git a/tests/regressiontests/forms/localflavor/jp.py b/tests/regressiontests/forms/localflavor/jp.py new file mode 100644 index 0000000000..7b5b82667c --- /dev/null +++ b/tests/regressiontests/forms/localflavor/jp.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ JP form fields. + +tests = r""" +# JPPostalCodeField ############################################################### + +A form field that validates its input is a Japanese postcode. + +Accepts 7 digits(with/out hyphen). +>>> from django.contrib.localflavor.jp.forms import JPPostalCodeField +>>> f = JPPostalCodeField() +>>> f.clean('251-0032') +u'2510032' +>>> f.clean('2510032') +u'2510032' +>>> f.clean('2510-032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('251a0032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('a51-0032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('25100321') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = JPPostalCodeField(required=False) +>>> f.clean('251-0032') +u'2510032' +>>> f.clean('2510032') +u'2510032' +>>> f.clean('2510-032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('') +u'' +>>> f.clean(None) +u'' + +# JPPrefectureSelect ############################################################### + +A Select widget that uses a list of Japanese prefectures as its choices. +>>> from django.contrib.localflavor.jp.forms import JPPrefectureSelect +>>> w = JPPrefectureSelect() +>>> print w.render('prefecture', 'kanagawa') +<select name="prefecture"> +<option value="hokkaido">Hokkaido</option> +<option value="aomori">Aomori</option> +<option value="iwate">Iwate</option> +<option value="miyagi">Miyagi</option> +<option value="akita">Akita</option> +<option value="yamagata">Yamagata</option> +<option value="fukushima">Fukushima</option> +<option value="ibaraki">Ibaraki</option> +<option value="tochigi">Tochigi</option> +<option value="gunma">Gunma</option> +<option value="saitama">Saitama</option> +<option value="chiba">Chiba</option> +<option value="tokyo">Tokyo</option> +<option value="kanagawa" selected="selected">Kanagawa</option> +<option value="yamanashi">Yamanashi</option> +<option value="nagano">Nagano</option> +<option value="niigata">Niigata</option> +<option value="toyama">Toyama</option> +<option value="ishikawa">Ishikawa</option> +<option value="fukui">Fukui</option> +<option value="gifu">Gifu</option> +<option value="shizuoka">Shizuoka</option> +<option value="aichi">Aichi</option> +<option value="mie">Mie</option> +<option value="shiga">Shiga</option> +<option value="kyoto">Kyoto</option> +<option value="osaka">Osaka</option> +<option value="hyogo">Hyogo</option> +<option value="nara">Nara</option> +<option value="wakayama">Wakayama</option> +<option value="tottori">Tottori</option> +<option value="shimane">Shimane</option> +<option value="okayama">Okayama</option> +<option value="hiroshima">Hiroshima</option> +<option value="yamaguchi">Yamaguchi</option> +<option value="tokushima">Tokushima</option> +<option value="kagawa">Kagawa</option> +<option value="ehime">Ehime</option> +<option value="kochi">Kochi</option> +<option value="fukuoka">Fukuoka</option> +<option value="saga">Saga</option> +<option value="nagasaki">Nagasaki</option> +<option value="kumamoto">Kumamoto</option> +<option value="oita">Oita</option> +<option value="miyazaki">Miyazaki</option> +<option value="kagoshima">Kagoshima</option> +<option value="okinawa">Okinawa</option> +</select> +""" diff --git a/tests/regressiontests/forms/localflavor/nl.py b/tests/regressiontests/forms/localflavor/nl.py new file mode 100644 index 0000000000..38bbf72384 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/nl.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ NL form fields. + +tests = r""" +# NLPhoneNumberField ######################################################## + +>>> from django.contrib.localflavor.nl.forms import NLPhoneNumberField +>>> f = NLPhoneNumberField(required=False) +>>> f.clean('') +u'' +>>> f.clean('012-3456789') +'012-3456789' +>>> f.clean('0123456789') +'0123456789' +>>> f.clean('+31-12-3456789') +'+31-12-3456789' +>>> f.clean('(0123) 456789') +'(0123) 456789' +>>> f.clean('foo') +Traceback (most recent call last): + ... +ValidationError: [u'Enter a valid phone number'] + +# NLZipCodeField ############################################################ + +>>> from django.contrib.localflavor.nl.forms import NLZipCodeField +>>> f = NLZipCodeField(required=False) +>>> f.clean('') +u'' +>>> f.clean('1234ab') +u'1234 AB' +>>> f.clean('1234 ab') +u'1234 AB' +>>> f.clean('1234 AB') +u'1234 AB' +>>> f.clean('0123AB') +Traceback (most recent call last): + ... +ValidationError: [u'Enter a valid postal code'] +>>> f.clean('foo') +Traceback (most recent call last): + ... +ValidationError: [u'Enter a valid postal code'] + +# NLSoFiNumberField ######################################################### + +>>> from django.contrib.localflavor.nl.forms import NLSoFiNumberField +>>> f = NLSoFiNumberField(required=False) +>>> f.clean('') +u'' +>>> f.clean('123456782') +'123456782' +>>> f.clean('000000000') +Traceback (most recent call last): + ... +ValidationError: [u'Enter a valid SoFi number'] +>>> f.clean('123456789') +Traceback (most recent call last): + ... +ValidationError: [u'Enter a valid SoFi number'] +>>> f.clean('foo') +Traceback (most recent call last): + ... +ValidationError: [u'Enter a valid SoFi number'] + +# NLProvinceSelect ########################################################## + +>>> from django.contrib.localflavor.nl.forms import NLProvinceSelect +>>> s = NLProvinceSelect() +>>> s.render('provinces', 'OV') +u'<select name="provinces">\n<option value="DR">Drente</option>\n<option value="FL">Flevoland</option>\n<option value="FR">Friesland</option>\n<option value="GL">Gelderland</option>\n<option value="GR">Groningen</option>\n<option value="LB">Limburg</option>\n<option value="NB">Noord-Brabant</option>\n<option value="NH">Noord-Holland</option>\n<option value="OV" selected="selected">Overijssel</option>\n<option value="UT">Utrecht</option>\n<option value="ZE">Zeeland</option>\n<option value="ZH">Zuid-Holland</option>\n</select>' +""" diff --git a/tests/regressiontests/forms/localflavor/pl.py b/tests/regressiontests/forms/localflavor/pl.py new file mode 100644 index 0000000000..be8437208b --- /dev/null +++ b/tests/regressiontests/forms/localflavor/pl.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ PL form fields. + +tests = r""" +# PLVoivodeshipSelect ########################################################## + +>>> from django.contrib.localflavor.pl.forms import PLVoivodeshipSelect +>>> f = PLVoivodeshipSelect() +>>> f.render('voivodeships','pomerania') +u'<select name="voivodeships">\n<option value="lower_silesia">Lower Silesia</option>\n<option value="kuyavia-pomerania">Kuyavia-Pomerania</option>\n<option value="lublin">Lublin</option>\n<option value="lubusz">Lubusz</option>\n<option value="lodz">Lodz</option>\n<option value="lesser_poland">Lesser Poland</option>\n<option value="masovia">Masovia</option>\n<option value="opole">Opole</option>\n<option value="subcarpatia">Subcarpatia</option>\n<option value="podlasie">Podlasie</option>\n<option value="pomerania" selected="selected">Pomerania</option>\n<option value="silesia">Silesia</option>\n<option value="swietokrzyskie">Swietokrzyskie</option>\n<option value="warmia-masuria">Warmia-Masuria</option>\n<option value="greater_poland">Greater Poland</option>\n<option value="west_pomerania">West Pomerania</option>\n</select>' + +# PLAdministrativeUnitSelect ########################################################## + +>>> from django.contrib.localflavor.pl.forms import PLAdministrativeUnitSelect +>>> f = PLAdministrativeUnitSelect() +>>> f.render('administrativeunit','katowice') +u'<select name="administrativeunit">\n<option value="wroclaw">Wroc\u0142aw</option>\n<option value="jeleniagora">Jelenia G\xf3ra</option>\n<option value="legnica">Legnica</option>\n<option value="boleslawiecki">boles\u0142awiecki</option>\n<option value="dzierzoniowski">dzier\u017coniowski</option>\n<option value="glogowski">g\u0142ogowski</option>\n<option value="gorowski">g\xf3rowski</option>\n<option value="jaworski">jaworski</option>\n<option value="jeleniogorski">jeleniog\xf3rski</option>\n<option value="kamiennogorski">kamiennog\xf3rski</option>\n<option value="klodzki">k\u0142odzki</option>\n<option value="legnicki">legnicki</option>\n<option value="lubanski">luba\u0144ski</option>\n<option value="lubinski">lubi\u0144ski</option>\n<option value="lwowecki">lw\xf3wecki</option>\n<option value="milicki">milicki</option>\n<option value="olesnicki">ole\u015bnicki</option>\n<option value="olawski">o\u0142awski</option>\n<option value="polkowicki">polkowicki</option>\n<option value="strzelinski">strzeli\u0144ski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="trzebnicki">trzebnicki</option>\n<option value="walbrzyski">wa\u0142brzyski</option>\n<option value="wolowski">wo\u0142owski</option>\n<option value="wroclawski">wroc\u0142awski</option>\n<option value="zabkowicki">z\u0105bkowicki</option>\n<option value="zgorzelecki">zgorzelecki</option>\n<option value="zlotoryjski">z\u0142otoryjski</option>\n<option value="bydgoszcz">Bydgoszcz</option>\n<option value="torun">Toru\u0144</option>\n<option value="wloclawek">W\u0142oc\u0142awek</option>\n<option value="grudziadz">Grudzi\u0105dz</option>\n<option value="aleksandrowski">aleksandrowski</option>\n<option value="brodnicki">brodnicki</option>\n<option value="bydgoski">bydgoski</option>\n<option value="chelminski">che\u0142mi\u0144ski</option>\n<option value="golubsko-dobrzynski">golubsko-dobrzy\u0144ski</option>\n<option value="grudziadzki">grudzi\u0105dzki</option>\n<option value="inowroclawski">inowroc\u0142awski</option>\n<option value="lipnowski">lipnowski</option>\n<option value="mogilenski">mogile\u0144ski</option>\n<option value="nakielski">nakielski</option>\n<option value="radziejowski">radziejowski</option>\n<option value="rypinski">rypi\u0144ski</option>\n<option value="sepolenski">s\u0119pole\u0144ski</option>\n<option value="swiecki">\u015bwiecki</option>\n<option value="torunski">toru\u0144ski</option>\n<option value="tucholski">tucholski</option>\n<option value="wabrzeski">w\u0105brzeski</option>\n<option value="wloclawski">wroc\u0142awski</option>\n<option value="zninski">\u017ani\u0144ski</option>\n<option value="lublin">Lublin</option>\n<option value="biala-podlaska">Bia\u0142a Podlaska</option>\n<option value="chelm">Che\u0142m</option>\n<option value="zamosc">Zamo\u015b\u0107</option>\n<option value="bialski">bialski</option>\n<option value="bilgorajski">bi\u0142gorajski</option>\n<option value="chelmski">che\u0142mski</option>\n<option value="hrubieszowski">hrubieszowski</option>\n<option value="janowski">janowski</option>\n<option value="krasnostawski">krasnostawski</option>\n<option value="krasnicki">kra\u015bnicki</option>\n<option value="lubartowski">lubartowski</option>\n<option value="lubelski">lubelski</option>\n<option value="leczynski">\u0142\u0119czy\u0144ski</option>\n<option value="lukowski">\u0142ukowski</option>\n<option value="opolski">opolski</option>\n<option value="parczewski">parczewski</option>\n<option value="pulawski">pu\u0142awski</option>\n<option value="radzynski">radzy\u0144ski</option>\n<option value="rycki">rycki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wlodawski">w\u0142odawski</option>\n<option value="zamojski">zamojski</option>\n<option value="gorzow-wielkopolski">Gorz\xf3w Wielkopolski</option>\n<option value="zielona-gora">Zielona G\xf3ra</option>\n<option value="gorzowski">gorzowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="miedzyrzecki">mi\u0119dzyrzecki</option>\n<option value="nowosolski">nowosolski</option>\n<option value="slubicki">s\u0142ubicki</option>\n<option value="strzelecko-drezdenecki">strzelecko-drezdenecki</option>\n<option value="sulecinski">sule\u0144ci\u0144ski</option>\n<option value="swiebodzinski">\u015bwiebodzi\u0144ski</option>\n<option value="wschowski">wschowski</option>\n<option value="zielonogorski">zielonog\xf3rski</option>\n<option value="zaganski">\u017caga\u0144ski</option>\n<option value="zarski">\u017carski</option>\n<option value="lodz">\u0141\xf3d\u017a</option>\n<option value="piotrkow-trybunalski">Piotrk\xf3w Trybunalski</option>\n<option value="skierniewice">Skierniewice</option>\n<option value="belchatowski">be\u0142chatowski</option>\n<option value="brzezinski">brzezi\u0144ski</option>\n<option value="kutnowski">kutnowski</option>\n<option value="laski">\u0142aski</option>\n<option value="leczycki">\u0142\u0119czycki</option>\n<option value="lowicki">\u0142owicki</option>\n<option value="lodzki wschodni">\u0142\xf3dzki wschodni</option>\n<option value="opoczynski">opoczy\u0144ski</option>\n<option value="pabianicki">pabianicki</option>\n<option value="pajeczanski">paj\u0119cza\u0144ski</option>\n<option value="piotrkowski">piotrkowski</option>\n<option value="poddebicki">podd\u0119bicki</option>\n<option value="radomszczanski">radomszcza\u0144ski</option>\n<option value="rawski">rawski</option>\n<option value="sieradzki">sieradzki</option>\n<option value="skierniewicki">skierniewicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wielunski">wielu\u0144ski</option>\n<option value="wieruszowski">wieruszowski</option>\n<option value="zdunskowolski">zdu\u0144skowolski</option>\n<option value="zgierski">zgierski</option>\n<option value="krakow">Krak\xf3w</option>\n<option value="tarnow">Tarn\xf3w</option>\n<option value="nowy-sacz">Nowy S\u0105cz</option>\n<option value="bochenski">boche\u0144ski</option>\n<option value="brzeski">brzeski</option>\n<option value="chrzanowski">chrzanowski</option>\n<option value="dabrowski">d\u0105browski</option>\n<option value="gorlicki">gorlicki</option>\n<option value="krakowski">krakowski</option>\n<option value="limanowski">limanowski</option>\n<option value="miechowski">miechowski</option>\n<option value="myslenicki">my\u015blenicki</option>\n<option value="nowosadecki">nowos\u0105decki</option>\n<option value="nowotarski">nowotarski</option>\n<option value="olkuski">olkuski</option>\n<option value="oswiecimski">o\u015bwi\u0119cimski</option>\n<option value="proszowicki">proszowicki</option>\n<option value="suski">suski</option>\n<option value="tarnowski">tarnowski</option>\n<option value="tatrzanski">tatrza\u0144ski</option>\n<option value="wadowicki">wadowicki</option>\n<option value="wielicki">wielicki</option>\n<option value="warszawa">Warszawa</option>\n<option value="ostroleka">Ostro\u0142\u0119ka</option>\n<option value="plock">P\u0142ock</option>\n<option value="radom">Radom</option>\n<option value="siedlce">Siedlce</option>\n<option value="bialobrzeski">bia\u0142obrzeski</option>\n<option value="ciechanowski">ciechanowski</option>\n<option value="garwolinski">garwoli\u0144ski</option>\n<option value="gostyninski">gostyni\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="grojecki">gr\xf3jecki</option>\n<option value="kozienicki">kozenicki</option>\n<option value="legionowski">legionowski</option>\n<option value="lipski">lipski</option>\n<option value="losicki">\u0142osicki</option>\n<option value="makowski">makowski</option>\n<option value="minski">mi\u0144ski</option>\n<option value="mlawski">m\u0142awski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="ostrolecki">ostro\u0142\u0119cki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="otwocki">otwocki</option>\n<option value="piaseczynski">piaseczy\u0144ski</option>\n<option value="plocki">p\u0142ocki</option>\n<option value="plonski">p\u0142o\u0144ski</option>\n<option value="pruszkowski">pruszkowski</option>\n<option value="przasnyski">przasnyski</option>\n<option value="przysuski">przysuski</option>\n<option value="pultuski">pu\u0142tuski</option>\n<option value="radomski">radomski</option>\n<option value="siedlecki">siedlecki</option>\n<option value="sierpecki">sierpecki</option>\n<option value="sochaczewski">sochaczewski</option>\n<option value="sokolowski">soko\u0142owski</option>\n<option value="szydlowiecki">szyd\u0142owiecki</option>\n<option value="warszawski-zachodni">warszawski zachodni</option>\n<option value="wegrowski">w\u0119growski</option>\n<option value="wolominski">wo\u0142omi\u0144ski</option>\n<option value="wyszkowski">wyszkowski</option>\n<option value="zwolenski">zwole\u0144ski</option>\n<option value="zurominski">\u017curomi\u0144ski</option>\n<option value="zyrardowski">\u017cyrardowski</option>\n<option value="opole">Opole</option>\n<option value="brzeski">brzeski</option>\n<option value="glubczycki">g\u0142ubczyski</option>\n<option value="kedzierzynsko-kozielski">k\u0119dzierzy\u0144ski-kozielski</option>\n<option value="kluczborski">kluczborski</option>\n<option value="krapkowicki">krapkowicki</option>\n<option value="namyslowski">namys\u0142owski</option>\n<option value="nyski">nyski</option>\n<option value="oleski">oleski</option>\n<option value="opolski">opolski</option>\n<option value="prudnicki">prudnicki</option>\n<option value="strzelecki">strzelecki</option>\n<option value="rzeszow">Rzesz\xf3w</option>\n<option value="krosno">Krosno</option>\n<option value="przemysl">Przemy\u015bl</option>\n<option value="tarnobrzeg">Tarnobrzeg</option>\n<option value="bieszczadzki">bieszczadzki</option>\n<option value="brzozowski">brzozowski</option>\n<option value="debicki">d\u0119bicki</option>\n<option value="jaroslawski">jaros\u0142awski</option>\n<option value="jasielski">jasielski</option>\n<option value="kolbuszowski">kolbuszowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="leski">leski</option>\n<option value="lezajski">le\u017cajski</option>\n<option value="lubaczowski">lubaczowski</option>\n<option value="lancucki">\u0142a\u0144cucki</option>\n<option value="mielecki">mielecki</option>\n<option value="nizanski">ni\u017ca\u0144ski</option>\n<option value="przemyski">przemyski</option>\n<option value="przeworski">przeworski</option>\n<option value="ropczycko-sedziszowski">ropczycko-s\u0119dziszowski</option>\n<option value="rzeszowski">rzeszowski</option>\n<option value="sanocki">sanocki</option>\n<option value="stalowowolski">stalowowolski</option>\n<option value="strzyzowski">strzy\u017cowski</option>\n<option value="tarnobrzeski">tarnobrzeski</option>\n<option value="bialystok">Bia\u0142ystok</option>\n<option value="lomza">\u0141om\u017ca</option>\n<option value="suwalki">Suwa\u0142ki</option>\n<option value="augustowski">augustowski</option>\n<option value="bialostocki">bia\u0142ostocki</option>\n<option value="bielski">bielski</option>\n<option value="grajewski">grajewski</option>\n<option value="hajnowski">hajnowski</option>\n<option value="kolnenski">kolne\u0144ski</option>\n<option value="\u0142omzynski">\u0142om\u017cy\u0144ski</option>\n<option value="moniecki">moniecki</option>\n<option value="sejnenski">sejne\u0144ski</option>\n<option value="siemiatycki">siematycki</option>\n<option value="sokolski">sok\xf3lski</option>\n<option value="suwalski">suwalski</option>\n<option value="wysokomazowiecki">wysokomazowiecki</option>\n<option value="zambrowski">zambrowski</option>\n<option value="gdansk">Gda\u0144sk</option>\n<option value="gdynia">Gdynia</option>\n<option value="slupsk">S\u0142upsk</option>\n<option value="sopot">Sopot</option>\n<option value="bytowski">bytowski</option>\n<option value="chojnicki">chojnicki</option>\n<option value="czluchowski">cz\u0142uchowski</option>\n<option value="kartuski">kartuski</option>\n<option value="koscierski">ko\u015bcierski</option>\n<option value="kwidzynski">kwidzy\u0144ski</option>\n<option value="leborski">l\u0119borski</option>\n<option value="malborski">malborski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="gdanski">gda\u0144ski</option>\n<option value="pucki">pucki</option>\n<option value="slupski">s\u0142upski</option>\n<option value="starogardzki">starogardzki</option>\n<option value="sztumski">sztumski</option>\n<option value="tczewski">tczewski</option>\n<option value="wejherowski">wejcherowski</option>\n<option value="katowice" selected="selected">Katowice</option>\n<option value="bielsko-biala">Bielsko-Bia\u0142a</option>\n<option value="bytom">Bytom</option>\n<option value="chorzow">Chorz\xf3w</option>\n<option value="czestochowa">Cz\u0119stochowa</option>\n<option value="dabrowa-gornicza">D\u0105browa G\xf3rnicza</option>\n<option value="gliwice">Gliwice</option>\n<option value="jastrzebie-zdroj">Jastrz\u0119bie Zdr\xf3j</option>\n<option value="jaworzno">Jaworzno</option>\n<option value="myslowice">Mys\u0142owice</option>\n<option value="piekary-slaskie">Piekary \u015al\u0105skie</option>\n<option value="ruda-slaska">Ruda \u015al\u0105ska</option>\n<option value="rybnik">Rybnik</option>\n<option value="siemianowice-slaskie">Siemianowice \u015al\u0105skie</option>\n<option value="sosnowiec">Sosnowiec</option>\n<option value="swietochlowice">\u015awi\u0119toch\u0142owice</option>\n<option value="tychy">Tychy</option>\n<option value="zabrze">Zabrze</option>\n<option value="zory">\u017bory</option>\n<option value="bedzinski">b\u0119dzi\u0144ski</option>\n<option value="bielski">bielski</option>\n<option value="bierunsko-ledzinski">bieru\u0144sko-l\u0119dzi\u0144ski</option>\n<option value="cieszynski">cieszy\u0144ski</option>\n<option value="czestochowski">cz\u0119stochowski</option>\n<option value="gliwicki">gliwicki</option>\n<option value="klobucki">k\u0142obucki</option>\n<option value="lubliniecki">lubliniecki</option>\n<option value="mikolowski">miko\u0142owski</option>\n<option value="myszkowski">myszkowski</option>\n<option value="pszczynski">pszczy\u0144ski</option>\n<option value="raciborski">raciborski</option>\n<option value="rybnicki">rybnicki</option>\n<option value="tarnogorski">tarnog\xf3rski</option>\n<option value="wodzislawski">wodzis\u0142awski</option>\n<option value="zawiercianski">zawiercia\u0144ski</option>\n<option value="zywiecki">\u017cywiecki</option>\n<option value="kielce">Kielce</option>\n<option value="buski">buski</option>\n<option value="jedrzejowski">j\u0119drzejowski</option>\n<option value="kazimierski">kazimierski</option>\n<option value="kielecki">kielecki</option>\n<option value="konecki">konecki</option>\n<option value="opatowski">opatowski</option>\n<option value="ostrowiecki">ostrowiecki</option>\n<option value="pinczowski">pi\u0144czowski</option>\n<option value="sandomierski">sandomierski</option>\n<option value="skarzyski">skar\u017cyski</option>\n<option value="starachowicki">starachowicki</option>\n<option value="staszowski">staszowski</option>\n<option value="wloszczowski">w\u0142oszczowski</option>\n<option value="olsztyn">Olsztyn</option>\n<option value="elblag">Elbl\u0105g</option>\n<option value="bartoszycki">bartoszycki</option>\n<option value="braniewski">braniewski</option>\n<option value="dzialdowski">dzia\u0142dowski</option>\n<option value="elblaski">elbl\u0105ski</option>\n<option value="elcki">e\u0142cki</option>\n<option value="gizycki">gi\u017cycki</option>\n<option value="goldapski">go\u0142dapski</option>\n<option value="ilawski">i\u0142awski</option>\n<option value="ketrzynski">k\u0119trzy\u0144ski</option>\n<option value="lidzbarski">lidzbarski</option>\n<option value="mragowski">mr\u0105gowski</option>\n<option value="nidzicki">nidzicki</option>\n<option value="nowomiejski">nowomiejski</option>\n<option value="olecki">olecki</option>\n<option value="olsztynski">olszty\u0144ski</option>\n<option value="ostrodzki">ostr\xf3dzki</option>\n<option value="piski">piski</option>\n<option value="szczycienski">szczycie\u0144ski</option>\n<option value="wegorzewski">w\u0119gorzewski</option>\n<option value="poznan">Pozna\u0144</option>\n<option value="kalisz">Kalisz</option>\n<option value="konin">Konin</option>\n<option value="leszno">Leszno</option>\n<option value="chodzieski">chodziejski</option>\n<option value="czarnkowsko-trzcianecki">czarnkowsko-trzcianecki</option>\n<option value="gnieznienski">gnie\u017anie\u0144ski</option>\n<option value="gostynski">gosty\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="jarocinski">jaroci\u0144ski</option>\n<option value="kaliski">kaliski</option>\n<option value="kepinski">k\u0119pi\u0144ski</option>\n<option value="kolski">kolski</option>\n<option value="koninski">koni\u0144ski</option>\n<option value="koscianski">ko\u015bcia\u0144ski</option>\n<option value="krotoszynski">krotoszy\u0144ski</option>\n<option value="leszczynski">leszczy\u0144ski</option>\n<option value="miedzychodzki">mi\u0119dzychodzki</option>\n<option value="nowotomyski">nowotomyski</option>\n<option value="obornicki">obornicki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="ostrzeszowski">ostrzeszowski</option>\n<option value="pilski">pilski</option>\n<option value="pleszewski">pleszewski</option>\n<option value="poznanski">pozna\u0144ski</option>\n<option value="rawicki">rawicki</option>\n<option value="slupecki">s\u0142upecki</option>\n<option value="szamotulski">szamotulski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="sremski">\u015bremski</option>\n<option value="turecki">turecki</option>\n<option value="wagrowiecki">w\u0105growiecki</option>\n<option value="wolsztynski">wolszty\u0144ski</option>\n<option value="wrzesinski">wrzesi\u0144ski</option>\n<option value="zlotowski">z\u0142otowski</option>\n<option value="bialogardzki">bia\u0142ogardzki</option>\n<option value="choszczenski">choszcze\u0144ski</option>\n<option value="drawski">drawski</option>\n<option value="goleniowski">goleniowski</option>\n<option value="gryficki">gryficki</option>\n<option value="gryfinski">gryfi\u0144ski</option>\n<option value="kamienski">kamie\u0144ski</option>\n<option value="kolobrzeski">ko\u0142obrzeski</option>\n<option value="koszalinski">koszali\u0144ski</option>\n<option value="lobeski">\u0142obeski</option>\n<option value="mysliborski">my\u015bliborski</option>\n<option value="policki">policki</option>\n<option value="pyrzycki">pyrzycki</option>\n<option value="slawienski">s\u0142awie\u0144ski</option>\n<option value="stargardzki">stargardzki</option>\n<option value="szczecinecki">szczecinecki</option>\n<option value="swidwinski">\u015bwidwi\u0144ski</option>\n<option value="walecki">wa\u0142ecki</option>\n</select>' + +# PLPostalCodeField ############################################################## + +>>> from django.contrib.localflavor.pl.forms import PLPostalCodeField +>>> f = PLPostalCodeField() +>>> f.clean('43--434') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XX-XXX.'] +>>> f.clean('41-403') +u'41-403' + +# PLTaxNumberField ############################################################### + +>>> from django.contrib.localflavor.pl.forms import PLTaxNumberField +>>> f = PLTaxNumberField() +>>> f.clean('43-343-234-323') +Traceback (most recent call last): +... +ValidationError: [u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'] +>>> f.clean('43-34-234-323') +u'43-34-234-323' +>>> f.clean('433-344-24-23') +u'433-344-24-23' + +# PLNationalIdentificationNumberField ############################################ + +>>> from django.contrib.localflavor.pl.forms import PLNationalIdentificationNumberField +>>> f = PLNationalIdentificationNumberField() +>>> f.clean('80071610614') +u'80071610614' +>>> f.clean('80071610610') +Traceback (most recent call last): +... +ValidationError: [u'Wrong checksum for the National Identification Number.'] +>>> f.clean('80') +Traceback (most recent call last): +... +ValidationError: [u'National Identification Number consists of 11 digits.'] +>>> f.clean('800716106AA') +Traceback (most recent call last): +... +ValidationError: [u'National Identification Number consists of 11 digits.'] +""" diff --git a/tests/regressiontests/forms/localflavor/sk.py b/tests/regressiontests/forms/localflavor/sk.py new file mode 100644 index 0000000000..069a99b2d2 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/sk.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ SK form fields. + +tests = r""" +# SKPostalCodeField ######################################################### + +>>> from django.contrib.localflavor.sk.forms import SKPostalCodeField +>>> f = SKPostalCodeField() +>>> f.clean('84545x') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.'] +>>> f.clean('91909') +u'91909' +>>> f.clean('917 01') +u'91701' + +# SKRegionSelect ############################################################ + +>>> from django.contrib.localflavor.sk.forms import SKRegionSelect +>>> w = SKRegionSelect() +>>> w.render('regions', 'TT') +u'<select name="regions">\n<option value="BB">Banska Bystrica region</option>\n<option value="BA">Bratislava region</option>\n<option value="KE">Kosice region</option>\n<option value="NR">Nitra region</option>\n<option value="PO">Presov region</option>\n<option value="TN">Trencin region</option>\n<option value="TT" selected="selected">Trnava region</option>\n<option value="ZA">Zilina region</option>\n</select>' + +# SKDistrictSelect ########################################################## + +>>> from django.contrib.localflavor.sk.forms import SKDistrictSelect +>>> w = SKDistrictSelect() +>>> w.render('Districts', 'RK') +u'<select name="Districts">\n<option value="BB">Banska Bystrica</option>\n<option value="BS">Banska Stiavnica</option>\n<option value="BJ">Bardejov</option>\n<option value="BN">Banovce nad Bebravou</option>\n<option value="BR">Brezno</option>\n<option value="BA1">Bratislava I</option>\n<option value="BA2">Bratislava II</option>\n<option value="BA3">Bratislava III</option>\n<option value="BA4">Bratislava IV</option>\n<option value="BA5">Bratislava V</option>\n<option value="BY">Bytca</option>\n<option value="CA">Cadca</option>\n<option value="DT">Detva</option>\n<option value="DK">Dolny Kubin</option>\n<option value="DS">Dunajska Streda</option>\n<option value="GA">Galanta</option>\n<option value="GL">Gelnica</option>\n<option value="HC">Hlohovec</option>\n<option value="HE">Humenne</option>\n<option value="IL">Ilava</option>\n<option value="KK">Kezmarok</option>\n<option value="KN">Komarno</option>\n<option value="KE1">Kosice I</option>\n<option value="KE2">Kosice II</option>\n<option value="KE3">Kosice III</option>\n<option value="KE4">Kosice IV</option>\n<option value="KEO">Kosice - okolie</option>\n<option value="KA">Krupina</option>\n<option value="KM">Kysucke Nove Mesto</option>\n<option value="LV">Levice</option>\n<option value="LE">Levoca</option>\n<option value="LM">Liptovsky Mikulas</option>\n<option value="LC">Lucenec</option>\n<option value="MA">Malacky</option>\n<option value="MT">Martin</option>\n<option value="ML">Medzilaborce</option>\n<option value="MI">Michalovce</option>\n<option value="MY">Myjava</option>\n<option value="NO">Namestovo</option>\n<option value="NR">Nitra</option>\n<option value="NM">Nove Mesto nad Vahom</option>\n<option value="NZ">Nove Zamky</option>\n<option value="PE">Partizanske</option>\n<option value="PK">Pezinok</option>\n<option value="PN">Piestany</option>\n<option value="PT">Poltar</option>\n<option value="PP">Poprad</option>\n<option value="PB">Povazska Bystrica</option>\n<option value="PO">Presov</option>\n<option value="PD">Prievidza</option>\n<option value="PU">Puchov</option>\n<option value="RA">Revuca</option>\n<option value="RS">Rimavska Sobota</option>\n<option value="RV">Roznava</option>\n<option value="RK" selected="selected">Ruzomberok</option>\n<option value="SB">Sabinov</option>\n<option value="SC">Senec</option>\n<option value="SE">Senica</option>\n<option value="SI">Skalica</option>\n<option value="SV">Snina</option>\n<option value="SO">Sobrance</option>\n<option value="SN">Spisska Nova Ves</option>\n<option value="SL">Stara Lubovna</option>\n<option value="SP">Stropkov</option>\n<option value="SK">Svidnik</option>\n<option value="SA">Sala</option>\n<option value="TO">Topolcany</option>\n<option value="TV">Trebisov</option>\n<option value="TN">Trencin</option>\n<option value="TT">Trnava</option>\n<option value="TR">Turcianske Teplice</option>\n<option value="TS">Tvrdosin</option>\n<option value="VK">Velky Krtis</option>\n<option value="VT">Vranov nad Toplou</option>\n<option value="ZM">Zlate Moravce</option>\n<option value="ZV">Zvolen</option>\n<option value="ZC">Zarnovica</option>\n<option value="ZH">Ziar nad Hronom</option>\n<option value="ZA">Zilina</option>\n</select>' +""" diff --git a/tests/regressiontests/forms/localflavor/uk.py b/tests/regressiontests/forms/localflavor/uk.py new file mode 100644 index 0000000000..d7848f70a8 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/uk.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ UK form fields. + +tests = r""" +# UKPostcodeField ############################################################# + +UKPostcodeField validates that the data is a valid UK postcode. +>>> from django.contrib.localflavor.uk.forms import UKPostcodeField +>>> f = UKPostcodeField() +>>> f.clean('BT32 4PX') +u'BT32 4PX' +>>> f.clean('GIR 0AA') +u'GIR 0AA' +>>> f.clean('BT324PX') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean('1NV 4L1D') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = UKPostcodeField(required=False) +>>> f.clean('BT32 4PX') +u'BT32 4PX' +>>> f.clean('GIR 0AA') +u'GIR 0AA' +>>> f.clean('1NV 4L1D') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean('BT324PX') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +""" diff --git a/tests/regressiontests/forms/localflavor/us.py b/tests/regressiontests/forms/localflavor/us.py new file mode 100644 index 0000000000..dfbfef681d --- /dev/null +++ b/tests/regressiontests/forms/localflavor/us.py @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- +# Tests for the contrib/localflavor/ US form fields. + +tests = r""" +# USZipCodeField ############################################################## + +USZipCodeField validates that the data is either a five-digit U.S. zip code or +a zip+4. +>>> from django.contrib.localflavor.us.forms import USZipCodeField +>>> f = USZipCodeField() +>>> f.clean('60606') +u'60606' +>>> f.clean(60606) +u'60606' +>>> f.clean('04000') +u'04000' +>>> f.clean('4000') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-1234') +u'60606-1234' +>>> f.clean('6060-1234') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = USZipCodeField(required=False) +>>> f.clean('60606') +u'60606' +>>> f.clean(60606) +u'60606' +>>> f.clean('04000') +u'04000' +>>> f.clean('4000') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-1234') +u'60606-1234' +>>> f.clean('6060-1234') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# USPhoneNumberField ########################################################## + +USPhoneNumberField validates that the data is a valid U.S. phone number, +including the area code. It's normalized to XXX-XXX-XXXX format. +>>> from django.contrib.localflavor.us.forms import USPhoneNumberField +>>> f = USPhoneNumberField() +>>> f.clean('312-555-1212') +u'312-555-1212' +>>> f.clean('3125551212') +u'312-555-1212' +>>> f.clean('312 555-1212') +u'312-555-1212' +>>> f.clean('(312) 555-1212') +u'312-555-1212' +>>> f.clean('312 555 1212') +u'312-555-1212' +>>> f.clean('312.555.1212') +u'312-555-1212' +>>> f.clean('312.555-1212') +u'312-555-1212' +>>> f.clean(' (312) 555.1212 ') +u'312-555-1212' +>>> f.clean('555-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean('312-55-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = USPhoneNumberField(required=False) +>>> f.clean('312-555-1212') +u'312-555-1212' +>>> f.clean('3125551212') +u'312-555-1212' +>>> f.clean('312 555-1212') +u'312-555-1212' +>>> f.clean('(312) 555-1212') +u'312-555-1212' +>>> f.clean('312 555 1212') +u'312-555-1212' +>>> f.clean('312.555.1212') +u'312-555-1212' +>>> f.clean('312.555-1212') +u'312-555-1212' +>>> f.clean(' (312) 555.1212 ') +u'312-555-1212' +>>> f.clean('555-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean('312-55-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# USStateField ################################################################ + +USStateField validates that the data is either an abbreviation or name of a +U.S. state. +>>> from django.contrib.localflavor.us.forms import USStateField +>>> f = USStateField() +>>> f.clean('il') +u'IL' +>>> f.clean('IL') +u'IL' +>>> f.clean('illinois') +u'IL' +>>> f.clean(' illinois ') +u'IL' +>>> f.clean(60606) +Traceback (most recent call last): +... +ValidationError: [u'Enter a U.S. state or territory.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = USStateField(required=False) +>>> f.clean('il') +u'IL' +>>> f.clean('IL') +u'IL' +>>> f.clean('illinois') +u'IL' +>>> f.clean(' illinois ') +u'IL' +>>> f.clean(60606) +Traceback (most recent call last): +... +ValidationError: [u'Enter a U.S. state or territory.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# USStateSelect ############################################################### + +USStateSelect is a Select widget that uses a list of U.S. states/territories +as its choices. +>>> from django.contrib.localflavor.us.forms import USStateSelect +>>> w = USStateSelect() +>>> print w.render('state', 'IL') +<select name="state"> +<option value="AL">Alabama</option> +<option value="AK">Alaska</option> +<option value="AS">American Samoa</option> +<option value="AZ">Arizona</option> +<option value="AR">Arkansas</option> +<option value="CA">California</option> +<option value="CO">Colorado</option> +<option value="CT">Connecticut</option> +<option value="DE">Delaware</option> +<option value="DC">District of Columbia</option> +<option value="FM">Federated States of Micronesia</option> +<option value="FL">Florida</option> +<option value="GA">Georgia</option> +<option value="GU">Guam</option> +<option value="HI">Hawaii</option> +<option value="ID">Idaho</option> +<option value="IL" selected="selected">Illinois</option> +<option value="IN">Indiana</option> +<option value="IA">Iowa</option> +<option value="KS">Kansas</option> +<option value="KY">Kentucky</option> +<option value="LA">Louisiana</option> +<option value="ME">Maine</option> +<option value="MH">Marshall Islands</option> +<option value="MD">Maryland</option> +<option value="MA">Massachusetts</option> +<option value="MI">Michigan</option> +<option value="MN">Minnesota</option> +<option value="MS">Mississippi</option> +<option value="MO">Missouri</option> +<option value="MT">Montana</option> +<option value="NE">Nebraska</option> +<option value="NV">Nevada</option> +<option value="NH">New Hampshire</option> +<option value="NJ">New Jersey</option> +<option value="NM">New Mexico</option> +<option value="NY">New York</option> +<option value="NC">North Carolina</option> +<option value="ND">North Dakota</option> +<option value="MP">Northern Mariana Islands</option> +<option value="OH">Ohio</option> +<option value="OK">Oklahoma</option> +<option value="OR">Oregon</option> +<option value="PW">Palau</option> +<option value="PA">Pennsylvania</option> +<option value="PR">Puerto Rico</option> +<option value="RI">Rhode Island</option> +<option value="SC">South Carolina</option> +<option value="SD">South Dakota</option> +<option value="TN">Tennessee</option> +<option value="TX">Texas</option> +<option value="UT">Utah</option> +<option value="VT">Vermont</option> +<option value="VI">Virgin Islands</option> +<option value="VA">Virginia</option> +<option value="WA">Washington</option> +<option value="WV">West Virginia</option> +<option value="WI">Wisconsin</option> +<option value="WY">Wyoming</option> +</select> + +# USSocialSecurityNumberField ################################################# +>>> from django.contrib.localflavor.us.forms import USSocialSecurityNumberField +>>> f = USSocialSecurityNumberField() +>>> f.clean('987-65-4330') +u'987-65-4330' +>>> f.clean('987654330') +u'987-65-4330' +>>> f.clean('078-05-1120') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid U.S. Social Security number in XXX-XX-XXXX format.'] +""" diff --git a/tests/regressiontests/forms/regressions.py b/tests/regressiontests/forms/regressions.py index 3212e20170..1bfb425188 100644 --- a/tests/regressiontests/forms/regressions.py +++ b/tests/regressiontests/forms/regressions.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Tests to prevent against recurrences of earlier bugs. -regression_tests = r""" +tests = r""" It should be possible to re-use attribute dictionaries (#3810) >>> from django.newforms import * >>> extra_attrs = {'class': 'special'} diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index e14c4e1ae0..49f03da798 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -1,3991 +1,58 @@ # -*- coding: utf-8 -*- -from localflavor import localflavor_tests -from regressions import regression_tests +from extra import tests as extra_tests +from fields import tests as fields_tests +from forms import tests as form_tests +from localflavor.ar import tests as localflavor_ar_tests +from localflavor.au import tests as localflavor_au_tests +from localflavor.br import tests as localflavor_br_tests +from localflavor.ca import tests as localflavor_ca_tests +from localflavor.ch import tests as localflavor_ch_tests +from localflavor.cl import tests as localflavor_cl_tests +from localflavor.de import tests as localflavor_de_tests +from localflavor.fi import tests as localflavor_fi_tests +from localflavor.fr import tests as localflavor_fr_tests +from localflavor.generic import tests as localflavor_generic_tests +from localflavor.is_ import tests as localflavor_is_tests +from localflavor.it import tests as localflavor_it_tests +from localflavor.jp import tests as localflavor_jp_tests +from localflavor.nl import tests as localflavor_nl_tests +from localflavor.pl import tests as localflavor_pl_tests +from localflavor.sk import tests as localflavor_sk_tests +from localflavor.uk import tests as localflavor_uk_tests +from localflavor.us import tests as localflavor_us_tests +from regressions import tests as regression_tests +from util import tests as util_tests +from widgets import tests as widgets_tests from formsets import formset_tests from media import media_tests -from util import util_tests - -form_tests = r""" ->>> from django.newforms import * ->>> from django.newforms.widgets import RadioFieldRenderer ->>> import datetime ->>> import time ->>> import re ->>> try: -... from decimal import Decimal -... except ImportError: -... from django.utils._decimal import Decimal - -########### -# Widgets # -########### - -Each Widget class corresponds to an HTML form widget. A Widget knows how to -render itself, given a field name and some data. Widgets don't perform -validation. - -# TextInput Widget ############################################################ - ->>> w = TextInput() ->>> w.render('email', '') -u'<input type="text" name="email" />' ->>> w.render('email', None) -u'<input type="text" name="email" />' ->>> w.render('email', 'test@example.com') -u'<input type="text" name="email" value="test@example.com" />' ->>> w.render('email', 'some "quoted" & ampersanded value') -u'<input type="text" name="email" value="some "quoted" & ampersanded value" />' ->>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) -u'<input type="text" name="email" value="test@example.com" class="fun" />' - -# Note that doctest in Python 2.4 (and maybe 2.5?) doesn't support non-ascii -# characters in output, so we're displaying the repr() here. ->>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) -u'<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />' - -You can also pass 'attrs' to the constructor: ->>> w = TextInput(attrs={'class': 'fun'}) ->>> w.render('email', '') -u'<input type="text" class="fun" name="email" />' ->>> w.render('email', 'foo@example.com') -u'<input type="text" class="fun" value="foo@example.com" name="email" />' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = TextInput(attrs={'class': 'pretty'}) ->>> w.render('email', '', attrs={'class': 'special'}) -u'<input type="text" class="special" name="email" />' - -# PasswordInput Widget ############################################################ - ->>> w = PasswordInput() ->>> w.render('email', '') -u'<input type="password" name="email" />' ->>> w.render('email', None) -u'<input type="password" name="email" />' ->>> w.render('email', 'test@example.com') -u'<input type="password" name="email" value="test@example.com" />' ->>> w.render('email', 'some "quoted" & ampersanded value') -u'<input type="password" name="email" value="some "quoted" & ampersanded value" />' ->>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) -u'<input type="password" name="email" value="test@example.com" class="fun" />' - -You can also pass 'attrs' to the constructor: ->>> w = PasswordInput(attrs={'class': 'fun'}) ->>> w.render('email', '') -u'<input type="password" class="fun" name="email" />' ->>> w.render('email', 'foo@example.com') -u'<input type="password" class="fun" value="foo@example.com" name="email" />' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = PasswordInput(attrs={'class': 'pretty'}) ->>> w.render('email', '', attrs={'class': 'special'}) -u'<input type="password" class="special" name="email" />' - ->>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) -u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />' - -The render_value argument lets you specify whether the widget should render -its value. You may want to do this for security reasons. ->>> w = PasswordInput(render_value=True) ->>> w.render('email', 'secret') -u'<input type="password" name="email" value="secret" />' ->>> w = PasswordInput(render_value=False) ->>> w.render('email', '') -u'<input type="password" name="email" />' ->>> w.render('email', None) -u'<input type="password" name="email" />' ->>> w.render('email', 'secret') -u'<input type="password" name="email" />' ->>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False) ->>> w.render('email', 'secret') -u'<input type="password" class="fun" name="email" />' - -# HiddenInput Widget ############################################################ - ->>> w = HiddenInput() ->>> w.render('email', '') -u'<input type="hidden" name="email" />' ->>> w.render('email', None) -u'<input type="hidden" name="email" />' ->>> w.render('email', 'test@example.com') -u'<input type="hidden" name="email" value="test@example.com" />' ->>> w.render('email', 'some "quoted" & ampersanded value') -u'<input type="hidden" name="email" value="some "quoted" & ampersanded value" />' ->>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) -u'<input type="hidden" name="email" value="test@example.com" class="fun" />' - -You can also pass 'attrs' to the constructor: ->>> w = HiddenInput(attrs={'class': 'fun'}) ->>> w.render('email', '') -u'<input type="hidden" class="fun" name="email" />' ->>> w.render('email', 'foo@example.com') -u'<input type="hidden" class="fun" value="foo@example.com" name="email" />' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = HiddenInput(attrs={'class': 'pretty'}) ->>> w.render('email', '', attrs={'class': 'special'}) -u'<input type="hidden" class="special" name="email" />' - ->>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) -u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = HiddenInput(attrs={'class': 'pretty'}) ->>> w.render('email', '', attrs={'class': 'special'}) -u'<input type="hidden" class="special" name="email" />' - -# MultipleHiddenInput Widget ################################################## - ->>> w = MultipleHiddenInput() ->>> w.render('email', []) -u'' ->>> w.render('email', None) -u'' ->>> w.render('email', ['test@example.com']) -u'<input type="hidden" name="email" value="test@example.com" />' ->>> w.render('email', ['some "quoted" & ampersanded value']) -u'<input type="hidden" name="email" value="some "quoted" & ampersanded value" />' ->>> w.render('email', ['test@example.com', 'foo@example.com']) -u'<input type="hidden" name="email" value="test@example.com" />\n<input type="hidden" name="email" value="foo@example.com" />' ->>> w.render('email', ['test@example.com'], attrs={'class': 'fun'}) -u'<input type="hidden" name="email" value="test@example.com" class="fun" />' ->>> w.render('email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'}) -u'<input type="hidden" name="email" value="test@example.com" class="fun" />\n<input type="hidden" name="email" value="foo@example.com" class="fun" />' - -You can also pass 'attrs' to the constructor: ->>> w = MultipleHiddenInput(attrs={'class': 'fun'}) ->>> w.render('email', []) -u'' ->>> w.render('email', ['foo@example.com']) -u'<input type="hidden" class="fun" value="foo@example.com" name="email" />' ->>> w.render('email', ['foo@example.com', 'test@example.com']) -u'<input type="hidden" class="fun" value="foo@example.com" name="email" />\n<input type="hidden" class="fun" value="test@example.com" name="email" />' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = MultipleHiddenInput(attrs={'class': 'pretty'}) ->>> w.render('email', ['foo@example.com'], attrs={'class': 'special'}) -u'<input type="hidden" class="special" value="foo@example.com" name="email" />' - ->>> w.render('email', ['ŠĐĆŽćžšđ'], attrs={'class': 'fun'}) -u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = MultipleHiddenInput(attrs={'class': 'pretty'}) ->>> w.render('email', ['foo@example.com'], attrs={'class': 'special'}) -u'<input type="hidden" class="special" value="foo@example.com" name="email" />' - -# FileInput Widget ############################################################ - -FileInput widgets don't ever show the value, because the old value is of no use -if you are updating the form or if the provided file generated an error. ->>> w = FileInput() ->>> w.render('email', '') -u'<input type="file" name="email" />' ->>> w.render('email', None) -u'<input type="file" name="email" />' ->>> w.render('email', 'test@example.com') -u'<input type="file" name="email" />' ->>> w.render('email', 'some "quoted" & ampersanded value') -u'<input type="file" name="email" />' ->>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) -u'<input type="file" name="email" class="fun" />' - -You can also pass 'attrs' to the constructor: ->>> w = FileInput(attrs={'class': 'fun'}) ->>> w.render('email', '') -u'<input type="file" class="fun" name="email" />' ->>> w.render('email', 'foo@example.com') -u'<input type="file" class="fun" name="email" />' - ->>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) -u'<input type="file" class="fun" name="email" />' - -# Textarea Widget ############################################################# - ->>> w = Textarea() ->>> w.render('msg', '') -u'<textarea rows="10" cols="40" name="msg"></textarea>' ->>> w.render('msg', None) -u'<textarea rows="10" cols="40" name="msg"></textarea>' ->>> w.render('msg', 'value') -u'<textarea rows="10" cols="40" name="msg">value</textarea>' ->>> w.render('msg', 'some "quoted" & ampersanded value') -u'<textarea rows="10" cols="40" name="msg">some "quoted" & ampersanded value</textarea>' ->>> w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20}) -u'<textarea class="pretty" rows="20" cols="40" name="msg">value</textarea>' - -You can also pass 'attrs' to the constructor: ->>> w = Textarea(attrs={'class': 'pretty'}) ->>> w.render('msg', '') -u'<textarea rows="10" cols="40" name="msg" class="pretty"></textarea>' ->>> w.render('msg', 'example') -u'<textarea rows="10" cols="40" name="msg" class="pretty">example</textarea>' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = Textarea(attrs={'class': 'pretty'}) ->>> w.render('msg', '', attrs={'class': 'special'}) -u'<textarea rows="10" cols="40" name="msg" class="special"></textarea>' - ->>> w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) -u'<textarea rows="10" cols="40" name="msg" class="fun">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>' - -# CheckboxInput Widget ######################################################## - ->>> w = CheckboxInput() ->>> w.render('is_cool', '') -u'<input type="checkbox" name="is_cool" />' ->>> w.render('is_cool', None) -u'<input type="checkbox" name="is_cool" />' ->>> w.render('is_cool', False) -u'<input type="checkbox" name="is_cool" />' ->>> w.render('is_cool', True) -u'<input checked="checked" type="checkbox" name="is_cool" />' - -Using any value that's not in ('', None, False, True) will check the checkbox -and set the 'value' attribute. ->>> w.render('is_cool', 'foo') -u'<input checked="checked" type="checkbox" name="is_cool" value="foo" />' - ->>> w.render('is_cool', False, attrs={'class': 'pretty'}) -u'<input type="checkbox" name="is_cool" class="pretty" />' - -You can also pass 'attrs' to the constructor: ->>> w = CheckboxInput(attrs={'class': 'pretty'}) ->>> w.render('is_cool', '') -u'<input type="checkbox" class="pretty" name="is_cool" />' - -'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = CheckboxInput(attrs={'class': 'pretty'}) ->>> w.render('is_cool', '', attrs={'class': 'special'}) -u'<input type="checkbox" class="special" name="is_cool" />' - -You can pass 'check_test' to the constructor. This is a callable that takes the -value and returns True if the box should be checked. ->>> w = CheckboxInput(check_test=lambda value: value.startswith('hello')) ->>> w.render('greeting', '') -u'<input type="checkbox" name="greeting" />' ->>> w.render('greeting', 'hello') -u'<input checked="checked" type="checkbox" name="greeting" value="hello" />' ->>> w.render('greeting', 'hello there') -u'<input checked="checked" type="checkbox" name="greeting" value="hello there" />' ->>> w.render('greeting', 'hello & goodbye') -u'<input checked="checked" type="checkbox" name="greeting" value="hello & goodbye" />' - -A subtlety: If the 'check_test' argument cannot handle a value and raises any -exception during its __call__, then the exception will be swallowed and the box -will not be checked. In this example, the 'check_test' assumes the value has a -startswith() method, which fails for the values True, False and None. ->>> w.render('greeting', True) -u'<input type="checkbox" name="greeting" />' ->>> w.render('greeting', False) -u'<input type="checkbox" name="greeting" />' ->>> w.render('greeting', None) -u'<input type="checkbox" name="greeting" />' - -# Select Widget ############################################################### - ->>> w = Select() ->>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select name="beatle"> -<option value="J" selected="selected">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> - -If the value is None, none of the options are selected: ->>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select name="beatle"> -<option value="J">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> - -If the value corresponds to a label (but not to an option value), none of the options are selected: ->>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select name="beatle"> -<option value="J">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> - -The value is compared to its str(): ->>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]) -<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> ->>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]) -<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> ->>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]) -<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> - -The 'choices' argument can be any iterable: ->>> from itertools import chain ->>> def get_choices(): -... for i in range(5): -... yield (i, i) ->>> print w.render('num', 2, choices=get_choices()) -<select name="num"> -<option value="0">0</option> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -<option value="4">4</option> -</select> ->>> things = ({'id': 1, 'name': 'And Boom'}, {'id': 2, 'name': 'One More Thing!'}) ->>> class SomeForm(Form): -... somechoice = ChoiceField(choices=chain((('', '-'*9),), [(thing['id'], thing['name']) for thing in things])) ->>> f = SomeForm() ->>> f.as_table() -u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>' ->>> f.as_table() -u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>' ->>> f = SomeForm({'somechoice': 2}) ->>> f.as_table() -u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="">---------</option>\n<option value="1">And Boom</option>\n<option value="2" selected="selected">One More Thing!</option>\n</select></td></tr>' - -You can also pass 'choices' to the constructor: ->>> w = Select(choices=[(1, 1), (2, 2), (3, 3)]) ->>> print w.render('num', 2) -<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> - -If 'choices' is passed to both the constructor and render(), then they'll both be in the output: ->>> print w.render('num', 2, choices=[(4, 4), (5, 5)]) -<select name="num"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -<option value="4">4</option> -<option value="5">5</option> -</select> - ->>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) -u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>' - -If choices is passed to the constructor and is a generator, it can be iterated -over multiple times without getting consumed: ->>> w = Select(choices=get_choices()) ->>> print w.render('num', 2) -<select name="num"> -<option value="0">0</option> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -<option value="4">4</option> -</select> ->>> print w.render('num', 3) -<select name="num"> -<option value="0">0</option> -<option value="1">1</option> -<option value="2">2</option> -<option value="3" selected="selected">3</option> -<option value="4">4</option> -</select> - -# NullBooleanSelect Widget #################################################### - ->>> w = NullBooleanSelect() ->>> print w.render('is_cool', True) -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected="selected">Yes</option> -<option value="3">No</option> -</select> ->>> print w.render('is_cool', False) -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected="selected">No</option> -</select> ->>> print w.render('is_cool', None) -<select name="is_cool"> -<option value="1" selected="selected">Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> -</select> ->>> print w.render('is_cool', '2') -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected="selected">Yes</option> -<option value="3">No</option> -</select> ->>> print w.render('is_cool', '3') -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected="selected">No</option> -</select> - -# SelectMultiple Widget ####################################################### - ->>> w = SelectMultiple() ->>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select multiple="multiple" name="beatles"> -<option value="J" selected="selected">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> ->>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select multiple="multiple" name="beatles"> -<option value="J" selected="selected">John</option> -<option value="P" selected="selected">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> ->>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select multiple="multiple" name="beatles"> -<option value="J" selected="selected">John</option> -<option value="P" selected="selected">Paul</option> -<option value="G">George</option> -<option value="R" selected="selected">Ringo</option> -</select> - -If the value is None, none of the options are selected: ->>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select multiple="multiple" name="beatles"> -<option value="J">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> - -If the value corresponds to a label (but not to an option value), none of the options are selected: ->>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select multiple="multiple" name="beatles"> -<option value="J">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> - -If multiple values are given, but some of them are not valid, the valid ones are selected: ->>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<select multiple="multiple" name="beatles"> -<option value="J" selected="selected">John</option> -<option value="P">Paul</option> -<option value="G" selected="selected">George</option> -<option value="R">Ringo</option> -</select> - -The value is compared to its str(): ->>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]) -<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> ->>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]) -<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> ->>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]) -<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> - -The 'choices' argument can be any iterable: ->>> def get_choices(): -... for i in range(5): -... yield (i, i) ->>> print w.render('nums', [2], choices=get_choices()) -<select multiple="multiple" name="nums"> -<option value="0">0</option> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -<option value="4">4</option> -</select> - -You can also pass 'choices' to the constructor: ->>> w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) ->>> print w.render('nums', [2]) -<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -</select> - -If 'choices' is passed to both the constructor and render(), then they'll both be in the output: ->>> print w.render('nums', [2], choices=[(4, 4), (5, 5)]) -<select multiple="multiple" name="nums"> -<option value="1">1</option> -<option value="2" selected="selected">2</option> -<option value="3">3</option> -<option value="4">4</option> -<option value="5">5</option> -</select> - ->>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) -u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>' - -# RadioSelect Widget ########################################################## - ->>> w = RadioSelect() ->>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input checked="checked" type="radio" name="beatle" value="J" /> John</label></li> -<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li> -<li><label><input type="radio" name="beatle" value="G" /> George</label></li> -<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li> -</ul> - -If the value is None, none of the options are checked: ->>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input type="radio" name="beatle" value="J" /> John</label></li> -<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li> -<li><label><input type="radio" name="beatle" value="G" /> George</label></li> -<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li> -</ul> - -If the value corresponds to a label (but not to an option value), none of the options are checked: ->>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input type="radio" name="beatle" value="J" /> John</label></li> -<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li> -<li><label><input type="radio" name="beatle" value="G" /> George</label></li> -<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li> -</ul> - -The value is compared to its str(): ->>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]) -<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul> ->>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]) -<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul> ->>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]) -<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul> - -The 'choices' argument can be any iterable: ->>> def get_choices(): -... for i in range(5): -... yield (i, i) ->>> print w.render('num', 2, choices=get_choices()) -<ul> -<li><label><input type="radio" name="num" value="0" /> 0</label></li> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -<li><label><input type="radio" name="num" value="4" /> 4</label></li> -</ul> - -You can also pass 'choices' to the constructor: ->>> w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)]) ->>> print w.render('num', 2) -<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -</ul> - -If 'choices' is passed to both the constructor and render(), then they'll both be in the output: ->>> print w.render('num', 2, choices=[(4, 4), (5, 5)]) -<ul> -<li><label><input type="radio" name="num" value="1" /> 1</label></li> -<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> -<li><label><input type="radio" name="num" value="3" /> 3</label></li> -<li><label><input type="radio" name="num" value="4" /> 4</label></li> -<li><label><input type="radio" name="num" value="5" /> 5</label></li> -</ul> - -RadioSelect uses a RadioFieldRenderer to render the individual radio inputs. -You can manipulate that object directly to customize the way the RadioSelect -is rendered. ->>> w = RadioSelect() ->>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) ->>> for inp in r: -... print inp -<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label> -<label><input type="radio" name="beatle" value="P" /> Paul</label> -<label><input type="radio" name="beatle" value="G" /> George</label> -<label><input type="radio" name="beatle" value="R" /> Ringo</label> ->>> for inp in r: -... print '%s<br />' % inp -<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label><br /> -<label><input type="radio" name="beatle" value="P" /> Paul</label><br /> -<label><input type="radio" name="beatle" value="G" /> George</label><br /> -<label><input type="radio" name="beatle" value="R" /> Ringo</label><br /> ->>> for inp in r: -... print '<p>%s %s</p>' % (inp.tag(), inp.choice_label) -<p><input checked="checked" type="radio" name="beatle" value="J" /> John</p> -<p><input type="radio" name="beatle" value="P" /> Paul</p> -<p><input type="radio" name="beatle" value="G" /> George</p> -<p><input type="radio" name="beatle" value="R" /> Ringo</p> ->>> for inp in r: -... print '%s %s %s %s %s' % (inp.name, inp.value, inp.choice_value, inp.choice_label, inp.is_checked()) -beatle J J John True -beatle J P Paul False -beatle J G George False -beatle J R Ringo False - -You can create your own custom renderers for RadioSelect to use. ->>> class MyRenderer(RadioFieldRenderer): -... def render(self): -... return u'<br />\n'.join([unicode(choice) for choice in self]) ->>> w = RadioSelect(renderer=MyRenderer) ->>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<label><input type="radio" name="beatle" value="J" /> John</label><br /> -<label><input type="radio" name="beatle" value="P" /> Paul</label><br /> -<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br /> -<label><input type="radio" name="beatle" value="R" /> Ringo</label> - -A RadioFieldRenderer object also allows index access to individual RadioInput -objects. ->>> w = RadioSelect() ->>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) ->>> print r[1] -<label><input type="radio" name="beatle" value="P" /> Paul</label> ->>> print r[0] -<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label> ->>> r[0].is_checked() -True ->>> r[1].is_checked() -False ->>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label -('beatle', u'J', u'P', u'Paul') ->>> r[10] -Traceback (most recent call last): -... -IndexError: list index out of range - -# Unicode choices are correctly rendered as HTML ->>> w = RadioSelect() ->>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])) -u'<ul>\n<li><label><input checked="checked" type="radio" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="radio" name="email" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>' - -# Attributes provided at instantiation are passed to the constituent inputs ->>> w = RadioSelect(attrs={'id':'foo'}) ->>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input checked="checked" type="radio" id="foo_0" value="J" name="beatle" /> John</label></li> -<li><label><input type="radio" id="foo_1" value="P" name="beatle" /> Paul</label></li> -<li><label><input type="radio" id="foo_2" value="G" name="beatle" /> George</label></li> -<li><label><input type="radio" id="foo_3" value="R" name="beatle" /> Ringo</label></li> -</ul> - -# Attributes provided at render-time are passed to the constituent inputs ->>> w = RadioSelect() ->>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id':'bar'}) -<ul> -<li><label><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label></li> -<li><label><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li> -<li><label><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li> -<li><label><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></li> -</ul> - -# CheckboxSelectMultiple Widget ############################################### - ->>> w = CheckboxSelectMultiple() ->>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> -<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> -<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> -<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul> ->>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> -<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li> -<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> -<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul> ->>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> -<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li> -<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> -<li><label><input checked="checked" type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul> - -If the value is None, none of the options are selected: ->>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input type="checkbox" name="beatles" value="J" /> John</label></li> -<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> -<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> -<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul> - -If the value corresponds to a label (but not to an option value), none of the options are selected: ->>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input type="checkbox" name="beatles" value="J" /> John</label></li> -<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> -<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> -<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul> - -If multiple values are given, but some of them are not valid, the valid ones are selected: ->>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) -<ul> -<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> -<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> -<li><label><input checked="checked" type="checkbox" name="beatles" value="G" /> George</label></li> -<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> -</ul> - -The value is compared to its str(): ->>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]) -<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul> ->>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]) -<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul> ->>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]) -<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul> - -The 'choices' argument can be any iterable: ->>> def get_choices(): -... for i in range(5): -... yield (i, i) ->>> print w.render('nums', [2], choices=get_choices()) -<ul> -<li><label><input type="checkbox" name="nums" value="0" /> 0</label></li> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> -</ul> - -You can also pass 'choices' to the constructor: ->>> w = CheckboxSelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) ->>> print w.render('nums', [2]) -<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -</ul> - -If 'choices' is passed to both the constructor and render(), then they'll both be in the output: ->>> print w.render('nums', [2], choices=[(4, 4), (5, 5)]) -<ul> -<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> -<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> -<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> -<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> -<li><label><input type="checkbox" name="nums" value="5" /> 5</label></li> -</ul> - ->>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) -u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>' - -# MultiWidget ################################################################# - ->>> class MyMultiWidget(MultiWidget): -... def decompress(self, value): -... if value: -... return value.split('__') -... return ['', ''] -... def format_output(self, rendered_widgets): -... return u'<br />'.join(rendered_widgets) ->>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'}))) ->>> w.render('name', ['john', 'lennon']) -u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />' ->>> w.render('name', 'john__lennon') -u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />' ->>> w.render('name', 'john__lennon', attrs={'id':'foo'}) -u'<input id="foo_0" type="text" class="big" value="john" name="name_0" /><br /><input id="foo_1" type="text" class="small" value="lennon" name="name_1" />' ->>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'}) ->>> w.render('name', ['john', 'lennon']) -u'<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />' - -# SplitDateTimeWidget ######################################################### - ->>> w = SplitDateTimeWidget() ->>> w.render('date', '') -u'<input type="text" name="date_0" /><input type="text" name="date_1" />' ->>> w.render('date', None) -u'<input type="text" name="date_0" /><input type="text" name="date_1" />' ->>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30)) -u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />' ->>> w.render('date', [datetime.date(2006, 1, 10), datetime.time(7, 30)]) -u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />' - -You can also pass 'attrs' to the constructor. In this case, the attrs will be -included on both widgets. ->>> w = SplitDateTimeWidget(attrs={'class': 'pretty'}) ->>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30)) -u'<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />' - -########## -# Fields # -########## - -Each Field class does some sort of validation. Each Field has a clean() method, -which either raises django.newforms.ValidationError or returns the "clean" -data -- usually a Unicode object, but, in some rare cases, a list. - -Each Field's __init__() takes at least these parameters: - required -- Boolean that specifies whether the field is required. - True by default. - widget -- A Widget class, or instance of a Widget class, that should be - used for this Field when displaying it. Each Field has a default - Widget that it'll use if you don't specify this. In most cases, - the default widget is TextInput. - label -- A verbose name for this field, for use in displaying this field in - a form. By default, Django will use a "pretty" version of the form - field name, if the Field is part of a Form. - initial -- A value to use in this Field's initial display. This value is - *not* used as a fallback if data isn't given. - -Other than that, the Field subclasses have class-specific options for -__init__(). For example, CharField has a max_length option. - -# CharField ################################################################### - ->>> f = CharField() ->>> f.clean(1) -u'1' ->>> f.clean('hello') -u'hello' ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean([1, 2, 3]) -u'[1, 2, 3]' - ->>> f = CharField(required=False) ->>> f.clean(1) -u'1' ->>> f.clean('hello') -u'hello' ->>> f.clean(None) -u'' ->>> f.clean('') -u'' ->>> f.clean([1, 2, 3]) -u'[1, 2, 3]' - -CharField accepts an optional max_length parameter: ->>> f = CharField(max_length=10, required=False) ->>> f.clean('12345') -u'12345' ->>> f.clean('1234567890') -u'1234567890' ->>> f.clean('1234567890a') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 10 characters (it has 11).'] - -CharField accepts an optional min_length parameter: ->>> f = CharField(min_length=10, required=False) ->>> f.clean('') -u'' ->>> f.clean('12345') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 10 characters (it has 5).'] ->>> f.clean('1234567890') -u'1234567890' ->>> f.clean('1234567890a') -u'1234567890a' - ->>> f = CharField(min_length=10, required=True) ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('12345') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 10 characters (it has 5).'] ->>> f.clean('1234567890') -u'1234567890' ->>> f.clean('1234567890a') -u'1234567890a' - -# IntegerField ################################################################ - ->>> f = IntegerField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('1') -1 ->>> isinstance(f.clean('1'), int) -True ->>> f.clean('23') -23 ->>> f.clean('a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a whole number.'] ->>> f.clean(42) -42 ->>> f.clean(3.14) -Traceback (most recent call last): -... -ValidationError: [u'Enter a whole number.'] ->>> f.clean('1 ') -1 ->>> f.clean(' 1') -1 ->>> f.clean(' 1 ') -1 ->>> f.clean('1a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a whole number.'] - ->>> f = IntegerField(required=False) ->>> f.clean('') ->>> repr(f.clean('')) -'None' ->>> f.clean(None) ->>> repr(f.clean(None)) -'None' ->>> f.clean('1') -1 ->>> isinstance(f.clean('1'), int) -True ->>> f.clean('23') -23 ->>> f.clean('a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a whole number.'] ->>> f.clean('1 ') -1 ->>> f.clean(' 1') -1 ->>> f.clean(' 1 ') -1 ->>> f.clean('1a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a whole number.'] - -IntegerField accepts an optional max_value parameter: ->>> f = IntegerField(max_value=10) ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(1) -1 ->>> f.clean(10) -10 ->>> f.clean(11) -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is less than or equal to 10.'] ->>> f.clean('10') -10 ->>> f.clean('11') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is less than or equal to 10.'] - -IntegerField accepts an optional min_value parameter: ->>> f = IntegerField(min_value=10) ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(1) -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is greater than or equal to 10.'] ->>> f.clean(10) -10 ->>> f.clean(11) -11 ->>> f.clean('10') -10 ->>> f.clean('11') -11 - -min_value and max_value can be used together: ->>> f = IntegerField(min_value=10, max_value=20) ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(1) -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is greater than or equal to 10.'] ->>> f.clean(10) -10 ->>> f.clean(11) -11 ->>> f.clean('10') -10 ->>> f.clean('11') -11 ->>> f.clean(20) -20 ->>> f.clean(21) -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is less than or equal to 20.'] - -# FloatField ################################################################## - ->>> f = FloatField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('1') -1.0 ->>> isinstance(f.clean('1'), float) -True ->>> f.clean('23') -23.0 ->>> f.clean('3.14') -3.1400000000000001 ->>> f.clean(3.14) -3.1400000000000001 ->>> f.clean(42) -42.0 ->>> f.clean('a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a number.'] ->>> f.clean('1.0 ') -1.0 ->>> f.clean(' 1.0') -1.0 ->>> f.clean(' 1.0 ') -1.0 ->>> f.clean('1.0a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a number.'] - ->>> f = FloatField(required=False) ->>> f.clean('') - ->>> f.clean(None) - ->>> f.clean('1') -1.0 - -FloatField accepts min_value and max_value just like IntegerField: ->>> f = FloatField(max_value=1.5, min_value=0.5) - ->>> f.clean('1.6') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is less than or equal to 1.5.'] ->>> f.clean('0.4') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] ->>> f.clean('1.5') -1.5 ->>> f.clean('0.5') -0.5 - -# DecimalField ################################################################ - ->>> f = DecimalField(max_digits=4, decimal_places=2) ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('1') -Decimal("1") ->>> isinstance(f.clean('1'), Decimal) -True ->>> f.clean('23') -Decimal("23") ->>> f.clean('3.14') -Decimal("3.14") ->>> f.clean(3.14) -Decimal("3.14") ->>> f.clean(Decimal('3.14')) -Decimal("3.14") ->>> f.clean('a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a number.'] ->>> f.clean('1.0 ') -Decimal("1.0") ->>> f.clean(' 1.0') -Decimal("1.0") ->>> f.clean(' 1.0 ') -Decimal("1.0") ->>> f.clean('1.0a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a number.'] ->>> f.clean('123.45') -Traceback (most recent call last): -... -ValidationError: [u'Ensure that there are no more than 4 digits in total.'] ->>> f.clean('1.234') -Traceback (most recent call last): -... -ValidationError: [u'Ensure that there are no more than 2 decimal places.'] ->>> f.clean('123.4') -Traceback (most recent call last): -... -ValidationError: [u'Ensure that there are no more than 2 digits before the decimal point.'] ->>> f.clean('-12.34') -Decimal("-12.34") ->>> f.clean('-123.45') -Traceback (most recent call last): -... -ValidationError: [u'Ensure that there are no more than 4 digits in total.'] ->>> f.clean('-.12') -Decimal("-0.12") ->>> f.clean('-00.12') -Decimal("-0.12") ->>> f.clean('-000.12') -Decimal("-0.12") ->>> f.clean('-000.123') -Traceback (most recent call last): -... -ValidationError: [u'Ensure that there are no more than 2 decimal places.'] ->>> f.clean('-000.1234') -Traceback (most recent call last): -... -ValidationError: [u'Ensure that there are no more than 4 digits in total.'] ->>> f.clean('--0.12') -Traceback (most recent call last): -... -ValidationError: [u'Enter a number.'] - ->>> f = DecimalField(max_digits=4, decimal_places=2, required=False) ->>> f.clean('') - ->>> f.clean(None) - ->>> f.clean('1') -Decimal("1") - -DecimalField accepts min_value and max_value just like IntegerField: ->>> f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5')) - ->>> f.clean('1.6') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is less than or equal to 1.5.'] ->>> f.clean('0.4') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] ->>> f.clean('1.5') -Decimal("1.5") ->>> f.clean('0.5') -Decimal("0.5") ->>> f.clean('.5') -Decimal("0.5") ->>> f.clean('00.50') -Decimal("0.50") - -# DateField ################################################################### - ->>> import datetime ->>> f = DateField() ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) -datetime.date(2006, 10, 25) ->>> f.clean('2006-10-25') -datetime.date(2006, 10, 25) ->>> f.clean('10/25/2006') -datetime.date(2006, 10, 25) ->>> f.clean('10/25/06') -datetime.date(2006, 10, 25) ->>> f.clean('Oct 25 2006') -datetime.date(2006, 10, 25) ->>> f.clean('October 25 2006') -datetime.date(2006, 10, 25) ->>> f.clean('October 25, 2006') -datetime.date(2006, 10, 25) ->>> f.clean('25 October 2006') -datetime.date(2006, 10, 25) ->>> f.clean('25 October, 2006') -datetime.date(2006, 10, 25) ->>> f.clean('2006-4-31') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('200a-10-25') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('25/10/06') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = DateField(required=False) ->>> f.clean(None) ->>> repr(f.clean(None)) -'None' ->>> f.clean('') ->>> repr(f.clean('')) -'None' - -DateField accepts an optional input_formats parameter: ->>> f = DateField(input_formats=['%Y %m %d']) ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.date(2006, 10, 25) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.date(2006, 10, 25) ->>> f.clean('2006 10 25') -datetime.date(2006, 10, 25) - -The input_formats parameter overrides all default input formats, -so the default formats won't work unless you specify them: ->>> f.clean('2006-10-25') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('10/25/2006') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean('10/25/06') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] - -# TimeField ################################################################### - ->>> import datetime ->>> f = TimeField() ->>> f.clean(datetime.time(14, 25)) -datetime.time(14, 25) ->>> f.clean(datetime.time(14, 25, 59)) -datetime.time(14, 25, 59) ->>> f.clean('14:25') -datetime.time(14, 25) ->>> f.clean('14:25:59') -datetime.time(14, 25, 59) ->>> f.clean('hello') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid time.'] ->>> f.clean('1:24 p.m.') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid time.'] - -TimeField accepts an optional input_formats parameter: ->>> f = TimeField(input_formats=['%I:%M %p']) ->>> f.clean(datetime.time(14, 25)) -datetime.time(14, 25) ->>> f.clean(datetime.time(14, 25, 59)) -datetime.time(14, 25, 59) ->>> f.clean('4:25 AM') -datetime.time(4, 25) ->>> f.clean('4:25 PM') -datetime.time(16, 25) - -The input_formats parameter overrides all default input formats, -so the default formats won't work unless you specify them: ->>> f.clean('14:30:45') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid time.'] - -# DateTimeField ############################################################### - ->>> import datetime ->>> f = DateTimeField() ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) -datetime.datetime(2006, 10, 25, 14, 30, 59) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) -datetime.datetime(2006, 10, 25, 14, 30, 59, 200) ->>> f.clean('2006-10-25 14:30:45') -datetime.datetime(2006, 10, 25, 14, 30, 45) ->>> f.clean('2006-10-25 14:30:00') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('2006-10-25 14:30') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('2006-10-25') -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean('10/25/2006 14:30:45') -datetime.datetime(2006, 10, 25, 14, 30, 45) ->>> f.clean('10/25/2006 14:30:00') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('10/25/2006 14:30') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('10/25/2006') -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean('10/25/06 14:30:45') -datetime.datetime(2006, 10, 25, 14, 30, 45) ->>> f.clean('10/25/06 14:30:00') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('10/25/06 14:30') -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean('10/25/06') -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean('hello') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date/time.'] ->>> f.clean('2006-10-25 4:30 p.m.') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date/time.'] - -DateField accepts an optional input_formats parameter: ->>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p']) ->>> f.clean(datetime.date(2006, 10, 25)) -datetime.datetime(2006, 10, 25, 0, 0) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30)) -datetime.datetime(2006, 10, 25, 14, 30) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59)) -datetime.datetime(2006, 10, 25, 14, 30, 59) ->>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200)) -datetime.datetime(2006, 10, 25, 14, 30, 59, 200) ->>> f.clean('2006 10 25 2:30 PM') -datetime.datetime(2006, 10, 25, 14, 30) - -The input_formats parameter overrides all default input formats, -so the default formats won't work unless you specify them: ->>> f.clean('2006-10-25 14:30:45') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date/time.'] - ->>> f = DateTimeField(required=False) ->>> f.clean(None) ->>> repr(f.clean(None)) -'None' ->>> f.clean('') ->>> repr(f.clean('')) -'None' - -# RegexField ################################################################## - ->>> f = RegexField('^\d[A-F]\d$') ->>> f.clean('2A2') -u'2A2' ->>> f.clean('3F3') -u'3F3' ->>> f.clean('3G3') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] ->>> f.clean(' 2A2') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] ->>> f.clean('2A2 ') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = RegexField('^\d[A-F]\d$', required=False) ->>> f.clean('2A2') -u'2A2' ->>> f.clean('3F3') -u'3F3' ->>> f.clean('3G3') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] ->>> f.clean('') -u'' - -Alternatively, RegexField can take a compiled regular expression: ->>> f = RegexField(re.compile('^\d[A-F]\d$')) ->>> f.clean('2A2') -u'2A2' ->>> f.clean('3F3') -u'3F3' ->>> f.clean('3G3') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] ->>> f.clean(' 2A2') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] ->>> f.clean('2A2 ') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] - -RegexField takes an optional error_message argument: ->>> f = RegexField('^\d\d\d\d$', error_message='Enter a four-digit number.') ->>> f.clean('1234') -u'1234' ->>> f.clean('123') -Traceback (most recent call last): -... -ValidationError: [u'Enter a four-digit number.'] ->>> f.clean('abcd') -Traceback (most recent call last): -... -ValidationError: [u'Enter a four-digit number.'] - -RegexField also access min_length and max_length parameters, for convenience. ->>> f = RegexField('^\d+$', min_length=5, max_length=10) ->>> f.clean('123') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 5 characters (it has 3).'] ->>> f.clean('abc') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 5 characters (it has 3).'] ->>> f.clean('12345') -u'12345' ->>> f.clean('1234567890') -u'1234567890' ->>> f.clean('12345678901') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 10 characters (it has 11).'] ->>> f.clean('12345a') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid value.'] - -# EmailField ################################################################## - ->>> f = EmailField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('person@example.com') -u'person@example.com' ->>> f.clean('foo') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] ->>> f.clean('foo@') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] ->>> f.clean('foo@bar') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] - ->>> f = EmailField(required=False) ->>> f.clean('') -u'' ->>> f.clean(None) -u'' ->>> f.clean('person@example.com') -u'person@example.com' ->>> f.clean('foo') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] ->>> f.clean('foo@') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] ->>> f.clean('foo@bar') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] - -EmailField also access min_length and max_length parameters, for convenience. ->>> f = EmailField(min_length=10, max_length=15) ->>> f.clean('a@foo.com') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 10 characters (it has 9).'] ->>> f.clean('alf@foo.com') -u'alf@foo.com' ->>> f.clean('alf123456788@foo.com') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 15 characters (it has 20).'] - -# FileField ################################################################## - ->>> f = FileField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f.clean({}) -Traceback (most recent call last): -... -ValidationError: [u'No file was submitted.'] - ->>> f.clean('some content that is not a file') -Traceback (most recent call last): -... -ValidationError: [u'No file was submitted. Check the encoding type on the form.'] - ->>> f.clean({'filename': 'name', 'content':None}) -Traceback (most recent call last): -... -ValidationError: [u'The submitted file is empty.'] - ->>> f.clean({'filename': 'name', 'content':''}) -Traceback (most recent call last): -... -ValidationError: [u'The submitted file is empty.'] - ->>> type(f.clean({'filename': 'name', 'content':'Some File Content'})) -<class 'django.newforms.fields.UploadedFile'> - -# URLField ################################################################## - ->>> f = URLField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('http://localhost') -u'http://localhost' ->>> f.clean('http://example.com') -u'http://example.com' ->>> f.clean('http://www.example.com') -u'http://www.example.com' ->>> f.clean('http://www.example.com:8000/test') -u'http://www.example.com:8000/test' ->>> f.clean('http://200.8.9.10') -u'http://200.8.9.10' ->>> f.clean('http://200.8.9.10:8000/test') -u'http://200.8.9.10:8000/test' ->>> f.clean('foo') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://example') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://example.') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://.com') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] - ->>> f = URLField(required=False) ->>> f.clean('') -u'' ->>> f.clean(None) -u'' ->>> f.clean('http://example.com') -u'http://example.com' ->>> f.clean('http://www.example.com') -u'http://www.example.com' ->>> f.clean('foo') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://example') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://example.') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://.com') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] - -URLField takes an optional verify_exists parameter, which is False by default. -This verifies that the URL is live on the Internet and doesn't return a 404 or 500: ->>> f = URLField(verify_exists=True) ->>> f.clean('http://www.google.com') # This will fail if there's no Internet connection -u'http://www.google.com' ->>> f.clean('http://example') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid URL.'] ->>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') # bad domain -Traceback (most recent call last): -... -ValidationError: [u'This URL appears to be a broken link.'] ->>> f.clean('http://google.com/we-love-microsoft.html') # good domain, bad page -Traceback (most recent call last): -... -ValidationError: [u'This URL appears to be a broken link.'] ->>> f = URLField(verify_exists=True, required=False) ->>> f.clean('') -u'' ->>> f.clean('http://www.google.com') # This will fail if there's no Internet connection -u'http://www.google.com' - -URLField also access min_length and max_length parameters, for convenience. ->>> f = URLField(min_length=15, max_length=20) ->>> f.clean('http://f.com') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at least 15 characters (it has 12).'] ->>> f.clean('http://example.com') -u'http://example.com' ->>> f.clean('http://abcdefghijklmnopqrstuvwxyz.com') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 20 characters (it has 37).'] - -URLField should prepend 'http://' if no scheme was given ->>> f = URLField(required=False) ->>> f.clean('example.com') -u'http://example.com' ->>> f.clean('') -u'' ->>> f.clean('https://example.com') -u'https://example.com' - -# BooleanField ################################################################ - ->>> f = BooleanField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(True) -True ->>> f.clean(False) -False ->>> f.clean(1) -True ->>> f.clean(0) -False ->>> f.clean('Django rocks') -True - ->>> f = BooleanField(required=False) ->>> f.clean('') -False ->>> f.clean(None) -False ->>> f.clean(True) -True ->>> f.clean(False) -False ->>> f.clean(1) -True ->>> f.clean(0) -False ->>> f.clean('Django rocks') -True - -# ChoiceField ################################################################# - ->>> f = ChoiceField(choices=[('1', '1'), ('2', '2')]) ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(1) -u'1' ->>> f.clean('1') -u'1' ->>> f.clean('3') -Traceback (most recent call last): -... -ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] - ->>> f = ChoiceField(choices=[('1', '1'), ('2', '2')], required=False) ->>> f.clean('') -u'' ->>> f.clean(None) -u'' ->>> f.clean(1) -u'1' ->>> f.clean('1') -u'1' ->>> f.clean('3') -Traceback (most recent call last): -... -ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] - ->>> f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')]) ->>> f.clean('J') -u'J' ->>> f.clean('John') -Traceback (most recent call last): -... -ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] - -# NullBooleanField ############################################################ - ->>> f = NullBooleanField() ->>> f.clean('') ->>> f.clean(True) -True ->>> f.clean(False) -False ->>> f.clean(None) ->>> f.clean('1') ->>> f.clean('2') ->>> f.clean('3') ->>> f.clean('hello') - -# MultipleChoiceField ######################################################### - ->>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')]) ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean([1]) -[u'1'] ->>> f.clean(['1']) -[u'1'] ->>> f.clean(['1', '2']) -[u'1', u'2'] ->>> f.clean([1, '2']) -[u'1', u'2'] ->>> f.clean((1, '2')) -[u'1', u'2'] ->>> f.clean('hello') -Traceback (most recent call last): -... -ValidationError: [u'Enter a list of values.'] ->>> f.clean([]) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(()) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(['3']) -Traceback (most recent call last): -... -ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] - ->>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')], required=False) ->>> f.clean('') -[] ->>> f.clean(None) -[] ->>> f.clean([1]) -[u'1'] ->>> f.clean(['1']) -[u'1'] ->>> f.clean(['1', '2']) -[u'1', u'2'] ->>> f.clean([1, '2']) -[u'1', u'2'] ->>> f.clean((1, '2')) -[u'1', u'2'] ->>> f.clean('hello') -Traceback (most recent call last): -... -ValidationError: [u'Enter a list of values.'] ->>> f.clean([]) -[] ->>> f.clean(()) -[] ->>> f.clean(['3']) -Traceback (most recent call last): -... -ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] - -# ComboField ################################################################## - -ComboField takes a list of fields that should be used to validate a value, -in that order. ->>> f = ComboField(fields=[CharField(max_length=20), EmailField()]) ->>> f.clean('test@example.com') -u'test@example.com' ->>> f.clean('longemailaddress@example.com') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 20 characters (it has 28).'] ->>> f.clean('not an e-mail') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = ComboField(fields=[CharField(max_length=20), EmailField()], required=False) ->>> f.clean('test@example.com') -u'test@example.com' ->>> f.clean('longemailaddress@example.com') -Traceback (most recent call last): -... -ValidationError: [u'Ensure this value has at most 20 characters (it has 28).'] ->>> f.clean('not an e-mail') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid e-mail address.'] ->>> f.clean('') -u'' ->>> f.clean(None) -u'' - -# SplitDateTimeField ########################################################## - ->>> f = SplitDateTimeField() ->>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)]) -datetime.datetime(2006, 1, 10, 7, 30) ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('hello') -Traceback (most recent call last): -... -ValidationError: [u'Enter a list of values.'] ->>> f.clean(['hello', 'there']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.', u'Enter a valid time.'] ->>> f.clean(['2006-01-10', 'there']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid time.'] ->>> f.clean(['hello', '07:30']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] - ->>> f = SplitDateTimeField(required=False) ->>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)]) -datetime.datetime(2006, 1, 10, 7, 30) ->>> f.clean(['2006-01-10', '07:30']) -datetime.datetime(2006, 1, 10, 7, 30) ->>> f.clean(None) ->>> f.clean('') ->>> f.clean(['']) ->>> f.clean(['', '']) ->>> f.clean('hello') -Traceback (most recent call last): -... -ValidationError: [u'Enter a list of values.'] ->>> f.clean(['hello', 'there']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.', u'Enter a valid time.'] ->>> f.clean(['2006-01-10', 'there']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid time.'] ->>> f.clean(['hello', '07:30']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] ->>> f.clean(['2006-01-10', '']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid time.'] ->>> f.clean(['2006-01-10']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid time.'] ->>> f.clean(['', '07:30']) -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid date.'] - -######### -# Forms # -######### - -A Form is a collection of Fields. It knows how to validate a set of data and it -knows how to render itself in a couple of default ways (e.g., an HTML table). -You can pass it data in __init__(), as a dictionary. - -# Form ######################################################################## - ->>> class Person(Form): -... first_name = CharField() -... last_name = CharField() -... birthday = DateField() - -Pass a dictionary to a Form's __init__(). ->>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'}) ->>> p.is_bound -True ->>> p.errors -{} ->>> p.is_valid() -True ->>> p.errors.as_ul() -u'' ->>> p.errors.as_text() -u'' ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} ->>> print p['first_name'] -<input type="text" name="first_name" value="John" id="id_first_name" /> ->>> print p['last_name'] -<input type="text" name="last_name" value="Lennon" id="id_last_name" /> ->>> print p['birthday'] -<input type="text" name="birthday" value="1940-10-9" id="id_birthday" /> ->>> print p['nonexistentfield'] -Traceback (most recent call last): -... -KeyError: "Key 'nonexistentfield' not found in Form" - ->>> for boundfield in p: -... print boundfield -<input type="text" name="first_name" value="John" id="id_first_name" /> -<input type="text" name="last_name" value="Lennon" id="id_last_name" /> -<input type="text" name="birthday" value="1940-10-9" id="id_birthday" /> ->>> for boundfield in p: -... print boundfield.label, boundfield.data -First name John -Last name Lennon -Birthday 1940-10-9 ->>> print p -<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" value="John" id="id_first_name" /></td></tr> -<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" value="Lennon" id="id_last_name" /></td></tr> -<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></td></tr> - -Empty dictionaries are valid, too. ->>> p = Person({}) ->>> p.is_bound -True ->>> p.errors -{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} ->>> p.is_valid() -False ->>> p.cleaned_data -Traceback (most recent call last): -... -AttributeError: 'Person' object has no attribute 'cleaned_data' ->>> print p -<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr> -<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr> -<tr><th><label for="id_birthday">Birthday:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="birthday" id="id_birthday" /></td></tr> ->>> print p.as_table() -<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr> -<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr> -<tr><th><label for="id_birthday">Birthday:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="birthday" id="id_birthday" /></td></tr> ->>> print p.as_ul() -<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li> ->>> print p.as_p() -<ul class="errorlist"><li>This field is required.</li></ul> -<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> -<ul class="errorlist"><li>This field is required.</li></ul> -<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p> -<ul class="errorlist"><li>This field is required.</li></ul> -<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p> - -If you don't pass any values to the Form's __init__(), or if you pass None, -the Form will be considered unbound and won't do any validation. Form.errors -will be an empty dictionary *but* Form.is_valid() will return False. ->>> p = Person() ->>> p.is_bound -False ->>> p.errors -{} ->>> p.is_valid() -False ->>> p.cleaned_data -Traceback (most recent call last): -... -AttributeError: 'Person' object has no attribute 'cleaned_data' ->>> print p -<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> -<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> -<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr> ->>> print p.as_table() -<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> -<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> -<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr> ->>> print p.as_ul() -<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> -<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> -<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li> ->>> print p.as_p() -<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> -<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p> -<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p> - -Unicode values are handled properly. ->>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'}) ->>> p.as_table() -u'<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" value="John" id="id_first_name" /></td></tr>\n<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></td></tr>\n<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></td></tr>' ->>> p.as_ul() -u'<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></li>\n<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></li>\n<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></li>' ->>> p.as_p() -u'<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></p>\n<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></p>\n<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></p>' - ->>> p = Person({'last_name': u'Lennon'}) ->>> p.errors -{'first_name': [u'This field is required.'], 'birthday': [u'This field is required.']} ->>> p.is_valid() -False ->>> p.errors.as_ul() -u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is required.</li></ul></li><li>birthday<ul class="errorlist"><li>This field is required.</li></ul></li></ul>' ->>> print p.errors.as_text() -* first_name - * This field is required. -* birthday - * This field is required. ->>> p.cleaned_data -Traceback (most recent call last): -... -AttributeError: 'Person' object has no attribute 'cleaned_data' ->>> p['first_name'].errors -[u'This field is required.'] ->>> p['first_name'].errors.as_ul() -u'<ul class="errorlist"><li>This field is required.</li></ul>' ->>> p['first_name'].errors.as_text() -u'* This field is required.' - ->>> p = Person() ->>> print p['first_name'] -<input type="text" name="first_name" id="id_first_name" /> ->>> print p['last_name'] -<input type="text" name="last_name" id="id_last_name" /> ->>> print p['birthday'] -<input type="text" name="birthday" id="id_birthday" /> - -cleaned_data will always *only* contain a key for fields defined in the -Form, even if you pass extra data when you define the Form. In this -example, we pass a bunch of extra fields to the form constructor, -but cleaned_data contains only the form's fields. ->>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'} ->>> p = Person(data) ->>> p.is_valid() -True ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} - -cleaned_data will include a key and value for *all* fields defined in the Form, -even if the Form's data didn't include a value for fields that are not -required. In this example, the data dictionary doesn't include a value for the -"nick_name" field, but cleaned_data includes it. For CharFields, it's set to the -empty string. ->>> class OptionalPersonForm(Form): -... first_name = CharField() -... last_name = CharField() -... nick_name = CharField(required=False) ->>> data = {'first_name': u'John', 'last_name': u'Lennon'} ->>> f = OptionalPersonForm(data) ->>> f.is_valid() -True ->>> f.cleaned_data -{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} - -For DateFields, it's set to None. ->>> class OptionalPersonForm(Form): -... first_name = CharField() -... last_name = CharField() -... birth_date = DateField(required=False) ->>> data = {'first_name': u'John', 'last_name': u'Lennon'} ->>> f = OptionalPersonForm(data) ->>> f.is_valid() -True ->>> f.cleaned_data -{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'} - -"auto_id" tells the Form to add an "id" attribute to each form element. -If it's a string that contains '%s', Django will use that as a format string -into which the field's name will be inserted. It will also put a <label> around -the human-readable labels for a field. ->>> p = Person(auto_id='%s_id') ->>> print p.as_table() -<tr><th><label for="first_name_id">First name:</label></th><td><input type="text" name="first_name" id="first_name_id" /></td></tr> -<tr><th><label for="last_name_id">Last name:</label></th><td><input type="text" name="last_name" id="last_name_id" /></td></tr> -<tr><th><label for="birthday_id">Birthday:</label></th><td><input type="text" name="birthday" id="birthday_id" /></td></tr> ->>> print p.as_ul() -<li><label for="first_name_id">First name:</label> <input type="text" name="first_name" id="first_name_id" /></li> -<li><label for="last_name_id">Last name:</label> <input type="text" name="last_name" id="last_name_id" /></li> -<li><label for="birthday_id">Birthday:</label> <input type="text" name="birthday" id="birthday_id" /></li> ->>> print p.as_p() -<p><label for="first_name_id">First name:</label> <input type="text" name="first_name" id="first_name_id" /></p> -<p><label for="last_name_id">Last name:</label> <input type="text" name="last_name" id="last_name_id" /></p> -<p><label for="birthday_id">Birthday:</label> <input type="text" name="birthday" id="birthday_id" /></p> - -If auto_id is any True value whose str() does not contain '%s', the "id" -attribute will be the name of the field. ->>> p = Person(auto_id=True) ->>> print p.as_ul() -<li><label for="first_name">First name:</label> <input type="text" name="first_name" id="first_name" /></li> -<li><label for="last_name">Last name:</label> <input type="text" name="last_name" id="last_name" /></li> -<li><label for="birthday">Birthday:</label> <input type="text" name="birthday" id="birthday" /></li> - -If auto_id is any False value, an "id" attribute won't be output unless it -was manually entered. ->>> p = Person(auto_id=False) ->>> print p.as_ul() -<li>First name: <input type="text" name="first_name" /></li> -<li>Last name: <input type="text" name="last_name" /></li> -<li>Birthday: <input type="text" name="birthday" /></li> - -In this example, auto_id is False, but the "id" attribute for the "first_name" -field is given. Also note that field gets a <label>, while the others don't. ->>> class PersonNew(Form): -... first_name = CharField(widget=TextInput(attrs={'id': 'first_name_id'})) -... last_name = CharField() -... birthday = DateField() ->>> p = PersonNew(auto_id=False) ->>> print p.as_ul() -<li><label for="first_name_id">First name:</label> <input type="text" id="first_name_id" name="first_name" /></li> -<li>Last name: <input type="text" name="last_name" /></li> -<li>Birthday: <input type="text" name="birthday" /></li> - -If the "id" attribute is specified in the Form and auto_id is True, the "id" -attribute in the Form gets precedence. ->>> p = PersonNew(auto_id=True) ->>> print p.as_ul() -<li><label for="first_name_id">First name:</label> <input type="text" id="first_name_id" name="first_name" /></li> -<li><label for="last_name">Last name:</label> <input type="text" name="last_name" id="last_name" /></li> -<li><label for="birthday">Birthday:</label> <input type="text" name="birthday" id="birthday" /></li> - ->>> class SignupForm(Form): -... email = EmailField() -... get_spam = BooleanField() ->>> f = SignupForm(auto_id=False) ->>> print f['email'] -<input type="text" name="email" /> ->>> print f['get_spam'] -<input type="checkbox" name="get_spam" /> - ->>> f = SignupForm({'email': 'test@example.com', 'get_spam': True}, auto_id=False) ->>> print f['email'] -<input type="text" name="email" value="test@example.com" /> ->>> print f['get_spam'] -<input checked="checked" type="checkbox" name="get_spam" /> - -Any Field can have a Widget class passed to its constructor: ->>> class ContactForm(Form): -... subject = CharField() -... message = CharField(widget=Textarea) ->>> f = ContactForm(auto_id=False) ->>> print f['subject'] -<input type="text" name="subject" /> ->>> print f['message'] -<textarea rows="10" cols="40" name="message"></textarea> - -as_textarea(), as_text() and as_hidden() are shortcuts for changing the output -widget type: ->>> f['subject'].as_textarea() -u'<textarea rows="10" cols="40" name="subject"></textarea>' ->>> f['message'].as_text() -u'<input type="text" name="message" />' ->>> f['message'].as_hidden() -u'<input type="hidden" name="message" />' - -The 'widget' parameter to a Field can also be an instance: ->>> class ContactForm(Form): -... subject = CharField() -... message = CharField(widget=Textarea(attrs={'rows': 80, 'cols': 20})) ->>> f = ContactForm(auto_id=False) ->>> print f['message'] -<textarea rows="80" cols="20" name="message"></textarea> - -Instance-level attrs are *not* carried over to as_textarea(), as_text() and -as_hidden(): ->>> f['message'].as_text() -u'<input type="text" name="message" />' ->>> f = ContactForm({'subject': 'Hello', 'message': 'I love you.'}, auto_id=False) ->>> f['subject'].as_textarea() -u'<textarea rows="10" cols="40" name="subject">Hello</textarea>' ->>> f['message'].as_text() -u'<input type="text" name="message" value="I love you." />' ->>> f['message'].as_hidden() -u'<input type="hidden" name="message" value="I love you." />' - -For a form with a <select>, use ChoiceField: ->>> class FrameworkForm(Form): -... name = CharField() -... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')]) ->>> f = FrameworkForm(auto_id=False) ->>> print f['language'] -<select name="language"> -<option value="P">Python</option> -<option value="J">Java</option> -</select> ->>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False) ->>> print f['language'] -<select name="language"> -<option value="P" selected="selected">Python</option> -<option value="J">Java</option> -</select> - -A subtlety: If one of the choices' value is the empty string and the form is -unbound, then the <option> for the empty-string choice will get selected="selected". ->>> class FrameworkForm(Form): -... name = CharField() -... language = ChoiceField(choices=[('', '------'), ('P', 'Python'), ('J', 'Java')]) ->>> f = FrameworkForm(auto_id=False) ->>> print f['language'] -<select name="language"> -<option value="" selected="selected">------</option> -<option value="P">Python</option> -<option value="J">Java</option> -</select> - -You can specify widget attributes in the Widget constructor. ->>> class FrameworkForm(Form): -... name = CharField() -... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(attrs={'class': 'foo'})) ->>> f = FrameworkForm(auto_id=False) ->>> print f['language'] -<select class="foo" name="language"> -<option value="P">Python</option> -<option value="J">Java</option> -</select> ->>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False) ->>> print f['language'] -<select class="foo" name="language"> -<option value="P" selected="selected">Python</option> -<option value="J">Java</option> -</select> - -When passing a custom widget instance to ChoiceField, note that setting -'choices' on the widget is meaningless. The widget will use the choices -defined on the Field, not the ones defined on the Widget. ->>> class FrameworkForm(Form): -... name = CharField() -... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=Select(choices=[('R', 'Ruby'), ('P', 'Perl')], attrs={'class': 'foo'})) ->>> f = FrameworkForm(auto_id=False) ->>> print f['language'] -<select class="foo" name="language"> -<option value="P">Python</option> -<option value="J">Java</option> -</select> ->>> f = FrameworkForm({'name': 'Django', 'language': 'P'}, auto_id=False) ->>> print f['language'] -<select class="foo" name="language"> -<option value="P" selected="selected">Python</option> -<option value="J">Java</option> -</select> - -You can set a ChoiceField's choices after the fact. ->>> class FrameworkForm(Form): -... name = CharField() -... language = ChoiceField() ->>> f = FrameworkForm(auto_id=False) ->>> print f['language'] -<select name="language"> -</select> ->>> f.fields['language'].choices = [('P', 'Python'), ('J', 'Java')] ->>> print f['language'] -<select name="language"> -<option value="P">Python</option> -<option value="J">Java</option> -</select> - -Add widget=RadioSelect to use that widget with a ChoiceField. ->>> class FrameworkForm(Form): -... name = CharField() -... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect) ->>> f = FrameworkForm(auto_id=False) ->>> print f['language'] -<ul> -<li><label><input type="radio" name="language" value="P" /> Python</label></li> -<li><label><input type="radio" name="language" value="J" /> Java</label></li> -</ul> ->>> print f -<tr><th>Name:</th><td><input type="text" name="name" /></td></tr> -<tr><th>Language:</th><td><ul> -<li><label><input type="radio" name="language" value="P" /> Python</label></li> -<li><label><input type="radio" name="language" value="J" /> Java</label></li> -</ul></td></tr> ->>> print f.as_ul() -<li>Name: <input type="text" name="name" /></li> -<li>Language: <ul> -<li><label><input type="radio" name="language" value="P" /> Python</label></li> -<li><label><input type="radio" name="language" value="J" /> Java</label></li> -</ul></li> - -Regarding auto_id and <label>, RadioSelect is a special case. Each radio button -gets a distinct ID, formed by appending an underscore plus the button's -zero-based index. ->>> f = FrameworkForm(auto_id='id_%s') ->>> print f['language'] -<ul> -<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> -<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> -</ul> - -When RadioSelect is used with auto_id, and the whole form is printed using -either as_table() or as_ul(), the label for the RadioSelect will point to the -ID of the *first* radio button. ->>> print f -<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" /></td></tr> -<tr><th><label for="id_language_0">Language:</label></th><td><ul> -<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> -<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> -</ul></td></tr> ->>> print f.as_ul() -<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li> -<li><label for="id_language_0">Language:</label> <ul> -<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> -<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> -</ul></li> ->>> print f.as_p() -<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p> -<p><label for="id_language_0">Language:</label> <ul> -<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> -<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> -</ul></p> - -MultipleChoiceField is a special case, as its data is required to be a list: ->>> class SongForm(Form): -... name = CharField() -... composers = MultipleChoiceField() ->>> f = SongForm(auto_id=False) ->>> print f['composers'] -<select multiple="multiple" name="composers"> -</select> ->>> class SongForm(Form): -... name = CharField() -... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')]) ->>> f = SongForm(auto_id=False) ->>> print f['composers'] -<select multiple="multiple" name="composers"> -<option value="J">John Lennon</option> -<option value="P">Paul McCartney</option> -</select> ->>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False) ->>> print f['name'] -<input type="text" name="name" value="Yesterday" /> ->>> print f['composers'] -<select multiple="multiple" name="composers"> -<option value="J">John Lennon</option> -<option value="P" selected="selected">Paul McCartney</option> -</select> - -MultipleChoiceField rendered as_hidden() is a special case. Because it can -have multiple values, its as_hidden() renders multiple <input type="hidden"> -tags. ->>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False) ->>> print f['composers'].as_hidden() -<input type="hidden" name="composers" value="P" /> ->>> f = SongForm({'name': 'From Me To You', 'composers': ['P', 'J']}, auto_id=False) ->>> print f['composers'].as_hidden() -<input type="hidden" name="composers" value="P" /> -<input type="hidden" name="composers" value="J" /> - -MultipleChoiceField can also be used with the CheckboxSelectMultiple widget. ->>> class SongForm(Form): -... name = CharField() -... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=CheckboxSelectMultiple) ->>> f = SongForm(auto_id=False) ->>> print f['composers'] -<ul> -<li><label><input type="checkbox" name="composers" value="J" /> John Lennon</label></li> -<li><label><input type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> -</ul> ->>> f = SongForm({'composers': ['J']}, auto_id=False) ->>> print f['composers'] -<ul> -<li><label><input checked="checked" type="checkbox" name="composers" value="J" /> John Lennon</label></li> -<li><label><input type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> -</ul> ->>> f = SongForm({'composers': ['J', 'P']}, auto_id=False) ->>> print f['composers'] -<ul> -<li><label><input checked="checked" type="checkbox" name="composers" value="J" /> John Lennon</label></li> -<li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> -</ul> - -Regarding auto_id, CheckboxSelectMultiple is a special case. Each checkbox -gets a distinct ID, formed by appending an underscore plus the checkbox's -zero-based index. ->>> f = SongForm(auto_id='%s_id') ->>> print f['composers'] -<ul> -<li><label><input type="checkbox" name="composers" value="J" id="composers_id_0" /> John Lennon</label></li> -<li><label><input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li> -</ul> - -Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict -conveniently work with this. ->>> data = {'name': 'Yesterday', 'composers': ['J', 'P']} ->>> f = SongForm(data) ->>> f.errors -{} ->>> from django.http import QueryDict ->>> data = QueryDict('name=Yesterday&composers=J&composers=P') ->>> f = SongForm(data) ->>> f.errors -{} ->>> from django.utils.datastructures import MultiValueDict ->>> data = MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])) ->>> f = SongForm(data) ->>> f.errors -{} - -The MultipleHiddenInput widget renders multiple values as hidden fields. ->>> class SongFormHidden(Form): -... name = CharField() -... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput) ->>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False) ->>> print f.as_ul() -<li>Name: <input type="text" name="name" value="Yesterday" /><input type="hidden" name="composers" value="J" /> -<input type="hidden" name="composers" value="P" /></li> - -When using CheckboxSelectMultiple, the framework expects a list of input and -returns a list of input. ->>> f = SongForm({'name': 'Yesterday'}, auto_id=False) ->>> f.errors -{'composers': [u'This field is required.']} ->>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False) ->>> f.errors -{} ->>> f.cleaned_data -{'composers': [u'J'], 'name': u'Yesterday'} ->>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False) ->>> f.errors -{} ->>> f.cleaned_data -{'composers': [u'J', u'P'], 'name': u'Yesterday'} - -Validation errors are HTML-escaped when output as HTML. ->>> class EscapingForm(Form): -... special_name = CharField() -... def clean_special_name(self): -... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name']) - ->>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False) ->>> print f -<tr><th>Special name:</th><td><ul class="errorlist"><li>Something's wrong with 'Nothing to escape'</li></ul><input type="text" name="special_name" value="Nothing to escape" /></td></tr> ->>> f = EscapingForm({'special_name': "Should escape < & > and <script>alert('xss')</script>"}, auto_id=False) ->>> print f -<tr><th>Special name:</th><td><ul class="errorlist"><li>Something's wrong with 'Should escape < & > and <script>alert('xss')</script>'</li></ul><input type="text" name="special_name" value="Should escape < & > and <script>alert('xss')</script>" /></td></tr> - -# Validating multiple fields in relation to another ########################### - -There are a couple of ways to do multiple-field validation. If you want the -validation message to be associated with a particular field, implement the -clean_XXX() method on the Form, where XXX is the field name. As in -Field.clean(), the clean_XXX() method should return the cleaned value. In the -clean_XXX() method, you have access to self.cleaned_data, which is a dictionary -of all the data that has been cleaned *so far*, in order by the fields, -including the current field (e.g., the field XXX if you're in clean_XXX()). ->>> class UserRegistration(Form): -... username = CharField(max_length=10) -... password1 = CharField(widget=PasswordInput) -... password2 = CharField(widget=PasswordInput) -... def clean_password2(self): -... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: -... raise ValidationError(u'Please make sure your passwords match.') -... return self.cleaned_data['password2'] ->>> f = UserRegistration(auto_id=False) ->>> f.errors -{} ->>> f = UserRegistration({}, auto_id=False) ->>> f.errors -{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']} ->>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False) ->>> f.errors -{'password2': [u'Please make sure your passwords match.']} ->>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) ->>> f.errors -{} ->>> f.cleaned_data -{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} - -Another way of doing multiple-field validation is by implementing the -Form's clean() method. If you do this, any ValidationError raised by that -method will not be associated with a particular field; it will have a -special-case association with the field named '__all__'. -Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of -all the fields/values that have *not* raised a ValidationError. Also note -Form.clean() is required to return a dictionary of all clean data. ->>> class UserRegistration(Form): -... username = CharField(max_length=10) -... password1 = CharField(widget=PasswordInput) -... password2 = CharField(widget=PasswordInput) -... def clean(self): -... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: -... raise ValidationError(u'Please make sure your passwords match.') -... return self.cleaned_data ->>> f = UserRegistration(auto_id=False) ->>> f.errors -{} ->>> f = UserRegistration({}, auto_id=False) ->>> print f.as_table() -<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr> -<tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr> -<tr><th>Password2:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" /></td></tr> ->>> f.errors -{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']} ->>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False) ->>> f.errors -{'__all__': [u'Please make sure your passwords match.']} ->>> print f.as_table() -<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> -<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr> -<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr> -<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr> ->>> print f.as_ul() -<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li> -<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li> -<li>Password1: <input type="password" name="password1" value="foo" /></li> -<li>Password2: <input type="password" name="password2" value="bar" /></li> ->>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) ->>> f.errors -{} ->>> f.cleaned_data -{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} - -# Dynamic construction ######################################################## - -It's possible to construct a Form dynamically by adding to the self.fields -dictionary in __init__(). Don't forget to call Form.__init__() within the -subclass' __init__(). ->>> class Person(Form): -... first_name = CharField() -... last_name = CharField() -... def __init__(self, *args, **kwargs): -... super(Person, self).__init__(*args, **kwargs) -... self.fields['birthday'] = DateField() ->>> p = Person(auto_id=False) ->>> print p -<tr><th>First name:</th><td><input type="text" name="first_name" /></td></tr> -<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr> -<tr><th>Birthday:</th><td><input type="text" name="birthday" /></td></tr> - -Instances of a dynamic Form do not persist fields from one Form instance to -the next. ->>> class MyForm(Form): -... def __init__(self, data=None, auto_id=False, field_list=[]): -... Form.__init__(self, data, auto_id=auto_id) -... for field in field_list: -... self.fields[field[0]] = field[1] ->>> field_list = [('field1', CharField()), ('field2', CharField())] ->>> my_form = MyForm(field_list=field_list) ->>> print my_form -<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr> -<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> ->>> field_list = [('field3', CharField()), ('field4', CharField())] ->>> my_form = MyForm(field_list=field_list) ->>> print my_form -<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr> -<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> - ->>> class MyForm(Form): -... default_field_1 = CharField() -... default_field_2 = CharField() -... def __init__(self, data=None, auto_id=False, field_list=[]): -... Form.__init__(self, data, auto_id=auto_id) -... for field in field_list: -... self.fields[field[0]] = field[1] ->>> field_list = [('field1', CharField()), ('field2', CharField())] ->>> my_form = MyForm(field_list=field_list) ->>> print my_form -<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr> -<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr> -<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr> -<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> ->>> field_list = [('field3', CharField()), ('field4', CharField())] ->>> my_form = MyForm(field_list=field_list) ->>> print my_form -<tr><th>Default field 1:</th><td><input type="text" name="default_field_1" /></td></tr> -<tr><th>Default field 2:</th><td><input type="text" name="default_field_2" /></td></tr> -<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr> -<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> - -Similarly, changes to field attributes do not persist from one Form instance -to the next. ->>> class Person(Form): -... first_name = CharField(required=False) -... last_name = CharField(required=False) -... def __init__(self, names_required=False, *args, **kwargs): -... super(Person, self).__init__(*args, **kwargs) -... if names_required: -... self.fields['first_name'].required = True -... self.fields['first_name'].widget.attrs['class'] = 'required' -... self.fields['last_name'].required = True -... self.fields['last_name'].widget.attrs['class'] = 'required' ->>> f = Person(names_required=False) ->>> f['first_name'].field.required, f['last_name'].field.required -(False, False) ->>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs -({}, {}) ->>> f = Person(names_required=True) ->>> f['first_name'].field.required, f['last_name'].field.required -(True, True) ->>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs -({'class': 'required'}, {'class': 'required'}) ->>> f = Person(names_required=False) ->>> f['first_name'].field.required, f['last_name'].field.required -(False, False) ->>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs -({}, {}) ->>> class Person(Form): -... first_name = CharField(max_length=30) -... last_name = CharField(max_length=30) -... def __init__(self, name_max_length=None, *args, **kwargs): -... super(Person, self).__init__(*args, **kwargs) -... if name_max_length: -... self.fields['first_name'].max_length = name_max_length -... self.fields['last_name'].max_length = name_max_length ->>> f = Person(name_max_length=None) ->>> f['first_name'].field.max_length, f['last_name'].field.max_length -(30, 30) ->>> f = Person(name_max_length=20) ->>> f['first_name'].field.max_length, f['last_name'].field.max_length -(20, 20) ->>> f = Person(name_max_length=None) ->>> f['first_name'].field.max_length, f['last_name'].field.max_length -(30, 30) - -HiddenInput widgets are displayed differently in the as_table(), as_ul() -and as_p() output of a Form -- their verbose names are not displayed, and a -separate row is not displayed. They're displayed in the last row of the -form, directly after that row's form element. ->>> class Person(Form): -... first_name = CharField() -... last_name = CharField() -... hidden_text = CharField(widget=HiddenInput) -... birthday = DateField() ->>> p = Person(auto_id=False) ->>> print p -<tr><th>First name:</th><td><input type="text" name="first_name" /></td></tr> -<tr><th>Last name:</th><td><input type="text" name="last_name" /></td></tr> -<tr><th>Birthday:</th><td><input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></td></tr> ->>> print p.as_ul() -<li>First name: <input type="text" name="first_name" /></li> -<li>Last name: <input type="text" name="last_name" /></li> -<li>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></li> ->>> print p.as_p() -<p>First name: <input type="text" name="first_name" /></p> -<p>Last name: <input type="text" name="last_name" /></p> -<p>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></p> - -With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label. ->>> p = Person(auto_id='id_%s') ->>> print p -<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> -<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> -<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></td></tr> ->>> print p.as_ul() -<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> -<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> -<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></li> ->>> print p.as_p() -<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p> -<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p> -<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></p> - -If a field with a HiddenInput has errors, the as_table() and as_ul() output -will include the error message(s) with the text "(Hidden field [fieldname]) " -prepended. This message is displayed at the top of the output, regardless of -its field's order in the form. ->>> p = Person({'first_name': 'John', 'last_name': 'Lennon', 'birthday': '1940-10-9'}, auto_id=False) ->>> print p -<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></td></tr> -<tr><th>First name:</th><td><input type="text" name="first_name" value="John" /></td></tr> -<tr><th>Last name:</th><td><input type="text" name="last_name" value="Lennon" /></td></tr> -<tr><th>Birthday:</th><td><input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></td></tr> ->>> print p.as_ul() -<li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li> -<li>First name: <input type="text" name="first_name" value="John" /></li> -<li>Last name: <input type="text" name="last_name" value="Lennon" /></li> -<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></li> ->>> print p.as_p() -<ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul> -<p>First name: <input type="text" name="first_name" value="John" /></p> -<p>Last name: <input type="text" name="last_name" value="Lennon" /></p> -<p>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></p> - -A corner case: It's possible for a form to have only HiddenInputs. ->>> class TestForm(Form): -... foo = CharField(widget=HiddenInput) -... bar = CharField(widget=HiddenInput) ->>> p = TestForm(auto_id=False) ->>> print p.as_table() -<input type="hidden" name="foo" /><input type="hidden" name="bar" /> ->>> print p.as_ul() -<input type="hidden" name="foo" /><input type="hidden" name="bar" /> ->>> print p.as_p() -<input type="hidden" name="foo" /><input type="hidden" name="bar" /> - -A Form's fields are displayed in the same order in which they were defined. ->>> class TestForm(Form): -... field1 = CharField() -... field2 = CharField() -... field3 = CharField() -... field4 = CharField() -... field5 = CharField() -... field6 = CharField() -... field7 = CharField() -... field8 = CharField() -... field9 = CharField() -... field10 = CharField() -... field11 = CharField() -... field12 = CharField() -... field13 = CharField() -... field14 = CharField() ->>> p = TestForm(auto_id=False) ->>> print p -<tr><th>Field1:</th><td><input type="text" name="field1" /></td></tr> -<tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> -<tr><th>Field3:</th><td><input type="text" name="field3" /></td></tr> -<tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> -<tr><th>Field5:</th><td><input type="text" name="field5" /></td></tr> -<tr><th>Field6:</th><td><input type="text" name="field6" /></td></tr> -<tr><th>Field7:</th><td><input type="text" name="field7" /></td></tr> -<tr><th>Field8:</th><td><input type="text" name="field8" /></td></tr> -<tr><th>Field9:</th><td><input type="text" name="field9" /></td></tr> -<tr><th>Field10:</th><td><input type="text" name="field10" /></td></tr> -<tr><th>Field11:</th><td><input type="text" name="field11" /></td></tr> -<tr><th>Field12:</th><td><input type="text" name="field12" /></td></tr> -<tr><th>Field13:</th><td><input type="text" name="field13" /></td></tr> -<tr><th>Field14:</th><td><input type="text" name="field14" /></td></tr> - -Some Field classes have an effect on the HTML attributes of their associated -Widget. If you set max_length in a CharField and its associated widget is -either a TextInput or PasswordInput, then the widget's rendered HTML will -include the "maxlength" attribute. ->>> class UserRegistration(Form): -... username = CharField(max_length=10) # uses TextInput by default -... password = CharField(max_length=10, widget=PasswordInput) -... realname = CharField(max_length=10, widget=TextInput) # redundantly define widget, just to test -... address = CharField() # no max_length defined here ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" maxlength="10" /></li> -<li>Password: <input type="password" name="password" maxlength="10" /></li> -<li>Realname: <input type="text" name="realname" maxlength="10" /></li> -<li>Address: <input type="text" name="address" /></li> - -If you specify a custom "attrs" that includes the "maxlength" attribute, -the Field's max_length attribute will override whatever "maxlength" you specify -in "attrs". ->>> class UserRegistration(Form): -... username = CharField(max_length=10, widget=TextInput(attrs={'maxlength': 20})) -... password = CharField(max_length=10, widget=PasswordInput) ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" maxlength="10" /></li> -<li>Password: <input type="password" name="password" maxlength="10" /></li> - -# Specifying labels ########################################################### - -You can specify the label for a field by using the 'label' argument to a Field -class. If you don't specify 'label', Django will use the field name with -underscores converted to spaces, and the initial letter capitalized. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, label='Your username') -... password1 = CharField(widget=PasswordInput) -... password2 = CharField(widget=PasswordInput, label='Password (again)') ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Your username: <input type="text" name="username" maxlength="10" /></li> -<li>Password1: <input type="password" name="password1" /></li> -<li>Password (again): <input type="password" name="password2" /></li> - -Labels for as_* methods will only end in a colon if they don't end in other -punctuation already. ->>> class Questions(Form): -... q1 = CharField(label='The first question') -... q2 = CharField(label='What is your name?') -... q3 = CharField(label='The answer to life is:') -... q4 = CharField(label='Answer this question!') -... q5 = CharField(label='The last question. Period.') ->>> print Questions(auto_id=False).as_p() -<p>The first question: <input type="text" name="q1" /></p> -<p>What is your name? <input type="text" name="q2" /></p> -<p>The answer to life is: <input type="text" name="q3" /></p> -<p>Answer this question! <input type="text" name="q4" /></p> -<p>The last question. Period. <input type="text" name="q5" /></p> ->>> print Questions().as_p() -<p><label for="id_q1">The first question:</label> <input type="text" name="q1" id="id_q1" /></p> -<p><label for="id_q2">What is your name?</label> <input type="text" name="q2" id="id_q2" /></p> -<p><label for="id_q3">The answer to life is:</label> <input type="text" name="q3" id="id_q3" /></p> -<p><label for="id_q4">Answer this question!</label> <input type="text" name="q4" id="id_q4" /></p> -<p><label for="id_q5">The last question. Period.</label> <input type="text" name="q5" id="id_q5" /></p> - -A label can be a Unicode object or a bytestring with special characters. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, label='ŠĐĆŽćžšđ') -... password = CharField(widget=PasswordInput, label=u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') ->>> p = UserRegistration(auto_id=False) ->>> p.as_ul() -u'<li>\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: <input type="text" name="username" maxlength="10" /></li>\n<li>\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: <input type="password" name="password" /></li>' - -If a label is set to the empty string for a field, that field won't get a label. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, label='') -... password = CharField(widget=PasswordInput) ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li> <input type="text" name="username" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration(auto_id='id_%s') ->>> print p.as_ul() -<li> <input id="id_username" type="text" name="username" maxlength="10" /></li> -<li><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></li> - -If label is None, Django will auto-create the label from the field name. This -is default behavior. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, label=None) -... password = CharField(widget=PasswordInput) ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration(auto_id='id_%s') ->>> print p.as_ul() -<li><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></li> -<li><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></li> - - -# Label Suffix ################################################################ - -You can specify the 'label_suffix' argument to a Form class to modify the -punctuation symbol used at the end of a label. By default, the colon (:) is -used, and is only appended to the label if the label doesn't already end with a -punctuation symbol: ., !, ? or :. If you specify a different suffix, it will -be appended regardless of the last character of the label. - ->>> class FavoriteForm(Form): -... color = CharField(label='Favorite color?') -... animal = CharField(label='Favorite animal') -... ->>> f = FavoriteForm(auto_id=False) ->>> print f.as_ul() -<li>Favorite color? <input type="text" name="color" /></li> -<li>Favorite animal: <input type="text" name="animal" /></li> ->>> f = FavoriteForm(auto_id=False, label_suffix='?') ->>> print f.as_ul() -<li>Favorite color? <input type="text" name="color" /></li> -<li>Favorite animal? <input type="text" name="animal" /></li> ->>> f = FavoriteForm(auto_id=False, label_suffix='') ->>> print f.as_ul() -<li>Favorite color? <input type="text" name="color" /></li> -<li>Favorite animal <input type="text" name="animal" /></li> ->>> f = FavoriteForm(auto_id=False, label_suffix=u'\u2192') ->>> f.as_ul() -u'<li>Favorite color? <input type="text" name="color" /></li>\n<li>Favorite animal\u2192 <input type="text" name="animal" /></li>' - - - -# Initial data ################################################################ - -You can specify initial data for a field by using the 'initial' argument to a -Field class. This initial data is displayed when a Form is rendered with *no* -data. It is not displayed when a Form is rendered with any data (including an -empty dictionary). Also, the initial value is *not* used if data for a -particular required field isn't provided. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, initial='django') -... password = CharField(widget=PasswordInput) - -Here, we're not submitting any data, so the initial value will be displayed. ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> - -Here, we're submitting data, so the initial value will *not* be displayed. ->>> p = UserRegistration({}, auto_id=False) ->>> print p.as_ul() -<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration({'username': u''}, auto_id=False) ->>> print p.as_ul() -<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration({'username': u'foo'}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> - -An 'initial' value is *not* used as a fallback if data is not provided. In this -example, we don't provide a value for 'username', and the form raises a -validation error rather than using the initial value for 'username'. ->>> p = UserRegistration({'password': 'secret'}) ->>> p.errors -{'username': [u'This field is required.']} ->>> p.is_valid() -False - -# Dynamic initial data ######################################################## - -The previous technique dealt with "hard-coded" initial data, but it's also -possible to specify initial data after you've already created the Form class -(i.e., at runtime). Use the 'initial' parameter to the Form constructor. This -should be a dictionary containing initial values for one or more fields in the -form, keyed by field name. - ->>> class UserRegistration(Form): -... username = CharField(max_length=10) -... password = CharField(widget=PasswordInput) - -Here, we're not submitting any data, so the initial value will be displayed. ->>> p = UserRegistration(initial={'username': 'django'}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration(initial={'username': 'stephane'}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> - -The 'initial' parameter is meaningless if you pass data. ->>> p = UserRegistration({}, initial={'username': 'django'}, auto_id=False) ->>> print p.as_ul() -<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration({'username': u''}, initial={'username': 'django'}, auto_id=False) ->>> print p.as_ul() -<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration({'username': u'foo'}, initial={'username': 'django'}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> - -A dynamic 'initial' value is *not* used as a fallback if data is not provided. -In this example, we don't provide a value for 'username', and the form raises a -validation error rather than using the initial value for 'username'. ->>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'}) ->>> p.errors -{'username': [u'This field is required.']} ->>> p.is_valid() -False - -If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(), -then the latter will get precedence. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, initial='django') -... password = CharField(widget=PasswordInput) ->>> p = UserRegistration(initial={'username': 'babik'}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> - -# Callable initial data ######################################################## - -The previous technique dealt with raw values as initial data, but it's also -possible to specify callable data. - ->>> class UserRegistration(Form): -... username = CharField(max_length=10) -... password = CharField(widget=PasswordInput) - -We need to define functions that get called later. ->>> def initial_django(): -... return 'django' ->>> def initial_stephane(): -... return 'stephane' - -Here, we're not submitting any data, so the initial value will be displayed. ->>> p = UserRegistration(initial={'username': initial_django}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> - -The 'initial' parameter is meaningless if you pass data. ->>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False) ->>> print p.as_ul() -<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False) ->>> print p.as_ul() -<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration({'username': u'foo'}, initial={'username': initial_django}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> - -A callable 'initial' value is *not* used as a fallback if data is not provided. -In this example, we don't provide a value for 'username', and the form raises a -validation error rather than using the initial value for 'username'. ->>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django}) ->>> p.errors -{'username': [u'This field is required.']} ->>> p.is_valid() -False - -If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(), -then the latter will get precedence. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, initial=initial_django) -... password = CharField(widget=PasswordInput) ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> ->>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> -<li>Password: <input type="password" name="password" /></li> - -# Help text ################################################################### - -You can specify descriptive text for a field by using the 'help_text' argument -to a Field class. This help text is displayed when a Form is rendered. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, help_text='e.g., user@example.com') -... password = CharField(widget=PasswordInput, help_text='Choose wisely.') ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li> -<li>Password: <input type="password" name="password" /> Choose wisely.</li> ->>> print p.as_p() -<p>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</p> -<p>Password: <input type="password" name="password" /> Choose wisely.</p> ->>> print p.as_table() -<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br />e.g., user@example.com</td></tr> -<tr><th>Password:</th><td><input type="password" name="password" /><br />Choose wisely.</td></tr> - -The help text is displayed whether or not data is provided for the form. ->>> p = UserRegistration({'username': u'foo'}, auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> e.g., user@example.com</li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> Choose wisely.</li> - -help_text is not displayed for hidden fields. It can be used for documentation -purposes, though. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, help_text='e.g., user@example.com') -... password = CharField(widget=PasswordInput) -... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination') ->>> p = UserRegistration(auto_id=False) ->>> print p.as_ul() -<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li> -<li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li> - -Help text can include arbitrary Unicode characters. ->>> class UserRegistration(Form): -... username = CharField(max_length=10, help_text='ŠĐĆŽćžšđ') ->>> p = UserRegistration(auto_id=False) ->>> p.as_ul() -u'<li>Username: <input type="text" name="username" maxlength="10" /> \u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</li>' - -# Subclassing forms ########################################################### - -You can subclass a Form to add fields. The resulting form subclass will have -all of the fields of the parent Form, plus whichever fields you define in the -subclass. ->>> class Person(Form): -... first_name = CharField() -... last_name = CharField() -... birthday = DateField() ->>> class Musician(Person): -... instrument = CharField() ->>> p = Person(auto_id=False) ->>> print p.as_ul() -<li>First name: <input type="text" name="first_name" /></li> -<li>Last name: <input type="text" name="last_name" /></li> -<li>Birthday: <input type="text" name="birthday" /></li> ->>> m = Musician(auto_id=False) ->>> print m.as_ul() -<li>First name: <input type="text" name="first_name" /></li> -<li>Last name: <input type="text" name="last_name" /></li> -<li>Birthday: <input type="text" name="birthday" /></li> -<li>Instrument: <input type="text" name="instrument" /></li> - -Yes, you can subclass multiple forms. The fields are added in the order in -which the parent classes are listed. ->>> class Person(Form): -... first_name = CharField() -... last_name = CharField() -... birthday = DateField() ->>> class Instrument(Form): -... instrument = CharField() ->>> class Beatle(Person, Instrument): -... haircut_type = CharField() ->>> b = Beatle(auto_id=False) ->>> print b.as_ul() -<li>First name: <input type="text" name="first_name" /></li> -<li>Last name: <input type="text" name="last_name" /></li> -<li>Birthday: <input type="text" name="birthday" /></li> -<li>Instrument: <input type="text" name="instrument" /></li> -<li>Haircut type: <input type="text" name="haircut_type" /></li> - -# Forms with prefixes ######################################################### - -Sometimes it's necessary to have multiple forms display on the same HTML page, -or multiple copies of the same form. We can accomplish this with form prefixes. -Pass the keyword argument 'prefix' to the Form constructor to use this feature. -This value will be prepended to each HTML form field name. One way to think -about this is "namespaces for HTML forms". Notice that in the data argument, -each field's key has the prefix, in this case 'person1', prepended to the -actual field name. ->>> class Person(Form): -... first_name = CharField() -... last_name = CharField() -... birthday = DateField() ->>> data = { -... 'person1-first_name': u'John', -... 'person1-last_name': u'Lennon', -... 'person1-birthday': u'1940-10-9' -... } ->>> p = Person(data, prefix='person1') ->>> print p.as_ul() -<li><label for="id_person1-first_name">First name:</label> <input type="text" name="person1-first_name" value="John" id="id_person1-first_name" /></li> -<li><label for="id_person1-last_name">Last name:</label> <input type="text" name="person1-last_name" value="Lennon" id="id_person1-last_name" /></li> -<li><label for="id_person1-birthday">Birthday:</label> <input type="text" name="person1-birthday" value="1940-10-9" id="id_person1-birthday" /></li> ->>> print p['first_name'] -<input type="text" name="person1-first_name" value="John" id="id_person1-first_name" /> ->>> print p['last_name'] -<input type="text" name="person1-last_name" value="Lennon" id="id_person1-last_name" /> ->>> print p['birthday'] -<input type="text" name="person1-birthday" value="1940-10-9" id="id_person1-birthday" /> ->>> p.errors -{} ->>> p.is_valid() -True ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} - -Let's try submitting some bad data to make sure form.errors and field.errors -work as expected. ->>> data = { -... 'person1-first_name': u'', -... 'person1-last_name': u'', -... 'person1-birthday': u'' -... } ->>> p = Person(data, prefix='person1') ->>> p.errors -{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} ->>> p['first_name'].errors -[u'This field is required.'] ->>> p['person1-first_name'].errors -Traceback (most recent call last): -... -KeyError: "Key 'person1-first_name' not found in Form" - -In this example, the data doesn't have a prefix, but the form requires it, so -the form doesn't "see" the fields. ->>> data = { -... 'first_name': u'John', -... 'last_name': u'Lennon', -... 'birthday': u'1940-10-9' -... } ->>> p = Person(data, prefix='person1') ->>> p.errors -{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} - -With prefixes, a single data dictionary can hold data for multiple instances -of the same form. ->>> data = { -... 'person1-first_name': u'John', -... 'person1-last_name': u'Lennon', -... 'person1-birthday': u'1940-10-9', -... 'person2-first_name': u'Jim', -... 'person2-last_name': u'Morrison', -... 'person2-birthday': u'1943-12-8' -... } ->>> p1 = Person(data, prefix='person1') ->>> p1.is_valid() -True ->>> p1.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} ->>> p2 = Person(data, prefix='person2') ->>> p2.is_valid() -True ->>> p2.cleaned_data -{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)} - -By default, forms append a hyphen between the prefix and the field name, but a -form can alter that behavior by implementing the add_prefix() method. This -method takes a field name and returns the prefixed field, according to -self.prefix. ->>> class Person(Form): -... first_name = CharField() -... last_name = CharField() -... birthday = DateField() -... def add_prefix(self, field_name): -... return self.prefix and '%s-prefix-%s' % (self.prefix, field_name) or field_name ->>> p = Person(prefix='foo') ->>> print p.as_ul() -<li><label for="id_foo-prefix-first_name">First name:</label> <input type="text" name="foo-prefix-first_name" id="id_foo-prefix-first_name" /></li> -<li><label for="id_foo-prefix-last_name">Last name:</label> <input type="text" name="foo-prefix-last_name" id="id_foo-prefix-last_name" /></li> -<li><label for="id_foo-prefix-birthday">Birthday:</label> <input type="text" name="foo-prefix-birthday" id="id_foo-prefix-birthday" /></li> ->>> data = { -... 'foo-prefix-first_name': u'John', -... 'foo-prefix-last_name': u'Lennon', -... 'foo-prefix-birthday': u'1940-10-9' -... } ->>> p = Person(data, prefix='foo') ->>> p.is_valid() -True ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} - -# Forms with NullBooleanFields ################################################ - -NullBooleanField is a bit of a special case because its presentation (widget) -is different than its data. This is handled transparently, though. - ->>> class Person(Form): -... name = CharField() -... is_cool = NullBooleanField() ->>> p = Person({'name': u'Joe'}, auto_id=False) ->>> print p['is_cool'] -<select name="is_cool"> -<option value="1" selected="selected">Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> -</select> ->>> p = Person({'name': u'Joe', 'is_cool': u'1'}, auto_id=False) ->>> print p['is_cool'] -<select name="is_cool"> -<option value="1" selected="selected">Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> -</select> ->>> p = Person({'name': u'Joe', 'is_cool': u'2'}, auto_id=False) ->>> print p['is_cool'] -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected="selected">Yes</option> -<option value="3">No</option> -</select> ->>> p = Person({'name': u'Joe', 'is_cool': u'3'}, auto_id=False) ->>> print p['is_cool'] -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected="selected">No</option> -</select> ->>> p = Person({'name': u'Joe', 'is_cool': True}, auto_id=False) ->>> print p['is_cool'] -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected="selected">Yes</option> -<option value="3">No</option> -</select> ->>> p = Person({'name': u'Joe', 'is_cool': False}, auto_id=False) ->>> print p['is_cool'] -<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected="selected">No</option> -</select> - -# Forms with FileFields ################################################ - -FileFields are a special case because they take their data from the request.FILES, -not request.POST. - ->>> class FileForm(Form): -... file1 = FileField() ->>> f = FileForm(auto_id=False) ->>> print f -<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> - ->>> f = FileForm(data={}, files={}, auto_id=False) ->>> print f -<tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr> - ->>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False) ->>> print f -<tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr> - ->>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False) ->>> print f -<tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr> - ->>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False) ->>> print f -<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr> ->>> f.is_valid() -True - -# Basic form processing in a view ############################################# - ->>> from django.template import Template, Context ->>> class UserRegistration(Form): -... username = CharField(max_length=10) -... password1 = CharField(widget=PasswordInput) -... password2 = CharField(widget=PasswordInput) -... def clean(self): -... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: -... raise ValidationError(u'Please make sure your passwords match.') -... return self.cleaned_data ->>> def my_function(method, post_data): -... if method == 'POST': -... form = UserRegistration(post_data, auto_id=False) -... else: -... form = UserRegistration(auto_id=False) -... if form.is_valid(): -... return 'VALID: %r' % form.cleaned_data -... t = Template('<form action="" method="post">\n<table>\n{{ form }}\n</table>\n<input type="submit" />\n</form>') -... return t.render(Context({'form': form})) - -Case 1: GET (an empty form, with no errors). ->>> print my_function('GET', {}) -<form action="" method="post"> -<table> -<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /></td></tr> -<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr> -<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr> -</table> -<input type="submit" /> -</form> - -Case 2: POST with erroneous data (a redisplayed form, with errors). ->>> print my_function('POST', {'username': 'this-is-a-long-username', 'password1': 'foo', 'password2': 'bar'}) -<form action="" method="post"> -<table> -<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> -<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr> -<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr> -<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr> -</table> -<input type="submit" /> -</form> - -Case 3: POST with valid data (the success message). ->>> print my_function('POST', {'username': 'adrian', 'password1': 'secret', 'password2': 'secret'}) -VALID: {'username': u'adrian', 'password1': u'secret', 'password2': u'secret'} - -# Some ideas for using templates with forms ################################### - ->>> class UserRegistration(Form): -... username = CharField(max_length=10, help_text="Good luck picking a username that doesn't already exist.") -... password1 = CharField(widget=PasswordInput) -... password2 = CharField(widget=PasswordInput) -... def clean(self): -... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: -... raise ValidationError(u'Please make sure your passwords match.') -... return self.cleaned_data - -You have full flexibility in displaying form fields in a template. Just pass a -Form instance to the template, and use "dot" access to refer to individual -fields. Note, however, that this flexibility comes with the responsibility of -displaying all the errors, including any that might not be associated with a -particular field. ->>> t = Template('''<form action=""> -... {{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p> -... {{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p> -... {{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p> -... <input type="submit" /> -... </form>''') ->>> print t.render(Context({'form': UserRegistration(auto_id=False)})) -<form action=""> -<p><label>Your username: <input type="text" name="username" maxlength="10" /></label></p> -<p><label>Password: <input type="password" name="password1" /></label></p> -<p><label>Password (again): <input type="password" name="password2" /></label></p> -<input type="submit" /> -</form> ->>> print t.render(Context({'form': UserRegistration({'username': 'django'}, auto_id=False)})) -<form action=""> -<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> -<ul class="errorlist"><li>This field is required.</li></ul><p><label>Password: <input type="password" name="password1" /></label></p> -<ul class="errorlist"><li>This field is required.</li></ul><p><label>Password (again): <input type="password" name="password2" /></label></p> -<input type="submit" /> -</form> - -Use form.[field].label to output a field's label. You can specify the label for -a field by using the 'label' argument to a Field class. If you don't specify -'label', Django will use the field name with underscores converted to spaces, -and the initial letter capitalized. ->>> t = Template('''<form action=""> -... <p><label>{{ form.username.label }}: {{ form.username }}</label></p> -... <p><label>{{ form.password1.label }}: {{ form.password1 }}</label></p> -... <p><label>{{ form.password2.label }}: {{ form.password2 }}</label></p> -... <input type="submit" /> -... </form>''') ->>> print t.render(Context({'form': UserRegistration(auto_id=False)})) -<form action=""> -<p><label>Username: <input type="text" name="username" maxlength="10" /></label></p> -<p><label>Password1: <input type="password" name="password1" /></label></p> -<p><label>Password2: <input type="password" name="password2" /></label></p> -<input type="submit" /> -</form> - -User form.[field].label_tag to output a field's label with a <label> tag -wrapped around it, but *only* if the given field has an "id" attribute. -Recall from above that passing the "auto_id" argument to a Form gives each -field an "id" attribute. ->>> t = Template('''<form action=""> -... <p>{{ form.username.label_tag }}: {{ form.username }}</p> -... <p>{{ form.password1.label_tag }}: {{ form.password1 }}</p> -... <p>{{ form.password2.label_tag }}: {{ form.password2 }}</p> -... <input type="submit" /> -... </form>''') ->>> print t.render(Context({'form': UserRegistration(auto_id=False)})) -<form action=""> -<p>Username: <input type="text" name="username" maxlength="10" /></p> -<p>Password1: <input type="password" name="password1" /></p> -<p>Password2: <input type="password" name="password2" /></p> -<input type="submit" /> -</form> ->>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')})) -<form action=""> -<p><label for="id_username">Username</label>: <input id="id_username" type="text" name="username" maxlength="10" /></p> -<p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p> -<p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p> -<input type="submit" /> -</form> - -User form.[field].help_text to output a field's help text. If the given field -does not have help text, nothing will be output. ->>> t = Template('''<form action=""> -... <p>{{ form.username.label_tag }}: {{ form.username }}<br />{{ form.username.help_text }}</p> -... <p>{{ form.password1.label_tag }}: {{ form.password1 }}</p> -... <p>{{ form.password2.label_tag }}: {{ form.password2 }}</p> -... <input type="submit" /> -... </form>''') ->>> print t.render(Context({'form': UserRegistration(auto_id=False)})) -<form action=""> -<p>Username: <input type="text" name="username" maxlength="10" /><br />Good luck picking a username that doesn't already exist.</p> -<p>Password1: <input type="password" name="password1" /></p> -<p>Password2: <input type="password" name="password2" /></p> -<input type="submit" /> -</form> ->>> Template('{{ form.password1.help_text }}').render(Context({'form': UserRegistration(auto_id=False)})) -u'' - -The label_tag() method takes an optional attrs argument: a dictionary of HTML -attributes to add to the <label> tag. ->>> f = UserRegistration(auto_id='id_%s') ->>> for bf in f: -... print bf.label_tag(attrs={'class': 'pretty'}) -<label for="id_username" class="pretty">Username</label> -<label for="id_password1" class="pretty">Password1</label> -<label for="id_password2" class="pretty">Password2</label> - -To display the errors that aren't associated with a particular field -- e.g., -the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the -template. If used on its own, it is displayed as a <ul> (or an empty string, if -the list of errors is empty). You can also use it in {% if %} statements. ->>> t = Template('''<form action=""> -... {{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p> -... {{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p> -... {{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p> -... <input type="submit" /> -... </form>''') ->>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)})) -<form action=""> -<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> -<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> -<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> -<input type="submit" /> -</form> ->>> t = Template('''<form action=""> -... {{ form.non_field_errors }} -... {{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p> -... {{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p> -... {{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p> -... <input type="submit" /> -... </form>''') ->>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)})) -<form action=""> -<ul class="errorlist"><li>Please make sure your passwords match.</li></ul> -<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> -<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> -<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> -<input type="submit" /> -</form> - -############### -# Extra stuff # -############### - -The newforms library comes with some extra, higher-level Field and Widget -classes that demonstrate some of the library's abilities. - -# SelectDateWidget ############################################################ - ->>> from django.newforms.extras import SelectDateWidget ->>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016')) ->>> print w.render('mydate', '') -<select name="mydate_month"> -<option value="1">January</option> -<option value="2">February</option> -<option value="3">March</option> -<option value="4">April</option> -<option value="5">May</option> -<option value="6">June</option> -<option value="7">July</option> -<option value="8">August</option> -<option value="9">September</option> -<option value="10">October</option> -<option value="11">November</option> -<option value="12">December</option> -</select> -<select name="mydate_day"> -<option value="1">1</option> -<option value="2">2</option> -<option value="3">3</option> -<option value="4">4</option> -<option value="5">5</option> -<option value="6">6</option> -<option value="7">7</option> -<option value="8">8</option> -<option value="9">9</option> -<option value="10">10</option> -<option value="11">11</option> -<option value="12">12</option> -<option value="13">13</option> -<option value="14">14</option> -<option value="15">15</option> -<option value="16">16</option> -<option value="17">17</option> -<option value="18">18</option> -<option value="19">19</option> -<option value="20">20</option> -<option value="21">21</option> -<option value="22">22</option> -<option value="23">23</option> -<option value="24">24</option> -<option value="25">25</option> -<option value="26">26</option> -<option value="27">27</option> -<option value="28">28</option> -<option value="29">29</option> -<option value="30">30</option> -<option value="31">31</option> -</select> -<select name="mydate_year"> -<option value="2007">2007</option> -<option value="2008">2008</option> -<option value="2009">2009</option> -<option value="2010">2010</option> -<option value="2011">2011</option> -<option value="2012">2012</option> -<option value="2013">2013</option> -<option value="2014">2014</option> -<option value="2015">2015</option> -<option value="2016">2016</option> -</select> ->>> w.render('mydate', None) == w.render('mydate', '') -True ->>> print w.render('mydate', '2010-04-15') -<select name="mydate_month"> -<option value="1">January</option> -<option value="2">February</option> -<option value="3">March</option> -<option value="4" selected="selected">April</option> -<option value="5">May</option> -<option value="6">June</option> -<option value="7">July</option> -<option value="8">August</option> -<option value="9">September</option> -<option value="10">October</option> -<option value="11">November</option> -<option value="12">December</option> -</select> -<select name="mydate_day"> -<option value="1">1</option> -<option value="2">2</option> -<option value="3">3</option> -<option value="4">4</option> -<option value="5">5</option> -<option value="6">6</option> -<option value="7">7</option> -<option value="8">8</option> -<option value="9">9</option> -<option value="10">10</option> -<option value="11">11</option> -<option value="12">12</option> -<option value="13">13</option> -<option value="14">14</option> -<option value="15" selected="selected">15</option> -<option value="16">16</option> -<option value="17">17</option> -<option value="18">18</option> -<option value="19">19</option> -<option value="20">20</option> -<option value="21">21</option> -<option value="22">22</option> -<option value="23">23</option> -<option value="24">24</option> -<option value="25">25</option> -<option value="26">26</option> -<option value="27">27</option> -<option value="28">28</option> -<option value="29">29</option> -<option value="30">30</option> -<option value="31">31</option> -</select> -<select name="mydate_year"> -<option value="2007">2007</option> -<option value="2008">2008</option> -<option value="2009">2009</option> -<option value="2010" selected="selected">2010</option> -<option value="2011">2011</option> -<option value="2012">2012</option> -<option value="2013">2013</option> -<option value="2014">2014</option> -<option value="2015">2015</option> -<option value="2016">2016</option> -</select> - -Using a SelectDateWidget in a form: - ->>> class GetDate(Form): -... mydate = DateField(widget=SelectDateWidget) ->>> a = GetDate({'mydate_month':'4', 'mydate_day':'1', 'mydate_year':'2008'}) ->>> print a.is_valid() -True ->>> print a.cleaned_data['mydate'] -2008-04-01 - -As with any widget that implements get_value_from_datadict, -we must be prepared to accept the input from the "as_hidden" -rendering as well. - ->>> print a['mydate'].as_hidden() -<input type="hidden" name="mydate" value="2008-4-1" id="id_mydate" /> ->>> b=GetDate({'mydate':'2008-4-1'}) ->>> print b.is_valid() -True ->>> print b.cleaned_data['mydate'] -2008-04-01 - - -# MultiWidget and MultiValueField ############################################# -# MultiWidgets are widgets composed of other widgets. They are usually -# combined with MultiValueFields - a field that is composed of other fields. -# MulitWidgets can themselved be composed of other MultiWidgets. -# SplitDateTimeWidget is one example of a MultiWidget. - ->>> class ComplexMultiWidget(MultiWidget): -... def __init__(self, attrs=None): -... widgets = ( -... TextInput(), -... SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), -... SplitDateTimeWidget(), -... ) -... super(ComplexMultiWidget, self).__init__(widgets, attrs) -... -... def decompress(self, value): -... if value: -... data = value.split(',') -... return [data[0], data[1], datetime.datetime(*time.strptime(data[2], "%Y-%m-%d %H:%M:%S")[0:6])] -... return [None, None, None] -... def format_output(self, rendered_widgets): -... return u'\n'.join(rendered_widgets) ->>> w = ComplexMultiWidget() ->>> print w.render('name', 'some text,JP,2007-04-25 06:24:00') -<input type="text" name="name_0" value="some text" /> -<select multiple="multiple" name="name_1"> -<option value="J" selected="selected">John</option> -<option value="P" selected="selected">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> -<input type="text" name="name_2_0" value="2007-04-25" /><input type="text" name="name_2_1" value="06:24:00" /> - ->>> class ComplexField(MultiValueField): -... def __init__(self, required=True, widget=None, label=None, initial=None): -... fields = ( -... CharField(), -... MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), -... SplitDateTimeField() -... ) -... super(ComplexField, self).__init__(fields, required, widget, label, initial) -... -... def compress(self, data_list): -... if data_list: -... return '%s,%s,%s' % (data_list[0],''.join(data_list[1]),data_list[2]) -... return None - ->>> f = ComplexField(widget=w) ->>> f.clean(['some text', ['J','P'], ['2007-04-25','6:24:00']]) -u'some text,JP,2007-04-25 06:24:00' ->>> f.clean(['some text',['X'], ['2007-04-25','6:24:00']]) -Traceback (most recent call last): -... -ValidationError: [u'Select a valid choice. X is not one of the available choices.'] - -# If insufficient data is provided, None is substituted ->>> f.clean(['some text',['JP']]) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> class ComplexFieldForm(Form): -... field1 = ComplexField(widget=w) ->>> f = ComplexFieldForm() ->>> print f -<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" id="id_field1_0" /> -<select multiple="multiple" name="field1_1" id="id_field1_1"> -<option value="J">John</option> -<option value="P">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> -<input type="text" name="field1_2_0" id="id_field1_2_0" /><input type="text" name="field1_2_1" id="id_field1_2_1" /></td></tr> - ->>> f = ComplexFieldForm({'field1_0':'some text','field1_1':['J','P'], 'field1_2_0':'2007-04-25', 'field1_2_1':'06:24:00'}) ->>> print f -<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" value="some text" id="id_field1_0" /> -<select multiple="multiple" name="field1_1" id="id_field1_1"> -<option value="J" selected="selected">John</option> -<option value="P" selected="selected">Paul</option> -<option value="G">George</option> -<option value="R">Ringo</option> -</select> -<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr> - ->>> f.cleaned_data -{'field1': u'some text,JP,2007-04-25 06:24:00'} - - -# IPAddressField ################################################################## - ->>> f = IPAddressField() ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('127.0.0.1') -u'127.0.0.1' ->>> f.clean('foo') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] ->>> f.clean('127.0.0.') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] ->>> f.clean('1.2.3.4.5') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] ->>> f.clean('256.125.1.5') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] - ->>> f = IPAddressField(required=False) ->>> f.clean('') -u'' ->>> f.clean(None) -u'' ->>> f.clean('127.0.0.1') -u'127.0.0.1' ->>> f.clean('foo') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] ->>> f.clean('127.0.0.') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] ->>> f.clean('1.2.3.4.5') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] ->>> f.clean('256.125.1.5') -Traceback (most recent call last): -... -ValidationError: [u'Enter a valid IPv4 address.'] - -################################# -# Tests of underlying functions # -################################# - -# smart_unicode tests ->>> from django.utils.encoding import smart_unicode ->>> class Test: -... def __str__(self): -... return 'ŠĐĆŽćžšđ' ->>> class TestU: -... def __str__(self): -... return 'Foo' -... def __unicode__(self): -... return u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' ->>> smart_unicode(Test()) -u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' ->>> smart_unicode(TestU()) -u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' ->>> smart_unicode(1) -u'1' ->>> smart_unicode('foo') -u'foo' - - -#################################### -# Test accessing errors in clean() # -#################################### - ->>> class UserForm(Form): -... username = CharField(max_length=10) -... password = CharField(widget=PasswordInput) -... def clean(self): -... data = self.cleaned_data -... if not self.errors: -... data['username'] = data['username'].lower() -... return data - ->>> f = UserForm({'username': 'SirRobin', 'password': 'blue'}) ->>> f.is_valid() -True ->>> f.cleaned_data['username'] -u'sirrobin' - -####################################### -# Test overriding ErrorList in a form # -####################################### - ->>> from django.newforms.util import ErrorList ->>> class DivErrorList(ErrorList): -... def __unicode__(self): -... return self.as_divs() -... def as_divs(self): -... if not self: return u'' -... return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self]) ->>> class CommentForm(Form): -... name = CharField(max_length=50, required=False) -... email = EmailField() -... comment = CharField() ->>> data = dict(email='invalid') ->>> f = CommentForm(data, auto_id=False, error_class=DivErrorList) ->>> print f.as_p() -<p>Name: <input type="text" name="name" maxlength="50" /></p> -<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div> -<p>Email: <input type="text" name="email" value="invalid" /></p> -<div class="errorlist"><div class="error">This field is required.</div></div> -<p>Comment: <input type="text" name="comment" /></p> - -################################# -# Test multipart-encoded form # -################################# - ->>> class FormWithoutFile(Form): -... username = CharField() ->>> class FormWithFile(Form): -... username = CharField() -... file = FileField() ->>> class FormWithImage(Form): -... image = ImageField() - ->>> FormWithoutFile().is_multipart() -False ->>> FormWithFile().is_multipart() -True ->>> FormWithImage().is_multipart() -True - -""" __test__ = { + 'extra_tests': extra_tests, + 'fields_tests': fields_tests, 'form_tests': form_tests, - 'localflavor': localflavor_tests, - 'regressions': regression_tests, + 'localflavor_ar_tests': localflavor_ar_tests, + 'localflavor_au_tests': localflavor_au_tests, + 'localflavor_br_tests': localflavor_br_tests, + 'localflavor_ca_tests': localflavor_ca_tests, + 'localflavor_ch_tests': localflavor_ch_tests, + 'localflavor_cl_tests': localflavor_cl_tests, + 'localflavor_de_tests': localflavor_de_tests, + 'localflavor_fi_tests': localflavor_fi_tests, + 'localflavor_fr_tests': localflavor_fr_tests, + 'localflavor_generic_tests': localflavor_generic_tests, + 'localflavor_is_tests': localflavor_is_tests, + 'localflavor_it_tests': localflavor_it_tests, + 'localflavor_jp_tests': localflavor_jp_tests, + 'localflavor_nl_tests': localflavor_nl_tests, + 'localflavor_pl_tests': localflavor_pl_tests, + 'localflavor_sk_tests': localflavor_sk_tests, + 'localflavor_uk_tests': localflavor_uk_tests, + 'localflavor_us_tests': localflavor_us_tests, + 'regressions_tests': regression_tests, 'formset_tests': formset_tests, 'media_tests': media_tests, 'util_tests': util_tests, + 'widgets_tests': widgets_tests, } if __name__ == "__main__": diff --git a/tests/regressiontests/forms/util.py b/tests/regressiontests/forms/util.py index 1a02b7e55b..4f81709082 100644 --- a/tests/regressiontests/forms/util.py +++ b/tests/regressiontests/forms/util.py @@ -3,7 +3,7 @@ Tests for newforms/util.py module. """ -util_tests = r""" +tests = r""" >>> from django.newforms.util import * >>> from django.utils.translation import ugettext_lazy diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py new file mode 100644 index 0000000000..1c3e873963 --- /dev/null +++ b/tests/regressiontests/forms/widgets.py @@ -0,0 +1,848 @@ +# -*- coding: utf-8 -*- +tests = r""" +>>> from django.newforms import * +>>> from django.newforms.widgets import RadioFieldRenderer +>>> import datetime +>>> import time +>>> import re +>>> try: +... from decimal import Decimal +... except ImportError: +... from django.utils._decimal import Decimal + +########### +# Widgets # +########### + +Each Widget class corresponds to an HTML form widget. A Widget knows how to +render itself, given a field name and some data. Widgets don't perform +validation. + +# TextInput Widget ############################################################ + +>>> w = TextInput() +>>> w.render('email', '') +u'<input type="text" name="email" />' +>>> w.render('email', None) +u'<input type="text" name="email" />' +>>> w.render('email', 'test@example.com') +u'<input type="text" name="email" value="test@example.com" />' +>>> w.render('email', 'some "quoted" & ampersanded value') +u'<input type="text" name="email" value="some "quoted" & ampersanded value" />' +>>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) +u'<input type="text" name="email" value="test@example.com" class="fun" />' + +# Note that doctest in Python 2.4 (and maybe 2.5?) doesn't support non-ascii +# characters in output, so we're displaying the repr() here. +>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) +u'<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />' + +You can also pass 'attrs' to the constructor: +>>> w = TextInput(attrs={'class': 'fun'}) +>>> w.render('email', '') +u'<input type="text" class="fun" name="email" />' +>>> w.render('email', 'foo@example.com') +u'<input type="text" class="fun" value="foo@example.com" name="email" />' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = TextInput(attrs={'class': 'pretty'}) +>>> w.render('email', '', attrs={'class': 'special'}) +u'<input type="text" class="special" name="email" />' + +# PasswordInput Widget ############################################################ + +>>> w = PasswordInput() +>>> w.render('email', '') +u'<input type="password" name="email" />' +>>> w.render('email', None) +u'<input type="password" name="email" />' +>>> w.render('email', 'test@example.com') +u'<input type="password" name="email" value="test@example.com" />' +>>> w.render('email', 'some "quoted" & ampersanded value') +u'<input type="password" name="email" value="some "quoted" & ampersanded value" />' +>>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) +u'<input type="password" name="email" value="test@example.com" class="fun" />' + +You can also pass 'attrs' to the constructor: +>>> w = PasswordInput(attrs={'class': 'fun'}) +>>> w.render('email', '') +u'<input type="password" class="fun" name="email" />' +>>> w.render('email', 'foo@example.com') +u'<input type="password" class="fun" value="foo@example.com" name="email" />' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = PasswordInput(attrs={'class': 'pretty'}) +>>> w.render('email', '', attrs={'class': 'special'}) +u'<input type="password" class="special" name="email" />' + +>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) +u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />' + +The render_value argument lets you specify whether the widget should render +its value. You may want to do this for security reasons. +>>> w = PasswordInput(render_value=True) +>>> w.render('email', 'secret') +u'<input type="password" name="email" value="secret" />' +>>> w = PasswordInput(render_value=False) +>>> w.render('email', '') +u'<input type="password" name="email" />' +>>> w.render('email', None) +u'<input type="password" name="email" />' +>>> w.render('email', 'secret') +u'<input type="password" name="email" />' +>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False) +>>> w.render('email', 'secret') +u'<input type="password" class="fun" name="email" />' + +# HiddenInput Widget ############################################################ + +>>> w = HiddenInput() +>>> w.render('email', '') +u'<input type="hidden" name="email" />' +>>> w.render('email', None) +u'<input type="hidden" name="email" />' +>>> w.render('email', 'test@example.com') +u'<input type="hidden" name="email" value="test@example.com" />' +>>> w.render('email', 'some "quoted" & ampersanded value') +u'<input type="hidden" name="email" value="some "quoted" & ampersanded value" />' +>>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) +u'<input type="hidden" name="email" value="test@example.com" class="fun" />' + +You can also pass 'attrs' to the constructor: +>>> w = HiddenInput(attrs={'class': 'fun'}) +>>> w.render('email', '') +u'<input type="hidden" class="fun" name="email" />' +>>> w.render('email', 'foo@example.com') +u'<input type="hidden" class="fun" value="foo@example.com" name="email" />' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = HiddenInput(attrs={'class': 'pretty'}) +>>> w.render('email', '', attrs={'class': 'special'}) +u'<input type="hidden" class="special" name="email" />' + +>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) +u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = HiddenInput(attrs={'class': 'pretty'}) +>>> w.render('email', '', attrs={'class': 'special'}) +u'<input type="hidden" class="special" name="email" />' + +# MultipleHiddenInput Widget ################################################## + +>>> w = MultipleHiddenInput() +>>> w.render('email', []) +u'' +>>> w.render('email', None) +u'' +>>> w.render('email', ['test@example.com']) +u'<input type="hidden" name="email" value="test@example.com" />' +>>> w.render('email', ['some "quoted" & ampersanded value']) +u'<input type="hidden" name="email" value="some "quoted" & ampersanded value" />' +>>> w.render('email', ['test@example.com', 'foo@example.com']) +u'<input type="hidden" name="email" value="test@example.com" />\n<input type="hidden" name="email" value="foo@example.com" />' +>>> w.render('email', ['test@example.com'], attrs={'class': 'fun'}) +u'<input type="hidden" name="email" value="test@example.com" class="fun" />' +>>> w.render('email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'}) +u'<input type="hidden" name="email" value="test@example.com" class="fun" />\n<input type="hidden" name="email" value="foo@example.com" class="fun" />' + +You can also pass 'attrs' to the constructor: +>>> w = MultipleHiddenInput(attrs={'class': 'fun'}) +>>> w.render('email', []) +u'' +>>> w.render('email', ['foo@example.com']) +u'<input type="hidden" class="fun" value="foo@example.com" name="email" />' +>>> w.render('email', ['foo@example.com', 'test@example.com']) +u'<input type="hidden" class="fun" value="foo@example.com" name="email" />\n<input type="hidden" class="fun" value="test@example.com" name="email" />' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = MultipleHiddenInput(attrs={'class': 'pretty'}) +>>> w.render('email', ['foo@example.com'], attrs={'class': 'special'}) +u'<input type="hidden" class="special" value="foo@example.com" name="email" />' + +>>> w.render('email', ['ŠĐĆŽćžšđ'], attrs={'class': 'fun'}) +u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = MultipleHiddenInput(attrs={'class': 'pretty'}) +>>> w.render('email', ['foo@example.com'], attrs={'class': 'special'}) +u'<input type="hidden" class="special" value="foo@example.com" name="email" />' + +# FileInput Widget ############################################################ + +FileInput widgets don't ever show the value, because the old value is of no use +if you are updating the form or if the provided file generated an error. +>>> w = FileInput() +>>> w.render('email', '') +u'<input type="file" name="email" />' +>>> w.render('email', None) +u'<input type="file" name="email" />' +>>> w.render('email', 'test@example.com') +u'<input type="file" name="email" />' +>>> w.render('email', 'some "quoted" & ampersanded value') +u'<input type="file" name="email" />' +>>> w.render('email', 'test@example.com', attrs={'class': 'fun'}) +u'<input type="file" name="email" class="fun" />' + +You can also pass 'attrs' to the constructor: +>>> w = FileInput(attrs={'class': 'fun'}) +>>> w.render('email', '') +u'<input type="file" class="fun" name="email" />' +>>> w.render('email', 'foo@example.com') +u'<input type="file" class="fun" name="email" />' + +>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) +u'<input type="file" class="fun" name="email" />' + +# Textarea Widget ############################################################# + +>>> w = Textarea() +>>> w.render('msg', '') +u'<textarea rows="10" cols="40" name="msg"></textarea>' +>>> w.render('msg', None) +u'<textarea rows="10" cols="40" name="msg"></textarea>' +>>> w.render('msg', 'value') +u'<textarea rows="10" cols="40" name="msg">value</textarea>' +>>> w.render('msg', 'some "quoted" & ampersanded value') +u'<textarea rows="10" cols="40" name="msg">some "quoted" & ampersanded value</textarea>' +>>> w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20}) +u'<textarea class="pretty" rows="20" cols="40" name="msg">value</textarea>' + +You can also pass 'attrs' to the constructor: +>>> w = Textarea(attrs={'class': 'pretty'}) +>>> w.render('msg', '') +u'<textarea rows="10" cols="40" name="msg" class="pretty"></textarea>' +>>> w.render('msg', 'example') +u'<textarea rows="10" cols="40" name="msg" class="pretty">example</textarea>' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = Textarea(attrs={'class': 'pretty'}) +>>> w.render('msg', '', attrs={'class': 'special'}) +u'<textarea rows="10" cols="40" name="msg" class="special"></textarea>' + +>>> w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) +u'<textarea rows="10" cols="40" name="msg" class="fun">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>' + +# CheckboxInput Widget ######################################################## + +>>> w = CheckboxInput() +>>> w.render('is_cool', '') +u'<input type="checkbox" name="is_cool" />' +>>> w.render('is_cool', None) +u'<input type="checkbox" name="is_cool" />' +>>> w.render('is_cool', False) +u'<input type="checkbox" name="is_cool" />' +>>> w.render('is_cool', True) +u'<input checked="checked" type="checkbox" name="is_cool" />' + +Using any value that's not in ('', None, False, True) will check the checkbox +and set the 'value' attribute. +>>> w.render('is_cool', 'foo') +u'<input checked="checked" type="checkbox" name="is_cool" value="foo" />' + +>>> w.render('is_cool', False, attrs={'class': 'pretty'}) +u'<input type="checkbox" name="is_cool" class="pretty" />' + +You can also pass 'attrs' to the constructor: +>>> w = CheckboxInput(attrs={'class': 'pretty'}) +>>> w.render('is_cool', '') +u'<input type="checkbox" class="pretty" name="is_cool" />' + +'attrs' passed to render() get precedence over those passed to the constructor: +>>> w = CheckboxInput(attrs={'class': 'pretty'}) +>>> w.render('is_cool', '', attrs={'class': 'special'}) +u'<input type="checkbox" class="special" name="is_cool" />' + +You can pass 'check_test' to the constructor. This is a callable that takes the +value and returns True if the box should be checked. +>>> w = CheckboxInput(check_test=lambda value: value.startswith('hello')) +>>> w.render('greeting', '') +u'<input type="checkbox" name="greeting" />' +>>> w.render('greeting', 'hello') +u'<input checked="checked" type="checkbox" name="greeting" value="hello" />' +>>> w.render('greeting', 'hello there') +u'<input checked="checked" type="checkbox" name="greeting" value="hello there" />' +>>> w.render('greeting', 'hello & goodbye') +u'<input checked="checked" type="checkbox" name="greeting" value="hello & goodbye" />' + +A subtlety: If the 'check_test' argument cannot handle a value and raises any +exception during its __call__, then the exception will be swallowed and the box +will not be checked. In this example, the 'check_test' assumes the value has a +startswith() method, which fails for the values True, False and None. +>>> w.render('greeting', True) +u'<input type="checkbox" name="greeting" />' +>>> w.render('greeting', False) +u'<input type="checkbox" name="greeting" />' +>>> w.render('greeting', None) +u'<input type="checkbox" name="greeting" />' + +# Select Widget ############################################################### + +>>> w = Select() +>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select name="beatle"> +<option value="J" selected="selected">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> + +If the value is None, none of the options are selected: +>>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select name="beatle"> +<option value="J">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> + +If the value corresponds to a label (but not to an option value), none of the options are selected: +>>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select name="beatle"> +<option value="J">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> + +The value is compared to its str(): +>>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]) +<select name="num"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> +>>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]) +<select name="num"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> +>>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]) +<select name="num"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> + +The 'choices' argument can be any iterable: +>>> from itertools import chain +>>> def get_choices(): +... for i in range(5): +... yield (i, i) +>>> print w.render('num', 2, choices=get_choices()) +<select name="num"> +<option value="0">0</option> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +<option value="4">4</option> +</select> +>>> things = ({'id': 1, 'name': 'And Boom'}, {'id': 2, 'name': 'One More Thing!'}) +>>> class SomeForm(Form): +... somechoice = ChoiceField(choices=chain((('', '-'*9),), [(thing['id'], thing['name']) for thing in things])) +>>> f = SomeForm() +>>> f.as_table() +u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>' +>>> f.as_table() +u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>' +>>> f = SomeForm({'somechoice': 2}) +>>> f.as_table() +u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="">---------</option>\n<option value="1">And Boom</option>\n<option value="2" selected="selected">One More Thing!</option>\n</select></td></tr>' + +You can also pass 'choices' to the constructor: +>>> w = Select(choices=[(1, 1), (2, 2), (3, 3)]) +>>> print w.render('num', 2) +<select name="num"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> + +If 'choices' is passed to both the constructor and render(), then they'll both be in the output: +>>> print w.render('num', 2, choices=[(4, 4), (5, 5)]) +<select name="num"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +<option value="4">4</option> +<option value="5">5</option> +</select> + +>>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) +u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>' + +If choices is passed to the constructor and is a generator, it can be iterated +over multiple times without getting consumed: +>>> w = Select(choices=get_choices()) +>>> print w.render('num', 2) +<select name="num"> +<option value="0">0</option> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +<option value="4">4</option> +</select> +>>> print w.render('num', 3) +<select name="num"> +<option value="0">0</option> +<option value="1">1</option> +<option value="2">2</option> +<option value="3" selected="selected">3</option> +<option value="4">4</option> +</select> + +# NullBooleanSelect Widget #################################################### + +>>> w = NullBooleanSelect() +>>> print w.render('is_cool', True) +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2" selected="selected">Yes</option> +<option value="3">No</option> +</select> +>>> print w.render('is_cool', False) +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2">Yes</option> +<option value="3" selected="selected">No</option> +</select> +>>> print w.render('is_cool', None) +<select name="is_cool"> +<option value="1" selected="selected">Unknown</option> +<option value="2">Yes</option> +<option value="3">No</option> +</select> +>>> print w.render('is_cool', '2') +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2" selected="selected">Yes</option> +<option value="3">No</option> +</select> +>>> print w.render('is_cool', '3') +<select name="is_cool"> +<option value="1">Unknown</option> +<option value="2">Yes</option> +<option value="3" selected="selected">No</option> +</select> + +""" + \ +r""" # [This concatenation is to keep the string below the jython's 32K limit]. +# SelectMultiple Widget ####################################################### + +>>> w = SelectMultiple() +>>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select multiple="multiple" name="beatles"> +<option value="J" selected="selected">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> +>>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select multiple="multiple" name="beatles"> +<option value="J" selected="selected">John</option> +<option value="P" selected="selected">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> +>>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select multiple="multiple" name="beatles"> +<option value="J" selected="selected">John</option> +<option value="P" selected="selected">Paul</option> +<option value="G">George</option> +<option value="R" selected="selected">Ringo</option> +</select> + +If the value is None, none of the options are selected: +>>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select multiple="multiple" name="beatles"> +<option value="J">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> + +If the value corresponds to a label (but not to an option value), none of the options are selected: +>>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select multiple="multiple" name="beatles"> +<option value="J">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> + +If multiple values are given, but some of them are not valid, the valid ones are selected: +>>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<select multiple="multiple" name="beatles"> +<option value="J" selected="selected">John</option> +<option value="P">Paul</option> +<option value="G" selected="selected">George</option> +<option value="R">Ringo</option> +</select> + +The value is compared to its str(): +>>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]) +<select multiple="multiple" name="nums"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> +>>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]) +<select multiple="multiple" name="nums"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> +>>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]) +<select multiple="multiple" name="nums"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> + +The 'choices' argument can be any iterable: +>>> def get_choices(): +... for i in range(5): +... yield (i, i) +>>> print w.render('nums', [2], choices=get_choices()) +<select multiple="multiple" name="nums"> +<option value="0">0</option> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +<option value="4">4</option> +</select> + +You can also pass 'choices' to the constructor: +>>> w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) +>>> print w.render('nums', [2]) +<select multiple="multiple" name="nums"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +</select> + +If 'choices' is passed to both the constructor and render(), then they'll both be in the output: +>>> print w.render('nums', [2], choices=[(4, 4), (5, 5)]) +<select multiple="multiple" name="nums"> +<option value="1">1</option> +<option value="2" selected="selected">2</option> +<option value="3">3</option> +<option value="4">4</option> +<option value="5">5</option> +</select> + +>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) +u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>' + +# RadioSelect Widget ########################################################## + +>>> w = RadioSelect() +>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input checked="checked" type="radio" name="beatle" value="J" /> John</label></li> +<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li> +<li><label><input type="radio" name="beatle" value="G" /> George</label></li> +<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li> +</ul> + +If the value is None, none of the options are checked: +>>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input type="radio" name="beatle" value="J" /> John</label></li> +<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li> +<li><label><input type="radio" name="beatle" value="G" /> George</label></li> +<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li> +</ul> + +If the value corresponds to a label (but not to an option value), none of the options are checked: +>>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input type="radio" name="beatle" value="J" /> John</label></li> +<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li> +<li><label><input type="radio" name="beatle" value="G" /> George</label></li> +<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li> +</ul> + +The value is compared to its str(): +>>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]) +<ul> +<li><label><input type="radio" name="num" value="1" /> 1</label></li> +<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> +<li><label><input type="radio" name="num" value="3" /> 3</label></li> +</ul> +>>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]) +<ul> +<li><label><input type="radio" name="num" value="1" /> 1</label></li> +<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> +<li><label><input type="radio" name="num" value="3" /> 3</label></li> +</ul> +>>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]) +<ul> +<li><label><input type="radio" name="num" value="1" /> 1</label></li> +<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> +<li><label><input type="radio" name="num" value="3" /> 3</label></li> +</ul> + +The 'choices' argument can be any iterable: +>>> def get_choices(): +... for i in range(5): +... yield (i, i) +>>> print w.render('num', 2, choices=get_choices()) +<ul> +<li><label><input type="radio" name="num" value="0" /> 0</label></li> +<li><label><input type="radio" name="num" value="1" /> 1</label></li> +<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> +<li><label><input type="radio" name="num" value="3" /> 3</label></li> +<li><label><input type="radio" name="num" value="4" /> 4</label></li> +</ul> + +You can also pass 'choices' to the constructor: +>>> w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)]) +>>> print w.render('num', 2) +<ul> +<li><label><input type="radio" name="num" value="1" /> 1</label></li> +<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> +<li><label><input type="radio" name="num" value="3" /> 3</label></li> +</ul> + +If 'choices' is passed to both the constructor and render(), then they'll both be in the output: +>>> print w.render('num', 2, choices=[(4, 4), (5, 5)]) +<ul> +<li><label><input type="radio" name="num" value="1" /> 1</label></li> +<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li> +<li><label><input type="radio" name="num" value="3" /> 3</label></li> +<li><label><input type="radio" name="num" value="4" /> 4</label></li> +<li><label><input type="radio" name="num" value="5" /> 5</label></li> +</ul> + +RadioSelect uses a RadioFieldRenderer to render the individual radio inputs. +You can manipulate that object directly to customize the way the RadioSelect +is rendered. +>>> w = RadioSelect() +>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +>>> for inp in r: +... print inp +<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label> +<label><input type="radio" name="beatle" value="P" /> Paul</label> +<label><input type="radio" name="beatle" value="G" /> George</label> +<label><input type="radio" name="beatle" value="R" /> Ringo</label> +>>> for inp in r: +... print '%s<br />' % inp +<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label><br /> +<label><input type="radio" name="beatle" value="P" /> Paul</label><br /> +<label><input type="radio" name="beatle" value="G" /> George</label><br /> +<label><input type="radio" name="beatle" value="R" /> Ringo</label><br /> +>>> for inp in r: +... print '<p>%s %s</p>' % (inp.tag(), inp.choice_label) +<p><input checked="checked" type="radio" name="beatle" value="J" /> John</p> +<p><input type="radio" name="beatle" value="P" /> Paul</p> +<p><input type="radio" name="beatle" value="G" /> George</p> +<p><input type="radio" name="beatle" value="R" /> Ringo</p> +>>> for inp in r: +... print '%s %s %s %s %s' % (inp.name, inp.value, inp.choice_value, inp.choice_label, inp.is_checked()) +beatle J J John True +beatle J P Paul False +beatle J G George False +beatle J R Ringo False + +You can create your own custom renderers for RadioSelect to use. +>>> class MyRenderer(RadioFieldRenderer): +... def render(self): +... return u'<br />\n'.join([unicode(choice) for choice in self]) +>>> w = RadioSelect(renderer=MyRenderer) +>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<label><input type="radio" name="beatle" value="J" /> John</label><br /> +<label><input type="radio" name="beatle" value="P" /> Paul</label><br /> +<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br /> +<label><input type="radio" name="beatle" value="R" /> Ringo</label> + +A RadioFieldRenderer object also allows index access to individual RadioInput +objects. +>>> w = RadioSelect() +>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +>>> print r[1] +<label><input type="radio" name="beatle" value="P" /> Paul</label> +>>> print r[0] +<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label> +>>> r[0].is_checked() +True +>>> r[1].is_checked() +False +>>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label +('beatle', u'J', u'P', u'Paul') +>>> r[10] +Traceback (most recent call last): +... +IndexError: list index out of range + +# Unicode choices are correctly rendered as HTML +>>> w = RadioSelect() +>>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])) +u'<ul>\n<li><label><input checked="checked" type="radio" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="radio" name="email" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>' + +# Attributes provided at instantiation are passed to the constituent inputs +>>> w = RadioSelect(attrs={'id':'foo'}) +>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input checked="checked" type="radio" id="foo_0" value="J" name="beatle" /> John</label></li> +<li><label><input type="radio" id="foo_1" value="P" name="beatle" /> Paul</label></li> +<li><label><input type="radio" id="foo_2" value="G" name="beatle" /> George</label></li> +<li><label><input type="radio" id="foo_3" value="R" name="beatle" /> Ringo</label></li> +</ul> + +# Attributes provided at render-time are passed to the constituent inputs +>>> w = RadioSelect() +>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id':'bar'}) +<ul> +<li><label><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label></li> +<li><label><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li> +<li><label><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li> +<li><label><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></li> +</ul> + +# CheckboxSelectMultiple Widget ############################################### + +>>> w = CheckboxSelectMultiple() +>>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> +<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> +<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> +<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> +</ul> +>>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> +<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li> +<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> +<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> +</ul> +>>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> +<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li> +<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> +<li><label><input checked="checked" type="checkbox" name="beatles" value="R" /> Ringo</label></li> +</ul> + +If the value is None, none of the options are selected: +>>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input type="checkbox" name="beatles" value="J" /> John</label></li> +<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> +<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> +<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> +</ul> + +If the value corresponds to a label (but not to an option value), none of the options are selected: +>>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input type="checkbox" name="beatles" value="J" /> John</label></li> +<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> +<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li> +<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> +</ul> + +If multiple values are given, but some of them are not valid, the valid ones are selected: +>>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li> +<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li> +<li><label><input checked="checked" type="checkbox" name="beatles" value="G" /> George</label></li> +<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li> +</ul> + +The value is compared to its str(): +>>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]) +<ul> +<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> +<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> +<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> +</ul> +>>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]) +<ul> +<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> +<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> +<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> +</ul> +>>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]) +<ul> +<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> +<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> +<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> +</ul> + +The 'choices' argument can be any iterable: +>>> def get_choices(): +... for i in range(5): +... yield (i, i) +>>> print w.render('nums', [2], choices=get_choices()) +<ul> +<li><label><input type="checkbox" name="nums" value="0" /> 0</label></li> +<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> +<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> +<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> +<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> +</ul> + +You can also pass 'choices' to the constructor: +>>> w = CheckboxSelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) +>>> print w.render('nums', [2]) +<ul> +<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> +<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> +<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> +</ul> + +If 'choices' is passed to both the constructor and render(), then they'll both be in the output: +>>> print w.render('nums', [2], choices=[(4, 4), (5, 5)]) +<ul> +<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li> +<li><label><input checked="checked" type="checkbox" name="nums" value="2" /> 2</label></li> +<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li> +<li><label><input type="checkbox" name="nums" value="4" /> 4</label></li> +<li><label><input type="checkbox" name="nums" value="5" /> 5</label></li> +</ul> + +>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]) +u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>' + +# MultiWidget ################################################################# + +>>> class MyMultiWidget(MultiWidget): +... def decompress(self, value): +... if value: +... return value.split('__') +... return ['', ''] +... def format_output(self, rendered_widgets): +... return u'<br />'.join(rendered_widgets) +>>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'}))) +>>> w.render('name', ['john', 'lennon']) +u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />' +>>> w.render('name', 'john__lennon') +u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />' +>>> w.render('name', 'john__lennon', attrs={'id':'foo'}) +u'<input id="foo_0" type="text" class="big" value="john" name="name_0" /><br /><input id="foo_1" type="text" class="small" value="lennon" name="name_1" />' +>>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'}) +>>> w.render('name', ['john', 'lennon']) +u'<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />' + +# SplitDateTimeWidget ######################################################### + +>>> w = SplitDateTimeWidget() +>>> w.render('date', '') +u'<input type="text" name="date_0" /><input type="text" name="date_1" />' +>>> w.render('date', None) +u'<input type="text" name="date_0" /><input type="text" name="date_1" />' +>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30)) +u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />' +>>> w.render('date', [datetime.date(2006, 1, 10), datetime.time(7, 30)]) +u'<input type="text" name="date_0" value="2006-01-10" /><input type="text" name="date_1" value="07:30:00" />' + +You can also pass 'attrs' to the constructor. In this case, the attrs will be +included on both widgets. +>>> w = SplitDateTimeWidget(attrs={'class': 'pretty'}) +>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30)) +u'<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />' +""" diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index 9d5ca2006a..5cfae029bb 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -8,7 +8,7 @@ >>> q['foo'] Traceback (most recent call last): ... -MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>" +MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" >>> q['something'] = 'bar' Traceback (most recent call last): @@ -89,7 +89,7 @@ AttributeError: This QueryDict instance is immutable >>> q['foo'] Traceback (most recent call last): ... -MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>" +MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" >>> q['name'] = 'john' @@ -201,7 +201,7 @@ u'bar' >>> q['bar'] Traceback (most recent call last): ... -MultiValueDictKeyError: "Key 'bar' not found in <MultiValueDict: {u'foo': [u'bar']}>" +MultiValueDictKeyError: "Key 'bar' not found in <QueryDict: {u'foo': [u'bar']}>" >>> q['something'] = 'bar' Traceback (most recent call last): diff --git a/tests/regressiontests/max_lengths/__init__.py b/tests/regressiontests/max_lengths/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/tests/regressiontests/max_lengths/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/regressiontests/max_lengths/models.py b/tests/regressiontests/max_lengths/models.py new file mode 100644 index 0000000000..b4c43a220d --- /dev/null +++ b/tests/regressiontests/max_lengths/models.py @@ -0,0 +1,13 @@ +from django.db import models + +class PersonWithDefaultMaxLengths(models.Model): + email = models.EmailField() + vcard = models.FileField(upload_to='/tmp') + homepage = models.URLField() + avatar = models.FilePathField() + +class PersonWithCustomMaxLengths(models.Model): + email = models.EmailField(max_length=384) + vcard = models.FileField(upload_to='/tmp', max_length=1024) + homepage = models.URLField(max_length=256) + avatar = models.FilePathField(max_length=512) diff --git a/tests/regressiontests/max_lengths/tests.py b/tests/regressiontests/max_lengths/tests.py new file mode 100644 index 0000000000..0ef407f573 --- /dev/null +++ b/tests/regressiontests/max_lengths/tests.py @@ -0,0 +1,36 @@ +from unittest import TestCase +from django.db import DatabaseError +from regressiontests.max_lengths.models import PersonWithDefaultMaxLengths, PersonWithCustomMaxLengths + +class MaxLengthArgumentsTests(TestCase): + + def verify_max_length(self, model,field,length): + self.assertEquals(model._meta.get_field(field).max_length,length) + + def test_default_max_lengths(self): + self.verify_max_length(PersonWithDefaultMaxLengths, 'email', 75) + self.verify_max_length(PersonWithDefaultMaxLengths, 'vcard', 100) + self.verify_max_length(PersonWithDefaultMaxLengths, 'homepage', 200) + self.verify_max_length(PersonWithDefaultMaxLengths, 'avatar', 100) + + def test_custom_maxlengths(self): + self.verify_max_length(PersonWithCustomMaxLengths, 'email', 384) + self.verify_max_length(PersonWithCustomMaxLengths, 'vcard', 1024) + self.verify_max_length(PersonWithCustomMaxLengths, 'homepage', 256) + self.verify_max_length(PersonWithCustomMaxLengths, 'avatar', 512) + +class MaxLengthORMTests(TestCase): + + def test_custom_max_lengths(self): + args = { + "email": "someone@example.com", + "vcard": "vcard", + "homepage": "http://example.com/", + "avatar": "me.jpg" + } + + for field in ("email", "vcard", "homepage", "avatar"): + new_args = args.copy() + new_args[field] = "X" * 250 # a value longer than any of the default fields could hold. + p = PersonWithCustomMaxLengths.objects.create(**new_args) + self.assertEqual(getattr(p, field), ("X" * 250))
\ No newline at end of file diff --git a/tests/regressiontests/views/fixtures/testdata.json b/tests/regressiontests/views/fixtures/testdata.json index 2e52fa0989..449e91913a 100644 --- a/tests/regressiontests/views/fixtures/testdata.json +++ b/tests/regressiontests/views/fixtures/testdata.json @@ -4,10 +4,33 @@ "model": "views.article", "fields": { "author": 1, - "title": "An Article" + "title": "Old Article", + "slug": "old_article", + "date_created": "2001-01-01 21:22:23" } }, { + "pk": 2, + "model": "views.article", + "fields": { + "author": 1, + "title": "Current Article", + "slug": "current_article", + "date_created": "2007-09-17 21:22:23" + } + }, + { + "pk": 3, + "model": "views.article", + "fields": { + "author": 1, + "title": "Future Article", + "slug": "future_article", + "date_created": "3000-01-01 21:22:23" + } + }, + + { "pk": 1, "model": "views.author", "fields": { diff --git a/tests/regressiontests/views/models.py b/tests/regressiontests/views/models.py index 613050fb16..4bed1f3bde 100644 --- a/tests/regressiontests/views/models.py +++ b/tests/regressiontests/views/models.py @@ -17,8 +17,10 @@ class Author(models.Model): class Article(models.Model): title = models.CharField(max_length=100) + slug = models.SlugField() author = models.ForeignKey(Author) - + date_created = models.DateTimeField() + def __unicode__(self): return self.title diff --git a/tests/regressiontests/views/tests/__init__.py b/tests/regressiontests/views/tests/__init__.py index fa766ddd7c..2c8c5b4a92 100644 --- a/tests/regressiontests/views/tests/__init__.py +++ b/tests/regressiontests/views/tests/__init__.py @@ -1,3 +1,4 @@ from defaults import * from i18n import * -from static import *
\ No newline at end of file +from static import * +from generic.date_based import *
\ No newline at end of file diff --git a/tests/regressiontests/views/tests/generic/__init__.py b/tests/regressiontests/views/tests/generic/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/views/tests/generic/__init__.py diff --git a/tests/regressiontests/views/tests/generic/date_based.py b/tests/regressiontests/views/tests/generic/date_based.py new file mode 100644 index 0000000000..94ae99a20a --- /dev/null +++ b/tests/regressiontests/views/tests/generic/date_based.py @@ -0,0 +1,71 @@ +# coding: utf-8 +from django.test import TestCase +from datetime import datetime +from datetime import timedelta +from regressiontests.views.models import Article, Author + +class ObjectDetailTest(TestCase): + fixtures = ['testdata.json'] + def setUp(self): + # Correct the date for the current article + current_article = Article.objects.get(title="Current Article") + current_article.date_created = datetime.now() + current_article.save() + + def test_finds_past(self): + "date_based.object_detail can view a page in the past" + response = self.client.get('/views/date_based/object_detail/2001/01/01/old_article/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['object'].title, "Old Article") + + def test_object_detail_finds_today(self): + "date_based.object_detail can view a page from today" + today_url = datetime.now().strftime('%Y/%m/%d') + response = self.client.get('/views/date_based/object_detail/%s/current_article/' % today_url) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['object'].title, "Current Article") + + def test_object_detail_ignores_future(self): + "date_based.object_detail can view a page from the future, but only if allowed." + response = self.client.get('/views/date_based/object_detail/3000/01/01/future_article/') + self.assertEqual(response.status_code, 404) + + def test_object_detail_allowed_future_if_enabled(self): + "date_based.object_detail can view a page from the future if explicitly allowed." + response = self.client.get('/views/date_based/object_detail/3000/01/01/future_article/allow_future/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['object'].title, "Future Article") + +class MonthArchiveTest(TestCase): + def test_archive_month_includes_only_month(self): + "Regression for #3031: Archives around Feburary include only one month" + author = Author(name="John Smith") + author.save() + + # 2004 was a leap year, so it should be weird enough to not cheat + first_second_of_feb = datetime(2004, 2, 1, 0, 0, 1) + first_second_of_mar = datetime(2004, 3, 1, 0, 0, 1) + two_seconds = timedelta(0, 2, 0) + article = Article(title="example", author=author) + + article.date_created = first_second_of_feb + article.save() + response = self.client.get('/views/date_based/archive_month/2004/02/') + self.assertEqual(response.status_code, 200) + + article.date_created = first_second_of_feb-two_seconds + article.save() + response = self.client.get('/views/date_based/archive_month/2004/02/') + self.assertEqual(response.status_code, 404) + + article.date_created = first_second_of_mar-two_seconds + article.save() + response = self.client.get('/views/date_based/archive_month/2004/02/') + self.assertEqual(response.status_code, 200) + + article.date_created = first_second_of_mar + article.save() + response = self.client.get('/views/date_based/archive_month/2004/02/') + self.assertEqual(response.status_code, 404) + +
\ No newline at end of file diff --git a/tests/regressiontests/views/urls.py b/tests/regressiontests/views/urls.py index 67d918a018..5ef0c5129d 100644 --- a/tests/regressiontests/views/urls.py +++ b/tests/regressiontests/views/urls.py @@ -1,6 +1,8 @@ from os import path from django.conf.urls.defaults import * + +from models import * import views base_dir = path.dirname(path.abspath(__file__)) @@ -12,15 +14,35 @@ js_info_dict = { 'packages': ('regressiontests.views',), } +date_based_info_dict = { + 'queryset': Article.objects.all(), + 'date_field': 'date_created', + 'month_format': '%m', +} + urlpatterns = patterns('', (r'^$', views.index_page), + + # Default views (r'^shortcut/(\d+)/(.*)/$', 'django.views.defaults.shortcut'), (r'^non_existing_url/', 'django.views.defaults.page_not_found'), (r'^server_error/', 'django.views.defaults.server_error'), + # i18n views (r'^i18n/', include('django.conf.urls.i18n')), (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), - (r'^jsi18n_test/$', views.jsi18n_test), + # Static views (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': media_dir}), + + # Date-based generic views + (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/$', + 'django.views.generic.date_based.object_detail', + dict(slug_field='slug', **date_based_info_dict)), + (r'^date_based/object_detail/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[-\w]+)/allow_future/$', + 'django.views.generic.date_based.object_detail', + dict(allow_future=True, slug_field='slug', **date_based_info_dict)), + (r'^date_based/archive_month/(?P<year>\d{4})/(?P<month>\d{1,2})/$', + 'django.views.generic.date_based.archive_month', + date_based_info_dict), ) diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py index 720e43e0ac..9e0bbb2d66 100644 --- a/tests/regressiontests/views/views.py +++ b/tests/regressiontests/views/views.py @@ -1,12 +1,7 @@ from django.http import HttpResponse from django.template import RequestContext -from django.shortcuts import render_to_response def index_page(request): - """ Dummy index page """ + """Dummy index page""" return HttpResponse('<html><body>Dummy page</body></html>') - -def jsi18n_test(request): - """ View for testing javascript message files """ - return render_to_response('js_i18n.html', {}) diff --git a/tests/templates/views/article_archive_month.html b/tests/templates/views/article_archive_month.html new file mode 100644 index 0000000000..3f8ff55da6 --- /dev/null +++ b/tests/templates/views/article_archive_month.html @@ -0,0 +1 @@ +This template intentionally left blank
\ No newline at end of file diff --git a/tests/templates/views/article_detail.html b/tests/templates/views/article_detail.html new file mode 100644 index 0000000000..3f8ff55da6 --- /dev/null +++ b/tests/templates/views/article_detail.html @@ -0,0 +1 @@ +This template intentionally left blank
\ No newline at end of file |
