PKE-G} $django_tests/urls.py"""This urlconf exists because Django expects ROOT_URLCONF to exist. URLs should be added within the test folders, and use TestCase.urls to set them. This helps the tests remain isolated. """ urlpatterns = [] PKE-G)rCrCdjango_tests/runtests.py#!/usr/bin/env python import logging import os import shutil import subprocess import sys import tempfile import warnings from argparse import ArgumentParser import django from django.apps import apps from django.conf import settings from django.db import connection from django.test import TestCase, TransactionTestCase from django.test.utils import get_runner from django.utils import six from django.utils._os import upath from django.utils.deprecation import ( RemovedInDjango19Warning, RemovedInDjango110Warning, ) warnings.simplefilter("error", RemovedInDjango19Warning) warnings.simplefilter("error", RemovedInDjango110Warning) RUNTESTS_DIR = os.path.abspath(os.path.dirname(upath(__file__))) TEMPLATE_DIR = os.path.join(RUNTESTS_DIR, 'templates') # Create a specific subdirectory for the duration of the test suite. TMPDIR = tempfile.mkdtemp(prefix='django_') # Set the TMPDIR environment variable in addition to tempfile.tempdir # so that children processes inherit it. tempfile.tempdir = os.environ['TMPDIR'] = TMPDIR SUBDIRS_TO_SKIP = [ 'data', 'import_error_package', 'test_discovery_sample', 'test_discovery_sample2', 'test_runner_deprecation_app', ] ALWAYS_INSTALLED_APPS = [ 'django.contrib.contenttypes', 'django.contrib.auth', 'django.contrib.sites', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.admin.apps.SimpleAdminConfig', 'django.contrib.staticfiles', ] ALWAYS_MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ) # Need to add the associated contrib app to INSTALLED_APPS in some cases to # avoid "RuntimeError: Model class X doesn't declare an explicit app_label # and either isn't in an application in INSTALLED_APPS or else was imported # before its application was loaded." CONTRIB_TESTS_TO_APPS = { 'flatpages_tests': 'django.contrib.flatpages', 'redirects_tests': 'django.contrib.redirects', } def get_test_modules(): modules = [] discovery_paths = [ (None, RUNTESTS_DIR), ] # GIS tests are in nested apps if connection.features.gis_enabled: discovery_paths.append( ('gis_tests', os.path.join(RUNTESTS_DIR, 'gis_tests')) ) for modpath, dirpath in discovery_paths: for f in os.listdir(dirpath): if ('.' in f or f.startswith('sql') or os.path.basename(f) in SUBDIRS_TO_SKIP or os.path.isfile(f) or not os.path.exists(os.path.join(dirpath, f, '__init__.py'))): continue if connection.vendor != 'postgresql' and f == 'postgres_tests': continue modules.append((modpath, f)) return modules def get_installed(): return [app_config.name for app_config in apps.get_app_configs()] def setup(verbosity, test_labels): print("Testing against Django installed in '%s'" % os.path.dirname(django.__file__)) # Force declaring available_apps in TransactionTestCase for faster tests. def no_available_apps(self): raise Exception("Please define available_apps in TransactionTestCase " "and its subclasses.") TransactionTestCase.available_apps = property(no_available_apps) TestCase.available_apps = None state = { 'INSTALLED_APPS': settings.INSTALLED_APPS, 'ROOT_URLCONF': getattr(settings, "ROOT_URLCONF", ""), # Remove the following line in Django 1.10. 'TEMPLATE_DIRS': settings.TEMPLATE_DIRS, 'TEMPLATES': settings.TEMPLATES, 'LANGUAGE_CODE': settings.LANGUAGE_CODE, 'STATIC_URL': settings.STATIC_URL, 'STATIC_ROOT': settings.STATIC_ROOT, 'MIDDLEWARE_CLASSES': settings.MIDDLEWARE_CLASSES, } # Redirect some settings for the duration of these tests. settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS settings.ROOT_URLCONF = 'urls' settings.STATIC_URL = '/static/' settings.STATIC_ROOT = os.path.join(TMPDIR, 'static') # Remove the following line in Django 1.10. settings.TEMPLATE_DIRS = (TEMPLATE_DIR,) settings.TEMPLATES = [{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [TEMPLATE_DIR], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }] settings.LANGUAGE_CODE = 'en' settings.SITE_ID = 1 settings.MIDDLEWARE_CLASSES = ALWAYS_MIDDLEWARE_CLASSES # Ensure the middleware classes are seen as overridden otherwise we get a compatibility warning. settings._explicit_settings.add('MIDDLEWARE_CLASSES') settings.MIGRATION_MODULES = { # these 'tests.migrations' modules don't actually exist, but this lets # us skip creating migrations for the test models. 'auth': 'django.contrib.auth.tests.migrations', 'contenttypes': 'contenttypes_tests.migrations', } if verbosity > 0: # Ensure any warnings captured to logging are piped through a verbose # logging handler. If any -W options were passed explicitly on command # line, warnings are not captured, and this has no effect. logger = logging.getLogger('py.warnings') handler = logging.StreamHandler() logger.addHandler(handler) warnings.filterwarnings( 'ignore', 'django.contrib.webdesign will be removed in Django 1.10.', RemovedInDjango110Warning ) # Load all the ALWAYS_INSTALLED_APPS. django.setup() # Load all the test model apps. test_modules = get_test_modules() # Reduce given test labels to just the app module path test_labels_set = set() for label in test_labels: bits = label.split('.')[:1] test_labels_set.add('.'.join(bits)) installed_app_names = set(get_installed()) for modpath, module_name in test_modules: if modpath: module_label = '.'.join([modpath, module_name]) else: module_label = module_name # if the module (or an ancestor) was named on the command line, or # no modules were named (i.e., run all), import # this module and add it to INSTALLED_APPS. if not test_labels: module_found_in_labels = True else: module_found_in_labels = any( # exact match or ancestor match module_label == label or module_label.startswith(label + '.') for label in test_labels_set) if module_name in CONTRIB_TESTS_TO_APPS and module_found_in_labels: settings.INSTALLED_APPS.append(CONTRIB_TESTS_TO_APPS[module_name]) if module_found_in_labels and module_label not in installed_app_names: if verbosity >= 2: print("Importing application %s" % module_name) settings.INSTALLED_APPS.append(module_label) # Add contrib.gis to INSTALLED_APPS if needed (rather than requiring # @override_settings(INSTALLED_APPS=...) on all test cases. gis = 'django.contrib.gis' if connection.features.gis_enabled and gis not in settings.INSTALLED_APPS: if verbosity >= 2: print("Importing application %s" % gis) settings.INSTALLED_APPS.append(gis) apps.set_installed_apps(settings.INSTALLED_APPS) return state def teardown(state): try: # Removing the temporary TMPDIR. Ensure we pass in unicode # so that it will successfully remove temp trees containing # non-ASCII filenames on Windows. (We're assuming the temp dir # name itself does not contain non-ASCII characters.) shutil.rmtree(six.text_type(TMPDIR)) except OSError: print('Failed to remove temp directory: %s' % TMPDIR) # Restore the old settings. for key, value in state.items(): setattr(settings, key, value) def django_tests(verbosity, interactive, failfast, keepdb, reverse, test_labels, debug_sql): state = setup(verbosity, test_labels) extra_tests = [] if test_labels and 'postgres_tests' in test_labels and connection.vendor != 'postgresql': if verbosity >= 2: print("Removed postgres_tests from tests as we're not running with PostgreSQL.") test_labels.remove('postgres_tests') # Run the test suite, including the extra validation tests. if not hasattr(settings, 'TEST_RUNNER'): settings.TEST_RUNNER = 'django.test.runner.DiscoverRunner' TestRunner = get_runner(settings) test_runner = TestRunner( verbosity=verbosity, interactive=interactive, failfast=failfast, keepdb=keepdb, reverse=reverse, debug_sql=debug_sql, ) # Catch warnings thrown in test DB setup -- remove in Django 1.9 with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', "Custom SQL location '/models/sql' is deprecated, " "use '/sql' instead.", RemovedInDjango19Warning ) warnings.filterwarnings( 'ignore', 'initial_data fixtures are deprecated. Use data migrations instead.', RemovedInDjango19Warning ) failures = test_runner.run_tests( test_labels or get_installed(), extra_tests=extra_tests) teardown(state) return failures def bisect_tests(bisection_label, options, test_labels): state = setup(options.verbosity, test_labels) test_labels = test_labels or get_installed() print('***** Bisecting test suite: %s' % ' '.join(test_labels)) # Make sure the bisection point isn't in the test list # Also remove tests that need to be run in specific combinations for label in [bisection_label, 'model_inheritance_same_model_name']: try: test_labels.remove(label) except ValueError: pass subprocess_args = [ sys.executable, upath(__file__), '--settings=%s' % options.settings] if options.failfast: subprocess_args.append('--failfast') if options.verbosity: subprocess_args.append('--verbosity=%s' % options.verbosity) if not options.interactive: subprocess_args.append('--noinput') iteration = 1 while len(test_labels) > 1: midpoint = len(test_labels) // 2 test_labels_a = test_labels[:midpoint] + [bisection_label] test_labels_b = test_labels[midpoint:] + [bisection_label] print('***** Pass %da: Running the first half of the test suite' % iteration) print('***** Test labels: %s' % ' '.join(test_labels_a)) failures_a = subprocess.call(subprocess_args + test_labels_a) print('***** Pass %db: Running the second half of the test suite' % iteration) print('***** Test labels: %s' % ' '.join(test_labels_b)) print('') failures_b = subprocess.call(subprocess_args + test_labels_b) if failures_a and not failures_b: print("***** Problem found in first half. Bisecting again...") iteration = iteration + 1 test_labels = test_labels_a[:-1] elif failures_b and not failures_a: print("***** Problem found in second half. Bisecting again...") iteration = iteration + 1 test_labels = test_labels_b[:-1] elif failures_a and failures_b: print("***** Multiple sources of failure found") break else: print("***** No source of failure found... try pair execution (--pair)") break if len(test_labels) == 1: print("***** Source of error: %s" % test_labels[0]) teardown(state) def paired_tests(paired_test, options, test_labels): state = setup(options.verbosity, test_labels) test_labels = test_labels or get_installed() print('***** Trying paired execution') # Make sure the constant member of the pair isn't in the test list # Also remove tests that need to be run in specific combinations for label in [paired_test, 'model_inheritance_same_model_name']: try: test_labels.remove(label) except ValueError: pass subprocess_args = [ sys.executable, upath(__file__), '--settings=%s' % options.settings] if options.failfast: subprocess_args.append('--failfast') if options.verbosity: subprocess_args.append('--verbosity=%s' % options.verbosity) if not options.interactive: subprocess_args.append('--noinput') for i, label in enumerate(test_labels): print('***** %d of %d: Check test pairing with %s' % ( i + 1, len(test_labels), label)) failures = subprocess.call(subprocess_args + [label, paired_test]) if failures: print('***** Found problem pair with %s' % label) return print('***** No problem pair found') teardown(state) if __name__ == "__main__": parser = ArgumentParser(description="Run the Django test suite.") parser.add_argument('modules', nargs='*', metavar='module', help='Optional path(s) to test modules; e.g. "i18n" or ' '"i18n.tests.TranslationTests.test_lazy_objects".') parser.add_argument( '-v', '--verbosity', default=1, type=int, choices=[0, 1, 2, 3], help='Verbosity level; 0=minimal output, 1=normal output, 2=all output') parser.add_argument( '--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.') parser.add_argument( '--failfast', action='store_true', dest='failfast', default=False, help='Tells Django to stop running the test suite after first failed ' 'test.') parser.add_argument( '-k', '--keepdb', action='store_true', dest='keepdb', default=False, help='Tells Django to preserve the test database between runs.') parser.add_argument( '--settings', help='Python path to settings module, e.g. "myproject.settings". If ' 'this isn\'t provided, either the DJANGO_SETTINGS_MODULE ' 'environment variable or "test_sqlite" will be used.') parser.add_argument('--bisect', help='Bisect the test suite to discover a test that causes a test ' 'failure when combined with the named test.') parser.add_argument('--pair', help='Run the test suite in pairs with the named test to find problem ' 'pairs.') parser.add_argument('--reverse', action='store_true', default=False, help='Sort test suites and test cases in opposite order to debug ' 'test side effects not apparent with normal execution lineup.') parser.add_argument('--liveserver', help='Overrides the default address where the live server (used with ' 'LiveServerTestCase) is expected to run from. The default value ' 'is localhost:8081.') parser.add_argument( '--selenium', action='store_true', dest='selenium', default=False, help='Run the Selenium tests as well (if Selenium is installed)') parser.add_argument( '--debug-sql', action='store_true', dest='debug_sql', default=False, help='Turn on the SQL query logger within tests') options = parser.parse_args() # mock is a required dependency try: from django.test import mock # NOQA except ImportError: print( "Please install test dependencies first: \n" "$ pip install -r requirements/py%s.txt" % sys.version_info.major ) sys.exit(1) # Allow including a trailing slash on app_labels for tab completion convenience options.modules = [os.path.normpath(labels) for labels in options.modules] if options.settings: os.environ['DJANGO_SETTINGS_MODULE'] = options.settings else: if "DJANGO_SETTINGS_MODULE" not in os.environ: os.environ['DJANGO_SETTINGS_MODULE'] = 'test_sqlite' options.settings = os.environ['DJANGO_SETTINGS_MODULE'] if options.liveserver is not None: os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = options.liveserver if options.selenium: os.environ['DJANGO_SELENIUM_TESTS'] = '1' if options.bisect: bisect_tests(options.bisect, options, options.modules) elif options.pair: paired_tests(options.pair, options, options.modules) else: failures = django_tests(options.verbosity, options.interactive, options.failfast, options.keepdb, options.reverse, options.modules, options.debug_sql) if failures: sys.exit(bool(failures)) PKE-Go%/django_tests/test_sqlite.py# This is an example test settings file for use with the Django test suite. # # The 'sqlite3' backend requires only the ENGINE setting (an in- # memory database will be used). All other backends will require a # NAME and potentially authentication information. See the # following section in the docs for more information: # # https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/unit-tests/ # # The different databases that Django supports behave differently in certain # situations, so it is recommended to run the test suite against as many # database backends as possible. You may want to create a separate settings # file for each of the backends you test against. DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3' }, 'other': { 'ENGINE': 'django.db.backends.sqlite3', } } SECRET_KEY = "django_tests_secret_key" # Use a fast hasher to speed up tests. PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.MD5PasswordHasher', ) PKvn/Gdjango_tests/__init__.pyPKE-G~##!django_tests/sites_tests/tests.pyfrom __future__ import unicode_literals from django.apps import apps from django.conf import settings from django.contrib.sites import models from django.contrib.sites.management import create_default_site from django.contrib.sites.middleware import CurrentSiteMiddleware from django.contrib.sites.models import Site, clear_site_cache from django.contrib.sites.requests import RequestSite from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db.models.signals import post_migrate from django.http import HttpRequest from django.test import TestCase, modify_settings, override_settings from django.test.utils import captured_stdout @modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'}) class SitesFrameworkTests(TestCase): multi_db = True def setUp(self): self.site = Site( id=settings.SITE_ID, domain="example.com", name="example.com", ) self.site.save() def test_site_manager(self): # Make sure that get_current() does not return a deleted Site object. s = Site.objects.get_current() self.assertIsInstance(s, Site) s.delete() self.assertRaises(ObjectDoesNotExist, Site.objects.get_current) def test_site_cache(self): # After updating a Site object (e.g. via the admin), we shouldn't return a # bogus value from the SITE_CACHE. site = Site.objects.get_current() self.assertEqual("example.com", site.name) s2 = Site.objects.get(id=settings.SITE_ID) s2.name = "Example site" s2.save() site = Site.objects.get_current() self.assertEqual("Example site", site.name) def test_delete_all_sites_clears_cache(self): # When all site objects are deleted the cache should also # be cleared and get_current() should raise a DoesNotExist. self.assertIsInstance(Site.objects.get_current(), Site) Site.objects.all().delete() self.assertRaises(Site.DoesNotExist, Site.objects.get_current) @override_settings(ALLOWED_HOSTS=['example.com']) def test_get_current_site(self): # Test that the correct Site object is returned request = HttpRequest() request.META = { "SERVER_NAME": "example.com", "SERVER_PORT": "80", } site = get_current_site(request) self.assertIsInstance(site, Site) self.assertEqual(site.id, settings.SITE_ID) # Test that an exception is raised if the sites framework is installed # but there is no matching Site site.delete() self.assertRaises(ObjectDoesNotExist, get_current_site, request) # A RequestSite is returned if the sites framework is not installed with self.modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}): site = get_current_site(request) self.assertIsInstance(site, RequestSite) self.assertEqual(site.name, "example.com") @override_settings(SITE_ID='', ALLOWED_HOSTS=['example.com']) def test_get_current_site_no_site_id(self): request = HttpRequest() request.META = { "SERVER_NAME": "example.com", "SERVER_PORT": "80", } del settings.SITE_ID site = get_current_site(request) self.assertEqual(site.name, "example.com") def test_domain_name_with_whitespaces(self): # Regression for #17320 # Domain names are not allowed contain whitespace characters site = Site(name="test name", domain="test test") self.assertRaises(ValidationError, site.full_clean) site.domain = "test\ttest" self.assertRaises(ValidationError, site.full_clean) site.domain = "test\ntest" self.assertRaises(ValidationError, site.full_clean) def test_clear_site_cache(self): request = HttpRequest() request.META = { "SERVER_NAME": "example.com", "SERVER_PORT": "80", } self.assertEqual(models.SITE_CACHE, {}) get_current_site(request) expected_cache = {self.site.id: self.site} self.assertEqual(models.SITE_CACHE, expected_cache) with self.settings(SITE_ID=''): get_current_site(request) expected_cache.update({self.site.domain: self.site}) self.assertEqual(models.SITE_CACHE, expected_cache) clear_site_cache(Site, instance=self.site, using='default') self.assertEqual(models.SITE_CACHE, {}) @override_settings(SITE_ID='') def test_clear_site_cache_domain(self): site = Site.objects.create(name='example2.com', domain='example2.com') request = HttpRequest() request.META = { "SERVER_NAME": "example2.com", "SERVER_PORT": "80", } get_current_site(request) # prime the models.SITE_CACHE expected_cache = {site.domain: site} self.assertEqual(models.SITE_CACHE, expected_cache) # Site exists in 'default' database so using='other' shouldn't clear. clear_site_cache(Site, instance=site, using='other') self.assertEqual(models.SITE_CACHE, expected_cache) # using='default' should clear. clear_site_cache(Site, instance=site, using='default') self.assertEqual(models.SITE_CACHE, {}) class JustOtherRouter(object): def allow_migrate(self, db, app_label, **hints): return db == 'other' @modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'}) class CreateDefaultSiteTests(TestCase): multi_db = True def setUp(self): self.app_config = apps.get_app_config('sites') # Delete the site created as part of the default migration process. Site.objects.all().delete() def test_basic(self): """ #15346, #15573 - create_default_site() creates an example site only if none exist. """ with captured_stdout() as stdout: create_default_site(self.app_config) self.assertEqual(Site.objects.count(), 1) self.assertIn("Creating example.com", stdout.getvalue()) with captured_stdout() as stdout: create_default_site(self.app_config) self.assertEqual(Site.objects.count(), 1) self.assertEqual("", stdout.getvalue()) @override_settings(DATABASE_ROUTERS=[JustOtherRouter()]) def test_multi_db_with_router(self): """ #16353, #16828 - The default site creation should respect db routing. """ create_default_site(self.app_config, using='default', verbosity=0) create_default_site(self.app_config, using='other', verbosity=0) self.assertFalse(Site.objects.using('default').exists()) self.assertTrue(Site.objects.using('other').exists()) def test_multi_db(self): create_default_site(self.app_config, using='default', verbosity=0) create_default_site(self.app_config, using='other', verbosity=0) self.assertTrue(Site.objects.using('default').exists()) self.assertTrue(Site.objects.using('other').exists()) def test_save_another(self): """ #17415 - Another site can be created right after the default one. On some backends the sequence needs to be reset after saving with an explicit ID. Test that there isn't a sequence collisions by saving another site. This test is only meaningful with databases that use sequences for automatic primary keys such as PostgreSQL and Oracle. """ create_default_site(self.app_config, verbosity=0) Site(domain='example2.com', name='example2.com').save() def test_signal(self): """ #23641 - Sending the ``post_migrate`` signal triggers creation of the default site. """ post_migrate.send(sender=self.app_config, app_config=self.app_config, verbosity=0) self.assertTrue(Site.objects.exists()) @override_settings(SITE_ID=35696) def test_custom_site_id(self): """ #23945 - The configured ``SITE_ID`` should be respected. """ create_default_site(self.app_config, verbosity=0) self.assertEqual(Site.objects.get().pk, 35696) @override_settings() # Restore original ``SITE_ID`` afterwards. def test_no_site_id(self): """ #24488 - The pk should default to 1 if no ``SITE_ID`` is configured. """ del settings.SITE_ID create_default_site(self.app_config, verbosity=0) self.assertEqual(Site.objects.get().pk, 1) class MiddlewareTest(TestCase): def test_request(self): """ Makes sure that the request has correct `site` attribute. """ middleware = CurrentSiteMiddleware() request = HttpRequest() middleware.process_request(request) self.assertEqual(request.site.id, settings.SITE_ID) PKE-G$django_tests/sites_tests/__init__.pyPKE-Gnq#django_tests/user_commands/tests.pyimport os from django.apps import apps from django.core import management from django.core.management import BaseCommand, CommandError, find_commands from django.core.management.utils import find_command, popen_wrapper from django.db import connection from django.test import SimpleTestCase, ignore_warnings, override_settings from django.test.utils import captured_stderr, captured_stdout, extend_sys_path from django.utils import translation from django.utils._os import upath from django.utils.deprecation import RemovedInDjango110Warning from django.utils.six import StringIO # A minimal set of apps to avoid system checks running on all apps. @override_settings( INSTALLED_APPS=[ 'django.contrib.auth', 'django.contrib.contenttypes', 'user_commands', ], ) class CommandTests(SimpleTestCase): def test_command(self): out = StringIO() management.call_command('dance', stdout=out) self.assertIn("I don't feel like dancing Rock'n'Roll.\n", out.getvalue()) def test_command_style(self): out = StringIO() management.call_command('dance', style='Jive', stdout=out) self.assertIn("I don't feel like dancing Jive.\n", out.getvalue()) # Passing options as arguments also works (thanks argparse) management.call_command('dance', '--style', 'Jive', stdout=out) self.assertIn("I don't feel like dancing Jive.\n", out.getvalue()) def test_language_preserved(self): out = StringIO() with translation.override('fr'): management.call_command('dance', stdout=out) self.assertEqual(translation.get_language(), 'fr') def test_explode(self): """ Test that an unknown command raises CommandError """ self.assertRaises(CommandError, management.call_command, ('explode',)) def test_system_exit(self): """ Exception raised in a command should raise CommandError with call_command, but SystemExit when run from command line """ with self.assertRaises(CommandError): management.call_command('dance', example="raise") with captured_stderr() as stderr, self.assertRaises(SystemExit): management.ManagementUtility(['manage.py', 'dance', '--example=raise']).execute() self.assertIn("CommandError", stderr.getvalue()) def test_deactivate_locale_set(self): # Deactivate translation when set to true out = StringIO() with translation.override('pl'): management.call_command('leave_locale_alone_false', stdout=out) self.assertEqual(out.getvalue(), "") def test_configured_locale_preserved(self): # Leaves locale from settings when set to false out = StringIO() with translation.override('pl'): management.call_command('leave_locale_alone_true', stdout=out) self.assertEqual(out.getvalue(), "pl\n") def test_find_command_without_PATH(self): """ find_command should still work when the PATH environment variable doesn't exist (#22256). """ current_path = os.environ.pop('PATH', None) try: self.assertIsNone(find_command('_missing_')) finally: if current_path is not None: os.environ['PATH'] = current_path def test_discover_commands_in_eggs(self): """ Test that management commands can also be loaded from Python eggs. """ egg_dir = '%s/eggs' % os.path.dirname(upath(__file__)) egg_name = '%s/basic.egg' % egg_dir with extend_sys_path(egg_name): with self.settings(INSTALLED_APPS=['commandegg']): cmds = find_commands(os.path.join(apps.get_app_config('commandegg').path, 'management')) self.assertEqual(cmds, ['eggcommand']) def test_call_command_option_parsing(self): """ When passing the long option name to call_command, the available option key is the option dest name (#22985). """ out = StringIO() management.call_command('dance', stdout=out, opt_3=True) self.assertIn("option3", out.getvalue()) self.assertNotIn("opt_3", out.getvalue()) self.assertNotIn("opt-3", out.getvalue()) @ignore_warnings(category=RemovedInDjango110Warning) def test_optparse_compatibility(self): """ optparse should be supported during Django 1.8/1.9 releases. """ out = StringIO() management.call_command('optparse_cmd', stdout=out) self.assertEqual(out.getvalue(), "All right, let's dance Rock'n'Roll.\n") # Simulate command line execution with captured_stdout() as stdout, captured_stderr(): management.execute_from_command_line(['django-admin', 'optparse_cmd']) self.assertEqual(stdout.getvalue(), "All right, let's dance Rock'n'Roll.\n") def test_calling_a_command_with_only_empty_parameter_should_ends_gracefully(self): out = StringIO() management.call_command('hal', "--empty", stdout=out) self.assertIn("Dave, I can't do that.\n", out.getvalue()) def test_calling_command_with_app_labels_and_parameters_should_be_ok(self): out = StringIO() management.call_command('hal', 'myapp', "--verbosity", "3", stdout=out) self.assertIn("Dave, my mind is going. I can feel it. I can feel it.\n", out.getvalue()) def test_calling_command_with_parameters_and_app_labels_at_the_end_should_be_ok(self): out = StringIO() management.call_command('hal', "--verbosity", "3", "myapp", stdout=out) self.assertIn("Dave, my mind is going. I can feel it. I can feel it.\n", out.getvalue()) def test_calling_a_command_with_no_app_labels_and_parameters_should_raise_a_command_error(self): out = StringIO() with self.assertRaises(CommandError): management.call_command('hal', stdout=out) def test_output_transaction(self): out = StringIO() management.call_command('transaction', stdout=out, no_color=True) output = out.getvalue().strip() self.assertTrue(output.startswith(connection.ops.start_transaction_sql())) self.assertTrue(output.endswith(connection.ops.end_transaction_sql())) def test_call_command_no_checks(self): """ By default, call_command should not trigger the check framework, unless specifically asked. """ self.counter = 0 def patched_check(self_, **kwargs): self.counter = self.counter + 1 saved_check = BaseCommand.check BaseCommand.check = patched_check try: management.call_command("dance", verbosity=0) self.assertEqual(self.counter, 0) management.call_command("dance", verbosity=0, skip_checks=False) self.assertEqual(self.counter, 1) finally: BaseCommand.check = saved_check class UtilsTests(SimpleTestCase): def test_no_existent_external_program(self): self.assertRaises(CommandError, popen_wrapper, ['a_42_command_that_doesnt_exist_42']) PKE-G7UTT$django_tests/user_commands/models.py""" 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. """ PKE-G&django_tests/user_commands/__init__.pyPKE-G1django_tests/user_commands/management/__init__.pyPKE-G>XIdjango_tests/user_commands/management/commands/leave_locale_alone_true.pyfrom django.core.management.base import BaseCommand from django.utils import translation class Command(BaseCommand): can_import_settings = True leave_locale_alone = True def handle(self, *args, **options): return translation.get_language() PKE-Gmp  Jdjango_tests/user_commands/management/commands/leave_locale_alone_false.pyfrom django.core.management.base import BaseCommand from django.utils import translation class Command(BaseCommand): can_import_settings = True leave_locale_alone = False def handle(self, *args, **options): return translation.get_language() PKE-Ga7django_tests/user_commands/management/commands/dance.pyfrom django.core.management.base import BaseCommand, CommandError class Command(BaseCommand): help = "Dance around like a madman." args = '' requires_system_checks = True def add_arguments(self, parser): parser.add_argument("-s", "--style", default="Rock'n'Roll") parser.add_argument("-x", "--example") parser.add_argument("--opt-3", action='store_true', dest='option3') def handle(self, *args, **options): example = options["example"] if example == "raise": raise CommandError() if options['verbosity'] > 0: self.stdout.write("I don't feel like dancing %s." % options["style"]) self.stdout.write(','.join(options.keys())) PKE-G5django_tests/user_commands/management/commands/hal.pyfrom django.core.management.base import BaseCommand, CommandError class Command(BaseCommand): help = "Useless command." def add_arguments(self, parser): parser.add_argument('args', metavar='app_label', nargs='*', help='Specify the app label(s) to works on.') parser.add_argument('--empty', action='store_true', dest='empty', default=False, help="Do nothing.") def handle(self, *app_labels, **options): app_labels = set(app_labels) if options['empty']: self.stdout.write("Dave, I can't do that.") return if not app_labels: raise CommandError("I'm sorry Dave, I'm afraid I can't do that.") # raise an error if some --parameter is flowing from options to args for app_label in app_labels: if app_label.startswith('--'): raise CommandError("Sorry, Dave, I can't let you do that.") self.stdout.write("Dave, my mind is going. I can feel it. I can feel it.") PKE-Gy>django_tests/user_commands/management/commands/optparse_cmd.pyfrom optparse import make_option from django.core.management.base import BaseCommand class Command(BaseCommand): help = "Test optparse compatibility." args = '' option_list = BaseCommand.option_list + ( make_option("-s", "--style", default="Rock'n'Roll"), make_option("-x", "--example") ) def handle(self, *args, **options): options["example"] # BaseCommand default option is available options['verbosity'] assert isinstance(options['verbosity'], int), "verbosity option is not int, but %s" % type(options['verbosity']) self.stdout.write("All right, let's dance %s." % options["style"]) PKE-G:django_tests/user_commands/management/commands/__init__.pyPKE-Gk=django_tests/user_commands/management/commands/transaction.pyfrom django.core.management.base import BaseCommand class Command(BaseCommand): help = "Say hello." args = '' output_transaction = True def handle(self, *args, **options): return 'Hello!' PKE-G6mE[E["django_tests/file_uploads/tests.py#! -*- coding: utf-8 -*- from __future__ import unicode_literals import base64 import errno import hashlib import json import os import shutil import tempfile as sys_tempfile import unittest from django.core.files import temp as tempfile from django.core.files.uploadedfile import SimpleUploadedFile from django.http.multipartparser import MultiPartParser, parse_header from django.test import TestCase, client, override_settings from django.utils.encoding import force_bytes from django.utils.http import urlquote from django.utils.six import PY2, BytesIO, StringIO from . import uploadhandler from .models import FileModel UNICODE_FILENAME = 'test-0123456789_中文_Orléans.jpg' MEDIA_ROOT = sys_tempfile.mkdtemp() UPLOAD_TO = os.path.join(MEDIA_ROOT, 'test_upload') @override_settings(MEDIA_ROOT=MEDIA_ROOT, ROOT_URLCONF='file_uploads.urls', MIDDLEWARE_CLASSES=()) class FileUploadTests(TestCase): @classmethod def setUpClass(cls): super(FileUploadTests, cls).setUpClass() if not os.path.isdir(MEDIA_ROOT): os.makedirs(MEDIA_ROOT) @classmethod def tearDownClass(cls): shutil.rmtree(MEDIA_ROOT) super(FileUploadTests, cls).tearDownClass() def test_simple_upload(self): with open(__file__, 'rb') as fp: post_data = { 'name': 'Ringo', 'file_field': fp, } response = self.client.post('/upload/', post_data) self.assertEqual(response.status_code, 200) def test_large_upload(self): file = tempfile.NamedTemporaryFile with file(suffix=".file1") as file1, file(suffix=".file2") as file2: file1.write(b'a' * (2 ** 21)) file1.seek(0) file2.write(b'a' * (10 * 2 ** 20)) file2.seek(0) post_data = { 'name': 'Ringo', 'file_field1': file1, 'file_field2': file2, } for key in list(post_data): try: post_data[key + '_hash'] = hashlib.sha1(post_data[key].read()).hexdigest() post_data[key].seek(0) except AttributeError: post_data[key + '_hash'] = hashlib.sha1(force_bytes(post_data[key])).hexdigest() response = self.client.post('/verify/', post_data) self.assertEqual(response.status_code, 200) def _test_base64_upload(self, content, encode=base64.b64encode): payload = client.FakePayload("\r\n".join([ '--' + client.BOUNDARY, 'Content-Disposition: form-data; name="file"; filename="test.txt"', 'Content-Type: application/octet-stream', 'Content-Transfer-Encoding: base64', ''])) payload.write(b"\r\n" + encode(force_bytes(content)) + b"\r\n") payload.write('--' + client.BOUNDARY + '--\r\n') r = { 'CONTENT_LENGTH': len(payload), 'CONTENT_TYPE': client.MULTIPART_CONTENT, 'PATH_INFO': "/echo_content/", 'REQUEST_METHOD': 'POST', 'wsgi.input': payload, } response = self.client.request(**r) received = json.loads(response.content.decode('utf-8')) self.assertEqual(received['file'], content) def test_base64_upload(self): self._test_base64_upload("This data will be transmitted base64-encoded.") def test_big_base64_upload(self): self._test_base64_upload("Big data" * 68000) # > 512Kb def test_big_base64_newlines_upload(self): self._test_base64_upload( # encodestring is a deprecated alias on Python 3 "Big data" * 68000, encode=base64.encodestring if PY2 else base64.encodebytes) def test_unicode_file_name(self): tdir = sys_tempfile.mkdtemp() self.addCleanup(shutil.rmtree, tdir, True) # This file contains chinese symbols and an accented char in the name. with open(os.path.join(tdir, UNICODE_FILENAME), 'w+b') as file1: file1.write(b'b' * (2 ** 10)) file1.seek(0) post_data = { 'file_unicode': file1, } response = self.client.post('/unicode_name/', post_data) self.assertEqual(response.status_code, 200) def test_unicode_file_name_rfc2231(self): """ Test receiving file upload when filename is encoded with RFC2231 (#22971). """ payload = client.FakePayload() payload.write('\r\n'.join([ '--' + client.BOUNDARY, 'Content-Disposition: form-data; name="file_unicode"; filename*=UTF-8\'\'%s' % urlquote(UNICODE_FILENAME), 'Content-Type: application/octet-stream', '', 'You got pwnd.\r\n', '\r\n--' + client.BOUNDARY + '--\r\n' ])) r = { 'CONTENT_LENGTH': len(payload), 'CONTENT_TYPE': client.MULTIPART_CONTENT, 'PATH_INFO': "/unicode_name/", 'REQUEST_METHOD': 'POST', 'wsgi.input': payload, } response = self.client.request(**r) self.assertEqual(response.status_code, 200) def test_unicode_name_rfc2231(self): """ Test receiving file upload when filename is encoded with RFC2231 (#22971). """ payload = client.FakePayload() payload.write('\r\n'.join([ '--' + client.BOUNDARY, 'Content-Disposition: form-data; name*=UTF-8\'\'file_unicode; filename*=UTF-8\'\'%s' % urlquote(UNICODE_FILENAME), 'Content-Type: application/octet-stream', '', 'You got pwnd.\r\n', '\r\n--' + client.BOUNDARY + '--\r\n' ])) r = { 'CONTENT_LENGTH': len(payload), 'CONTENT_TYPE': client.MULTIPART_CONTENT, 'PATH_INFO': "/unicode_name/", 'REQUEST_METHOD': 'POST', 'wsgi.input': payload, } response = self.client.request(**r) self.assertEqual(response.status_code, 200) def test_dangerous_file_names(self): """Uploaded file names should be sanitized before ever reaching the view.""" # This test simulates possible directory traversal attacks by a # malicious uploader We have to do some monkeybusiness here to construct # a malicious payload with an invalid file name (containing os.sep or # os.pardir). This similar to what an attacker would need to do when # trying such an attack. scary_file_names = [ "/tmp/hax0rd.txt", # Absolute path, *nix-style. "C:\\Windows\\hax0rd.txt", # Absolute path, win-style. "C:/Windows/hax0rd.txt", # Absolute path, broken-style. "\\tmp\\hax0rd.txt", # Absolute path, broken in a different way. "/tmp\\hax0rd.txt", # Absolute path, broken by mixing. "subdir/hax0rd.txt", # Descendant path, *nix-style. "subdir\\hax0rd.txt", # Descendant path, win-style. "sub/dir\\hax0rd.txt", # Descendant path, mixed. "../../hax0rd.txt", # Relative path, *nix-style. "..\\..\\hax0rd.txt", # Relative path, win-style. "../..\\hax0rd.txt" # Relative path, mixed. ] payload = client.FakePayload() for i, name in enumerate(scary_file_names): payload.write('\r\n'.join([ '--' + client.BOUNDARY, 'Content-Disposition: form-data; name="file%s"; filename="%s"' % (i, name), 'Content-Type: application/octet-stream', '', 'You got pwnd.\r\n' ])) payload.write('\r\n--' + client.BOUNDARY + '--\r\n') r = { 'CONTENT_LENGTH': len(payload), 'CONTENT_TYPE': client.MULTIPART_CONTENT, 'PATH_INFO': "/echo/", 'REQUEST_METHOD': 'POST', 'wsgi.input': payload, } response = self.client.request(**r) # The filenames should have been sanitized by the time it got to the view. received = json.loads(response.content.decode('utf-8')) for i, name in enumerate(scary_file_names): got = received["file%s" % i] self.assertEqual(got, "hax0rd.txt") def test_filename_overflow(self): """File names over 256 characters (dangerous on some platforms) get fixed up.""" long_str = 'f' * 300 cases = [ # field name, filename, expected ('long_filename', '%s.txt' % long_str, '%s.txt' % long_str[:251]), ('long_extension', 'foo.%s' % long_str, '.%s' % long_str[:254]), ('no_extension', long_str, long_str[:255]), ('no_filename', '.%s' % long_str, '.%s' % long_str[:254]), ('long_everything', '%s.%s' % (long_str, long_str), '.%s' % long_str[:254]), ] payload = client.FakePayload() for name, filename, _ in cases: payload.write("\r\n".join([ '--' + client.BOUNDARY, 'Content-Disposition: form-data; name="{}"; filename="{}"', 'Content-Type: application/octet-stream', '', 'Oops.', '' ]).format(name, filename)) payload.write('\r\n--' + client.BOUNDARY + '--\r\n') r = { 'CONTENT_LENGTH': len(payload), 'CONTENT_TYPE': client.MULTIPART_CONTENT, 'PATH_INFO': "/echo/", 'REQUEST_METHOD': 'POST', 'wsgi.input': payload, } response = self.client.request(**r) result = json.loads(response.content.decode('utf-8')) for name, _, expected in cases: got = result[name] self.assertEqual(expected, got, 'Mismatch for {}'.format(name)) self.assertLess(len(got), 256, "Got a long file name (%s characters)." % len(got)) def test_file_content(self): file = tempfile.NamedTemporaryFile with file(suffix=".ctype_extra") as no_content_type, file(suffix=".ctype_extra") as simple_file: no_content_type.write(b'no content') no_content_type.seek(0) simple_file.write(b'text content') simple_file.seek(0) simple_file.content_type = 'text/plain' string_io = StringIO('string content') bytes_io = BytesIO(b'binary content') response = self.client.post('/echo_content/', { 'no_content_type': no_content_type, 'simple_file': simple_file, 'string': string_io, 'binary': bytes_io, }) received = json.loads(response.content.decode('utf-8')) self.assertEqual(received['no_content_type'], 'no content') self.assertEqual(received['simple_file'], 'text content') self.assertEqual(received['string'], 'string content') self.assertEqual(received['binary'], 'binary content') def test_content_type_extra(self): """Uploaded files may have content type parameters available.""" file = tempfile.NamedTemporaryFile with file(suffix=".ctype_extra") as no_content_type, file(suffix=".ctype_extra") as simple_file: no_content_type.write(b'something') no_content_type.seek(0) simple_file.write(b'something') simple_file.seek(0) simple_file.content_type = 'text/plain; test-key=test_value' response = self.client.post('/echo_content_type_extra/', { 'no_content_type': no_content_type, 'simple_file': simple_file, }) received = json.loads(response.content.decode('utf-8')) self.assertEqual(received['no_content_type'], {}) self.assertEqual(received['simple_file'], {'test-key': 'test_value'}) def test_truncated_multipart_handled_gracefully(self): """ If passed an incomplete multipart message, MultiPartParser does not attempt to read beyond the end of the stream, and simply will handle the part that can be parsed gracefully. """ payload_str = "\r\n".join([ '--' + client.BOUNDARY, 'Content-Disposition: form-data; name="file"; filename="foo.txt"', 'Content-Type: application/octet-stream', '', 'file contents' '--' + client.BOUNDARY + '--', '', ]) payload = client.FakePayload(payload_str[:-10]) r = { 'CONTENT_LENGTH': len(payload), 'CONTENT_TYPE': client.MULTIPART_CONTENT, 'PATH_INFO': '/echo/', 'REQUEST_METHOD': 'POST', 'wsgi.input': payload, } got = json.loads(self.client.request(**r).content.decode('utf-8')) self.assertEqual(got, {}) def test_empty_multipart_handled_gracefully(self): """ If passed an empty multipart message, MultiPartParser will return an empty QueryDict. """ r = { 'CONTENT_LENGTH': 0, 'CONTENT_TYPE': client.MULTIPART_CONTENT, 'PATH_INFO': '/echo/', 'REQUEST_METHOD': 'POST', 'wsgi.input': client.FakePayload(b''), } got = json.loads(self.client.request(**r).content.decode('utf-8')) self.assertEqual(got, {}) def test_custom_upload_handler(self): file = tempfile.NamedTemporaryFile with file() as smallfile, file() as bigfile: # A small file (under the 5M quota) smallfile.write(b'a' * (2 ** 21)) smallfile.seek(0) # A big file (over the quota) bigfile.write(b'a' * (10 * 2 ** 20)) bigfile.seek(0) # Small file posting should work. response = self.client.post('/quota/', {'f': smallfile}) got = json.loads(response.content.decode('utf-8')) self.assertIn('f', got) # Large files don't go through. response = self.client.post("/quota/", {'f': bigfile}) got = json.loads(response.content.decode('utf-8')) self.assertNotIn('f', got) def test_broken_custom_upload_handler(self): with tempfile.NamedTemporaryFile() as file: file.write(b'a' * (2 ** 21)) file.seek(0) # AttributeError: You cannot alter upload handlers after the upload has been processed. self.assertRaises( AttributeError, self.client.post, '/quota/broken/', {'f': file} ) def test_fileupload_getlist(self): file = tempfile.NamedTemporaryFile with file() as file1, file() as file2, file() as file2a: file1.write(b'a' * (2 ** 23)) file1.seek(0) file2.write(b'a' * (2 * 2 ** 18)) file2.seek(0) file2a.write(b'a' * (5 * 2 ** 20)) file2a.seek(0) response = self.client.post('/getlist_count/', { 'file1': file1, 'field1': 'test', 'field2': 'test3', 'field3': 'test5', 'field4': 'test6', 'field5': 'test7', 'file2': (file2, file2a) }) got = json.loads(response.content.decode('utf-8')) self.assertEqual(got.get('file1'), 1) self.assertEqual(got.get('file2'), 2) def test_fileuploads_closed_at_request_end(self): file = tempfile.NamedTemporaryFile with file() as f1, file() as f2a, file() as f2b: response = self.client.post('/fd_closing/t/', { 'file': f1, 'file2': (f2a, f2b), }) request = response.wsgi_request # Check that the files got actually parsed. self.assertTrue(hasattr(request, '_files')) file = request._files['file'] self.assertTrue(file.closed) files = request._files.getlist('file2') self.assertTrue(files[0].closed) self.assertTrue(files[1].closed) def test_no_parsing_triggered_by_fd_closing(self): file = tempfile.NamedTemporaryFile with file() as f1, file() as f2a, file() as f2b: response = self.client.post('/fd_closing/f/', { 'file': f1, 'file2': (f2a, f2b), }) request = response.wsgi_request # Check that the fd closing logic doesn't trigger parsing of the stream self.assertFalse(hasattr(request, '_files')) def test_file_error_blocking(self): """ The server should not block when there are upload errors (bug #8622). This can happen if something -- i.e. an exception handler -- tries to access POST while handling an error in parsing POST. This shouldn't cause an infinite loop! """ class POSTAccessingHandler(client.ClientHandler): """A handler that'll access POST during an exception.""" def handle_uncaught_exception(self, request, resolver, exc_info): ret = super(POSTAccessingHandler, self).handle_uncaught_exception(request, resolver, exc_info) request.POST # evaluate return ret # Maybe this is a little more complicated that it needs to be; but if # the django.test.client.FakePayload.read() implementation changes then # this test would fail. So we need to know exactly what kind of error # it raises when there is an attempt to read more than the available bytes: try: client.FakePayload(b'a').read(2) except Exception as err: reference_error = err # install the custom handler that tries to access request.POST self.client.handler = POSTAccessingHandler() with open(__file__, 'rb') as fp: post_data = { 'name': 'Ringo', 'file_field': fp, } try: self.client.post('/upload_errors/', post_data) except reference_error.__class__ as err: self.assertFalse( str(err) == str(reference_error), "Caught a repeated exception that'll cause an infinite loop in file uploads." ) except Exception as err: # CustomUploadError is the error that should have been raised self.assertEqual(err.__class__, uploadhandler.CustomUploadError) def test_filename_case_preservation(self): """ The storage backend shouldn't mess with the case of the filenames uploaded. """ # Synthesize the contents of a file upload with a mixed case filename # so we don't have to carry such a file in the Django tests source code # tree. vars = {'boundary': 'oUrBoUnDaRyStRiNg'} post_data = [ '--%(boundary)s', 'Content-Disposition: form-data; name="file_field"; filename="MiXeD_cAsE.txt"', 'Content-Type: application/octet-stream', '', 'file contents\n' '', '--%(boundary)s--\r\n', ] response = self.client.post( '/filename_case/', '\r\n'.join(post_data) % vars, 'multipart/form-data; boundary=%(boundary)s' % vars ) self.assertEqual(response.status_code, 200) id = int(response.content) obj = FileModel.objects.get(pk=id) # The name of the file uploaded and the file stored in the server-side # shouldn't differ. self.assertEqual(os.path.basename(obj.testfile.path), 'MiXeD_cAsE.txt') @override_settings(MEDIA_ROOT=MEDIA_ROOT) class DirectoryCreationTests(TestCase): """ Tests for error handling during directory creation via _save_FIELD_file (ticket #6450) """ @classmethod def setUpClass(cls): super(DirectoryCreationTests, cls).setUpClass() if not os.path.isdir(MEDIA_ROOT): os.makedirs(MEDIA_ROOT) @classmethod def tearDownClass(cls): shutil.rmtree(MEDIA_ROOT) super(DirectoryCreationTests, cls).tearDownClass() def setUp(self): self.obj = FileModel() def test_readonly_root(self): """Permission errors are not swallowed""" os.chmod(MEDIA_ROOT, 0o500) self.addCleanup(os.chmod, MEDIA_ROOT, 0o700) try: self.obj.testfile.save('foo.txt', SimpleUploadedFile('foo.txt', b'x')) except OSError as err: self.assertEqual(err.errno, errno.EACCES) except Exception: self.fail("OSError [Errno %s] not raised." % errno.EACCES) def test_not_a_directory(self): """The correct IOError is raised when the upload directory name exists but isn't a directory""" # Create a file with the upload directory name open(UPLOAD_TO, 'wb').close() self.addCleanup(os.remove, UPLOAD_TO) with self.assertRaises(IOError) as exc_info: with SimpleUploadedFile('foo.txt', b'x') as file: self.obj.testfile.save('foo.txt', file) # The test needs to be done on a specific string as IOError # is raised even without the patch (just not early enough) self.assertEqual(exc_info.exception.args[0], "%s exists and is not a directory." % UPLOAD_TO) class MultiParserTests(unittest.TestCase): def test_empty_upload_handlers(self): # We're not actually parsing here; just checking if the parser properly # instantiates with empty upload handlers. MultiPartParser({ 'CONTENT_TYPE': 'multipart/form-data; boundary=_foo', 'CONTENT_LENGTH': '1' }, StringIO('x'), [], 'utf-8') def test_rfc2231_parsing(self): test_data = ( (b"Content-Type: application/x-stuff; title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A", "This is ***fun***"), (b"Content-Type: application/x-stuff; title*=UTF-8''foo-%c3%a4.html", "foo-ä.html"), (b"Content-Type: application/x-stuff; title*=iso-8859-1''foo-%E4.html", "foo-ä.html"), ) for raw_line, expected_title in test_data: parsed = parse_header(raw_line) self.assertEqual(parsed[1]['title'], expected_title) def test_rfc2231_wrong_title(self): """ Test wrongly formatted RFC 2231 headers (missing double single quotes). Parsing should not crash (#24209). """ test_data = ( (b"Content-Type: application/x-stuff; title*='This%20is%20%2A%2A%2Afun%2A%2A%2A", b"'This%20is%20%2A%2A%2Afun%2A%2A%2A"), (b"Content-Type: application/x-stuff; title*='foo.html", b"'foo.html"), (b"Content-Type: application/x-stuff; title*=bar.html", b"bar.html"), ) for raw_line, expected_title in test_data: parsed = parse_header(raw_line) self.assertEqual(parsed[1]['title'], expected_title) PKE-G"H!django_tests/file_uploads/urls.pyfrom django.conf.urls import url from . import views urlpatterns = [ url(r'^upload/$', views.file_upload_view), url(r'^verify/$', views.file_upload_view_verify), url(r'^unicode_name/$', views.file_upload_unicode_name), url(r'^echo/$', views.file_upload_echo), url(r'^echo_content_type_extra/$', views.file_upload_content_type_extra), url(r'^echo_content/$', views.file_upload_echo_content), url(r'^quota/$', views.file_upload_quota), url(r'^quota/broken/$', views.file_upload_quota_broken), url(r'^getlist_count/$', views.file_upload_getlist_count), url(r'^upload_errors/$', views.file_upload_errors), url(r'^filename_case/$', views.file_upload_filename_case_view), url(r'^fd_closing/(?Pt|f)/$', views.file_upload_fd_closing), ] PKE-G *django_tests/file_uploads/uploadhandler.py""" Upload handlers to test the upload API. """ from django.core.files.uploadhandler import FileUploadHandler, StopUpload class QuotaUploadHandler(FileUploadHandler): """ This test upload handler terminates the connection if more than a quota (5MB) is uploaded. """ QUOTA = 5 * 2 ** 20 # 5 MB def __init__(self, request=None): super(QuotaUploadHandler, self).__init__(request) self.total_upload = 0 def receive_data_chunk(self, raw_data, start): self.total_upload += len(raw_data) if self.total_upload >= self.QUOTA: raise StopUpload(connection_reset=True) return raw_data def file_complete(self, file_size): return None class CustomUploadError(Exception): pass class ErroringUploadHandler(FileUploadHandler): """A handler that raises an exception.""" def receive_data_chunk(self, raw_data, start): raise CustomUploadError("Oops!") PKE-G?'۔oo"django_tests/file_uploads/views.pyfrom __future__ import unicode_literals import hashlib import json import os from django.core.files.uploadedfile import UploadedFile from django.http import HttpResponse, HttpResponseServerError from django.utils import six from django.utils.encoding import force_bytes, smart_str from .models import FileModel from .tests import UNICODE_FILENAME, UPLOAD_TO from .uploadhandler import ErroringUploadHandler, QuotaUploadHandler def file_upload_view(request): """ Check that a file upload can be updated into the POST dictionary without going pear-shaped. """ form_data = request.POST.copy() form_data.update(request.FILES) if isinstance(form_data.get('file_field'), UploadedFile) and isinstance(form_data['name'], six.text_type): # If a file is posted, the dummy client should only post the file name, # not the full path. if os.path.dirname(form_data['file_field'].name) != '': return HttpResponseServerError() return HttpResponse('') else: return HttpResponseServerError() def file_upload_view_verify(request): """ Use the sha digest hash to verify the uploaded contents. """ form_data = request.POST.copy() form_data.update(request.FILES) for key, value in form_data.items(): if key.endswith('_hash'): continue if key + '_hash' not in form_data: continue submitted_hash = form_data[key + '_hash'] if isinstance(value, UploadedFile): new_hash = hashlib.sha1(value.read()).hexdigest() else: new_hash = hashlib.sha1(force_bytes(value)).hexdigest() if new_hash != submitted_hash: return HttpResponseServerError() # Adding large file to the database should succeed largefile = request.FILES['file_field2'] obj = FileModel() obj.testfile.save(largefile.name, largefile) return HttpResponse('') def file_upload_unicode_name(request): # Check to see if unicode name came through properly. if not request.FILES['file_unicode'].name.endswith(UNICODE_FILENAME): return HttpResponseServerError() response = None # Check to make sure the exotic characters are preserved even # through file save. uni_named_file = request.FILES['file_unicode'] obj = FileModel.objects.create(testfile=uni_named_file) full_name = '%s/%s' % (UPLOAD_TO, uni_named_file.name) if not os.path.exists(full_name): response = HttpResponseServerError() # Cleanup the object with its exotic file name immediately. # (shutil.rmtree used elsewhere in the tests to clean up the # upload directory has been seen to choke on unicode # filenames on Windows.) obj.delete() os.unlink(full_name) if response: return response else: return HttpResponse('') def file_upload_echo(request): """ Simple view to echo back info about uploaded files for tests. """ r = {k: f.name for k, f in request.FILES.items()} return HttpResponse(json.dumps(r)) def file_upload_echo_content(request): """ Simple view to echo back the content of uploaded files for tests. """ r = {k: f.read().decode('utf-8') for k, f in request.FILES.items()} return HttpResponse(json.dumps(r)) def file_upload_quota(request): """ Dynamically add in an upload handler. """ request.upload_handlers.insert(0, QuotaUploadHandler()) return file_upload_echo(request) def file_upload_quota_broken(request): """ You can't change handlers after reading FILES; this view shouldn't work. """ response = file_upload_echo(request) request.upload_handlers.insert(0, QuotaUploadHandler()) return response def file_upload_getlist_count(request): """ Check the .getlist() function to ensure we receive the correct number of files. """ file_counts = {} for key in request.FILES.keys(): file_counts[key] = len(request.FILES.getlist(key)) return HttpResponse(json.dumps(file_counts)) def file_upload_errors(request): request.upload_handlers.insert(0, ErroringUploadHandler()) return file_upload_echo(request) def file_upload_filename_case_view(request): """ Check adding the file to the database will preserve the filename case. """ file = request.FILES['file_field'] obj = FileModel() obj.testfile.save(file.name, file) return HttpResponse('%d' % obj.pk) def file_upload_content_type_extra(request): """ Simple view to echo back extra content-type parameters. """ params = {} for file_name, uploadedfile in request.FILES.items(): params[file_name] = { k: smart_str(v) for k, v in uploadedfile.content_type_extra.items() } return HttpResponse(json.dumps(params)) def file_upload_fd_closing(request, access): if access == 't': request.FILES # Trigger file parsing. return HttpResponse('') PKE-G ww#django_tests/file_uploads/models.pyfrom django.db import models class FileModel(models.Model): testfile = models.FileField(upload_to='test_upload') PKE-G%django_tests/file_uploads/__init__.pyPKE-G3? !django_tests/m2m_and_m2o/tests.pyfrom django.db.models import Q from django.test import TestCase from .models import Issue, UnicodeReferenceModel, User class RelatedObjectTests(TestCase): def test_related_objects_have_name_attribute(self): for field_name in ('test_issue_client', 'test_issue_cc'): obj = User._meta.get_field(field_name) self.assertEqual(field_name, obj.field.related_query_name()) def test_m2m_and_m2o(self): r = User.objects.create(username="russell") g = User.objects.create(username="gustav") i1 = Issue(num=1) i1.client = r i1.save() i2 = Issue(num=2) i2.client = r i2.save() i2.cc.add(r) i3 = Issue(num=3) i3.client = g i3.save() i3.cc.add(r) self.assertQuerysetEqual( Issue.objects.filter(client=r.id), [ 1, 2, ], lambda i: i.num ) self.assertQuerysetEqual( Issue.objects.filter(client=g.id), [ 3, ], lambda i: i.num ) self.assertQuerysetEqual( Issue.objects.filter(cc__id__exact=g.id), [] ) self.assertQuerysetEqual( Issue.objects.filter(cc__id__exact=r.id), [ 2, 3, ], lambda i: i.num ) # These queries combine results from the m2m and the m2o relationships. # They're three ways of saying the same thing. self.assertQuerysetEqual( Issue.objects.filter(Q(cc__id__exact=r.id) | Q(client=r.id)), [ 1, 2, 3, ], lambda i: i.num ) self.assertQuerysetEqual( Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id), [ 1, 2, 3, ], lambda i: i.num ) self.assertQuerysetEqual( Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id)), [ 1, 2, 3, ], lambda i: i.num ) class RelatedObjectUnicodeTests(TestCase): def test_m2m_with_unicode_reference(self): """ Regression test for #6045: references to other models can be unicode strings, providing they are directly convertible to ASCII. """ m1 = UnicodeReferenceModel.objects.create() m2 = UnicodeReferenceModel.objects.create() m2.others.add(m1) # used to cause an error (see ticket #6045) m2.save() list(m2.others.all()) # Force retrieval. PKE-GmGG"django_tests/m2m_and_m2o/models.py""" Many-to-many and many-to-one relationships to the same table Make sure to set ``related_name`` if you use relationships to the same table. """ from __future__ import unicode_literals from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible class User(models.Model): username = models.CharField(max_length=20) @python_2_unicode_compatible class Issue(models.Model): num = models.IntegerField() cc = models.ManyToManyField(User, blank=True, related_name='test_issue_cc') client = models.ForeignKey(User, related_name='test_issue_client') def __str__(self): return six.text_type(self.num) class Meta: ordering = ('num',) class UnicodeReferenceModel(models.Model): others = models.ManyToManyField("UnicodeReferenceModel") PKE-G$django_tests/m2m_and_m2o/__init__.pyPKE-GHjW django_tests/forms_tests/urls.pyfrom django.conf.urls import url from .views import ArticleFormView urlpatterns = [ url(r'^model_form/(?P[0-9]+)/$', ArticleFormView.as_view(), name="article_form"), ] PKE-G6/!django_tests/forms_tests/views.pyfrom django.views.generic.edit import UpdateView from .models import Article class ArticleFormView(UpdateView): model = Article success_url = '/' fields = '__all__' PKE-GsL^II"django_tests/forms_tests/models.py# -*- coding: utf-8 -*- from __future__ import unicode_literals import datetime import itertools import tempfile from django.core.files.storage import FileSystemStorage from django.db import models from django.utils.encoding import python_2_unicode_compatible callable_default_counter = itertools.count() callable_default = lambda: next(callable_default_counter) temp_storage = FileSystemStorage(location=tempfile.mkdtemp()) class BoundaryModel(models.Model): positive_integer = models.PositiveIntegerField(null=True, blank=True) class Defaults(models.Model): name = models.CharField(max_length=255, default='class default value') def_date = models.DateField(default=datetime.date(1980, 1, 1)) value = models.IntegerField(default=42) callable_default = models.IntegerField(default=callable_default) class ChoiceModel(models.Model): """For ModelChoiceField and ModelMultipleChoiceField tests.""" CHOICES = [ ('', 'No Preference'), ('f', 'Foo'), ('b', 'Bar'), ] INTEGER_CHOICES = [ (None, 'No Preference'), (1, 'Foo'), (2, 'Bar'), ] STRING_CHOICES_WITH_NONE = [ (None, 'No Preference'), ('f', 'Foo'), ('b', 'Bar'), ] name = models.CharField(max_length=10) choice = models.CharField(max_length=2, blank=True, choices=CHOICES) choice_string_w_none = models.CharField( max_length=2, blank=True, null=True, choices=STRING_CHOICES_WITH_NONE) choice_integer = models.IntegerField(choices=INTEGER_CHOICES, blank=True, null=True) @python_2_unicode_compatible class ChoiceOptionModel(models.Model): """Destination for ChoiceFieldModel's ForeignKey. Can't reuse ChoiceModel because error_message tests require that it have no instances.""" name = models.CharField(max_length=10) class Meta: ordering = ('name',) def __str__(self): return 'ChoiceOption %d' % self.pk class ChoiceFieldModel(models.Model): """Model with ForeignKey to another model, for testing ModelForm generation with ModelChoiceField.""" choice = models.ForeignKey(ChoiceOptionModel, blank=False, default=lambda: ChoiceOptionModel.objects.get(name='default')) choice_int = models.ForeignKey(ChoiceOptionModel, blank=False, related_name='choice_int', default=lambda: 1) multi_choice = models.ManyToManyField(ChoiceOptionModel, blank=False, related_name='multi_choice', default=lambda: ChoiceOptionModel.objects.filter(name='default')) multi_choice_int = models.ManyToManyField(ChoiceOptionModel, blank=False, related_name='multi_choice_int', default=lambda: [1]) class OptionalMultiChoiceModel(models.Model): multi_choice = models.ManyToManyField(ChoiceOptionModel, blank=False, related_name='not_relevant', default=lambda: ChoiceOptionModel.objects.filter(name='default')) multi_choice_optional = models.ManyToManyField(ChoiceOptionModel, blank=True, related_name='not_relevant2') class FileModel(models.Model): file = models.FileField(storage=temp_storage, upload_to='tests') @python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=10) def __str__(self): return '%s' % self.name class Cheese(models.Model): name = models.CharField(max_length=100) class Article(models.Model): content = models.TextField() PKE-G$django_tests/forms_tests/__init__.pyPKE-GZ1django_tests/forms_tests/tests/test_validators.pyfrom __future__ import unicode_literals import re from unittest import TestCase from django import forms from django.core import validators from django.core.exceptions import ValidationError class UserForm(forms.Form): full_name = forms.CharField( max_length=50, validators=[ validators.validate_integer, validators.validate_email, ] ) string = forms.CharField( max_length=50, validators=[ validators.RegexValidator( regex='^[a-zA-Z]*$', message="Letters only.", ) ] ) ignore_case_string = forms.CharField( max_length=50, validators=[ validators.RegexValidator( regex='^[a-z]*$', message="Letters only.", flags=re.IGNORECASE, ) ] ) class TestFieldWithValidators(TestCase): def test_all_errors_get_reported(self): form = UserForm({'full_name': 'not int nor mail', 'string': '2 is not correct', 'ignore_case_string': "IgnORE Case strIng"}) self.assertRaises(ValidationError, form.fields['full_name'].clean, 'not int nor mail') try: form.fields['full_name'].clean('not int nor mail') except ValidationError as e: self.assertEqual(2, len(e.messages)) self.assertFalse(form.is_valid()) self.assertEqual(form.errors['string'], ["Letters only."]) self.assertEqual(form.errors['string'], ["Letters only."]) PKE-Gm 8?8?'django_tests/forms_tests/tests/tests.py# -*- coding: utf-8 -*- from __future__ import unicode_literals import datetime from django.core.files.uploadedfile import SimpleUploadedFile from django.db import models from django.forms import ( CharField, FileField, Form, ModelChoiceField, ModelForm, ) from django.forms.models import ModelFormMetaclass from django.test import TestCase from django.utils import six from ..models import ( BoundaryModel, ChoiceFieldModel, ChoiceModel, ChoiceOptionModel, Defaults, FileModel, Group, OptionalMultiChoiceModel, ) class ChoiceFieldForm(ModelForm): class Meta: model = ChoiceFieldModel fields = '__all__' class OptionalMultiChoiceModelForm(ModelForm): class Meta: model = OptionalMultiChoiceModel fields = '__all__' class ChoiceFieldExclusionForm(ModelForm): multi_choice = CharField(max_length=50) class Meta: exclude = ['multi_choice'] model = ChoiceFieldModel class EmptyCharLabelChoiceForm(ModelForm): class Meta: model = ChoiceModel fields = ['name', 'choice'] class EmptyIntegerLabelChoiceForm(ModelForm): class Meta: model = ChoiceModel fields = ['name', 'choice_integer'] class EmptyCharLabelNoneChoiceForm(ModelForm): class Meta: model = ChoiceModel fields = ['name', 'choice_string_w_none'] class FileForm(Form): file1 = FileField() class TestTicket12510(TestCase): ''' It is not necessary to generate choices for ModelChoiceField (regression test for #12510). ''' def setUp(self): self.groups = [Group.objects.create(name=name) for name in 'abc'] def test_choices_not_fetched_when_not_rendering(self): # only one query is required to pull the model from DB with self.assertNumQueries(1): field = ModelChoiceField(Group.objects.order_by('-name')) self.assertEqual('a', field.clean(self.groups[0].pk).name) class TestTicket14567(TestCase): """ Check that the return values of ModelMultipleChoiceFields are QuerySets """ def test_empty_queryset_return(self): "If a model's ManyToManyField has blank=True and is saved with no data, a queryset is returned." option = ChoiceOptionModel.objects.create(name='default') form = OptionalMultiChoiceModelForm({'multi_choice_optional': '', 'multi_choice': [option.pk]}) self.assertTrue(form.is_valid()) # Check that the empty value is a QuerySet self.assertIsInstance(form.cleaned_data['multi_choice_optional'], models.query.QuerySet) # While we're at it, test whether a QuerySet is returned if there *is* a value. self.assertIsInstance(form.cleaned_data['multi_choice'], models.query.QuerySet) class ModelFormCallableModelDefault(TestCase): def test_no_empty_option(self): "If a model's ForeignKey has blank=False and a default, no empty option is created (Refs #10792)." option = ChoiceOptionModel.objects.create(name='default') choices = list(ChoiceFieldForm().fields['choice'].choices) self.assertEqual(len(choices), 1) self.assertEqual(choices[0], (option.pk, six.text_type(option))) def test_callable_initial_value(self): "The initial value for a callable default returning a queryset is the pk (refs #13769)" ChoiceOptionModel.objects.create(id=1, name='default') ChoiceOptionModel.objects.create(id=2, name='option 2') ChoiceOptionModel.objects.create(id=3, name='option 3') self.assertHTMLEqual(ChoiceFieldForm().as_p(), """

""") def test_initial_instance_value(self): "Initial instances for model fields may also be instances (refs #7287)" ChoiceOptionModel.objects.create(id=1, name='default') obj2 = ChoiceOptionModel.objects.create(id=2, name='option 2') obj3 = ChoiceOptionModel.objects.create(id=3, name='option 3') self.assertHTMLEqual(ChoiceFieldForm(initial={ 'choice': obj2, 'choice_int': obj2, 'multi_choice': [obj2, obj3], 'multi_choice_int': ChoiceOptionModel.objects.exclude(name="default"), }).as_p(), """

""") class FormsModelTestCase(TestCase): def test_unicode_filename(self): # FileModel with unicode filename and data ######################### f = FileForm(data={}, files={'file1': SimpleUploadedFile('我隻氣墊船裝滿晒鱔.txt', 'मेरी मँडराने वाली नाव सर्पमीनों से भरी ह'.encode('utf-8'))}, auto_id=False) self.assertTrue(f.is_valid()) self.assertIn('file1', f.cleaned_data) m = FileModel.objects.create(file=f.cleaned_data['file1']) self.assertEqual(m.file.name, 'tests/\u6211\u96bb\u6c23\u588a\u8239\u88dd\u6eff\u6652\u9c54.txt') m.delete() def test_boundary_conditions(self): # Boundary conditions on a PostitiveIntegerField ######################### class BoundaryForm(ModelForm): class Meta: model = BoundaryModel fields = '__all__' f = BoundaryForm({'positive_integer': 100}) self.assertTrue(f.is_valid()) f = BoundaryForm({'positive_integer': 0}) self.assertTrue(f.is_valid()) f = BoundaryForm({'positive_integer': -100}) self.assertFalse(f.is_valid()) def test_formfield_initial(self): # Formfield initial values ######## # If the model has default values for some fields, they are used as the formfield # initial values. class DefaultsForm(ModelForm): class Meta: model = Defaults fields = '__all__' self.assertEqual(DefaultsForm().fields['name'].initial, 'class default value') self.assertEqual(DefaultsForm().fields['def_date'].initial, datetime.date(1980, 1, 1)) self.assertEqual(DefaultsForm().fields['value'].initial, 42) r1 = DefaultsForm()['callable_default'].as_widget() r2 = DefaultsForm()['callable_default'].as_widget() self.assertNotEqual(r1, r2) # In a ModelForm that is passed an instance, the initial values come from the # instance's values, not the model's defaults. foo_instance = Defaults(name='instance value', def_date=datetime.date(1969, 4, 4), value=12) instance_form = DefaultsForm(instance=foo_instance) self.assertEqual(instance_form.initial['name'], 'instance value') self.assertEqual(instance_form.initial['def_date'], datetime.date(1969, 4, 4)) self.assertEqual(instance_form.initial['value'], 12) from django.forms import CharField class ExcludingForm(ModelForm): name = CharField(max_length=255) class Meta: model = Defaults exclude = ['name', 'callable_default'] f = ExcludingForm({'name': 'Hello', 'value': 99, 'def_date': datetime.date(1999, 3, 2)}) self.assertTrue(f.is_valid()) self.assertEqual(f.cleaned_data['name'], 'Hello') obj = f.save() self.assertEqual(obj.name, 'class default value') self.assertEqual(obj.value, 99) self.assertEqual(obj.def_date, datetime.date(1999, 3, 2)) class RelatedModelFormTests(TestCase): def test_invalid_loading_order(self): """ Test for issue 10405 """ class A(models.Model): ref = models.ForeignKey("B") class Meta: model = A fields = '__all__' self.assertRaises(ValueError, ModelFormMetaclass, str('Form'), (ModelForm,), {'Meta': Meta}) class B(models.Model): pass def test_valid_loading_order(self): """ Test for issue 10405 """ class C(models.Model): ref = models.ForeignKey("D") class D(models.Model): pass class Meta: model = C fields = '__all__' self.assertTrue(issubclass(ModelFormMetaclass(str('Form'), (ModelForm,), {'Meta': Meta}), ModelForm)) class ManyToManyExclusionTestCase(TestCase): def test_m2m_field_exclusion(self): # Issue 12337. save_instance should honor the passed-in exclude keyword. opt1 = ChoiceOptionModel.objects.create(id=1, name='default') opt2 = ChoiceOptionModel.objects.create(id=2, name='option 2') opt3 = ChoiceOptionModel.objects.create(id=3, name='option 3') initial = { 'choice': opt1, 'choice_int': opt1, } data = { 'choice': opt2.pk, 'choice_int': opt2.pk, 'multi_choice': 'string data!', 'multi_choice_int': [opt1.pk], } instance = ChoiceFieldModel.objects.create(**initial) instance.multi_choice = instance.multi_choice_int = [opt2, opt3] form = ChoiceFieldExclusionForm(data=data, instance=instance) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['multi_choice'], data['multi_choice']) form.save() self.assertEqual(form.instance.choice.pk, data['choice']) self.assertEqual(form.instance.choice_int.pk, data['choice_int']) self.assertEqual(list(form.instance.multi_choice.all()), [opt2, opt3]) self.assertEqual([obj.pk for obj in form.instance.multi_choice_int.all()], data['multi_choice_int']) class EmptyLabelTestCase(TestCase): def test_empty_field_char(self): f = EmptyCharLabelChoiceForm() self.assertHTMLEqual(f.as_p(), """

""") def test_empty_field_char_none(self): f = EmptyCharLabelNoneChoiceForm() self.assertHTMLEqual(f.as_p(), """

""") def test_save_empty_label_forms(self): # Test that saving a form with a blank choice results in the expected # value being stored in the database. tests = [ (EmptyCharLabelNoneChoiceForm, 'choice_string_w_none', None), (EmptyIntegerLabelChoiceForm, 'choice_integer', None), (EmptyCharLabelChoiceForm, 'choice', ''), ] for form, key, expected in tests: f = form({'name': 'some-key', key: ''}) self.assertTrue(f.is_valid()) m = f.save() self.assertEqual(expected, getattr(m, key)) self.assertEqual('No Preference', getattr(m, 'get_{}_display'.format(key))()) def test_empty_field_integer(self): f = EmptyIntegerLabelChoiceForm() self.assertHTMLEqual(f.as_p(), """

""") def test_get_display_value_on_none(self): m = ChoiceModel.objects.create(name='test', choice='', choice_integer=None) self.assertIsNone(m.choice_integer) self.assertEqual('No Preference', m.get_choice_integer_display()) def test_html_rendering_of_prepopulated_models(self): none_model = ChoiceModel(name='none-test', choice_integer=None) f = EmptyIntegerLabelChoiceForm(instance=none_model) self.assertHTMLEqual(f.as_p(), """

""") foo_model = ChoiceModel(name='foo-test', choice_integer=1) f = EmptyIntegerLabelChoiceForm(instance=foo_model) self.assertHTMLEqual(f.as_p(), """

""") PKE-Gf9R!#!#.django_tests/forms_tests/tests/test_widgets.py# -*- coding: utf-8 -*- from __future__ import unicode_literals import copy import datetime from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.core.files.uploadedfile import SimpleUploadedFile from django.core.urlresolvers import reverse from django.forms import ( BooleanField, CheckboxInput, CheckboxSelectMultiple, ChoiceField, ClearableFileInput, DateInput, DateTimeField, DateTimeInput, FileInput, Form, HiddenInput, MultipleHiddenInput, MultiWidget, NullBooleanSelect, PasswordInput, RadioSelect, Select, SelectMultiple, SplitDateTimeWidget, Textarea, TextInput, TimeInput, ) from django.forms.widgets import ( ChoiceFieldRenderer, ChoiceInput, RadioFieldRenderer, ) from django.test import TestCase, ignore_warnings, override_settings from django.utils import six from django.utils.deprecation import RemovedInDjango19Warning from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.safestring import SafeData, mark_safe from django.utils.translation import activate, deactivate, override from ..models import Article class FormsWidgetTestCase(TestCase): # 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. def test_textinput(self): w = TextInput() self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', None), '') self.assertHTMLEqual(w.render('email', 'test@example.com'), '') self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '') self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '') self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '') # You can also pass 'attrs' to the constructor: w = TextInput(attrs={'class': 'fun', 'type': 'email'}) self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', 'foo@example.com'), '') # 'attrs' passed to render() get precedence over those passed to the constructor: w = TextInput(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '') # 'attrs' can be safe-strings if needed) w = TextInput(attrs={'onBlur': mark_safe("function('foo')")}) self.assertHTMLEqual(w.render('email', ''), '') def test_passwordinput(self): w = PasswordInput() self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', None), '') self.assertHTMLEqual(w.render('email', 'secret'), '') # The render_value argument lets you specify whether the widget should render # its value. For security reasons, this is off by default. w = PasswordInput(render_value=True) self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', None), '') self.assertHTMLEqual(w.render('email', 'test@example.com'), '') self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '') self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '') # You can also pass 'attrs' to the constructor: w = PasswordInput(attrs={'class': 'fun'}, render_value=True) self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', 'foo@example.com'), '') # 'attrs' passed to render() get precedence over those passed to the constructor: w = PasswordInput(attrs={'class': 'pretty'}, render_value=True) self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '') self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '') def test_hiddeninput(self): w = HiddenInput() self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', None), '') self.assertHTMLEqual(w.render('email', 'test@example.com'), '') self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '') self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '') # You can also pass 'attrs' to the constructor: w = HiddenInput(attrs={'class': 'fun'}) self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', 'foo@example.com'), '') # 'attrs' passed to render() get precedence over those passed to the constructor: w = HiddenInput(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '') self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '') # 'attrs' passed to render() get precedence over those passed to the constructor: w = HiddenInput(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('email', '', attrs={'class': 'special'}), '') # Boolean values are rendered to their string forms ("True" and "False"). w = HiddenInput() self.assertHTMLEqual(w.render('get_spam', False), '') self.assertHTMLEqual(w.render('get_spam', True), '') def test_multiplehiddeninput(self): w = MultipleHiddenInput() self.assertHTMLEqual(w.render('email', []), '') self.assertHTMLEqual(w.render('email', None), '') self.assertHTMLEqual(w.render('email', ['test@example.com']), '') self.assertHTMLEqual(w.render('email', ['some "quoted" & ampersanded value']), '') self.assertHTMLEqual(w.render('email', ['test@example.com', 'foo@example.com']), '\n') self.assertHTMLEqual(w.render('email', ['test@example.com'], attrs={'class': 'fun'}), '') self.assertHTMLEqual(w.render('email', ['test@example.com', 'foo@example.com'], attrs={'class': 'fun'}), '\n') # You can also pass 'attrs' to the constructor: w = MultipleHiddenInput(attrs={'class': 'fun'}) self.assertHTMLEqual(w.render('email', []), '') self.assertHTMLEqual(w.render('email', ['foo@example.com']), '') self.assertHTMLEqual(w.render('email', ['foo@example.com', 'test@example.com']), '\n') # 'attrs' passed to render() get precedence over those passed to the constructor: w = MultipleHiddenInput(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('email', ['foo@example.com'], attrs={'class': 'special'}), '') self.assertHTMLEqual(w.render('email', ['ŠĐĆŽćžšđ'], attrs={'class': 'fun'}), '') # 'attrs' passed to render() get precedence over those passed to the constructor: w = MultipleHiddenInput(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('email', ['foo@example.com'], attrs={'class': 'special'}), '') # Each input gets a separate ID. w = MultipleHiddenInput() self.assertHTMLEqual(w.render('letters', list('abc'), attrs={'id': 'hideme'}), '\n\n') def test_fileinput(self): # 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() self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', None), '') self.assertHTMLEqual(w.render('email', 'test@example.com'), '') self.assertHTMLEqual(w.render('email', 'some "quoted" & ampersanded value'), '') self.assertHTMLEqual(w.render('email', 'test@example.com', attrs={'class': 'fun'}), '') # You can also pass 'attrs' to the constructor: w = FileInput(attrs={'class': 'fun'}) self.assertHTMLEqual(w.render('email', ''), '') self.assertHTMLEqual(w.render('email', 'foo@example.com'), '') self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '') def test_textarea(self): w = Textarea() self.assertHTMLEqual(w.render('msg', ''), '') self.assertHTMLEqual(w.render('msg', None), '') self.assertHTMLEqual(w.render('msg', 'value'), '') self.assertHTMLEqual(w.render('msg', 'some "quoted" & ampersanded value'), '') self.assertHTMLEqual(w.render('msg', mark_safe('pre "quoted" value')), '') self.assertHTMLEqual(w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20}), '') # You can also pass 'attrs' to the constructor: w = Textarea(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('msg', ''), '') self.assertHTMLEqual(w.render('msg', 'example'), '') # 'attrs' passed to render() get precedence over those passed to the constructor: w = Textarea(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('msg', '', attrs={'class': 'special'}), '') self.assertHTMLEqual(w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '') def test_checkboxinput(self): w = CheckboxInput() self.assertHTMLEqual(w.render('is_cool', ''), '') self.assertHTMLEqual(w.render('is_cool', None), '') self.assertHTMLEqual(w.render('is_cool', False), '') self.assertHTMLEqual(w.render('is_cool', True), '') # Using any value that's not in ('', None, False, True) will check the checkbox # and set the 'value' attribute. self.assertHTMLEqual(w.render('is_cool', 'foo'), '') self.assertHTMLEqual(w.render('is_cool', False, attrs={'class': 'pretty'}), '') # regression for #17114 self.assertHTMLEqual(w.render('is_cool', 0), '') self.assertHTMLEqual(w.render('is_cool', 1), '') # You can also pass 'attrs' to the constructor: w = CheckboxInput(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('is_cool', ''), '') # 'attrs' passed to render() get precedence over those passed to the constructor: w = CheckboxInput(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('is_cool', '', attrs={'class': 'special'}), '') # 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')) self.assertHTMLEqual(w.render('greeting', ''), '') self.assertHTMLEqual(w.render('greeting', 'hello'), '') self.assertHTMLEqual(w.render('greeting', 'hello there'), '') self.assertHTMLEqual(w.render('greeting', 'hello & goodbye'), '') # Ticket #17888: calling check_test shouldn't swallow exceptions with self.assertRaises(AttributeError): w.render('greeting', True) # The CheckboxInput widget will return False if the key is not found in the data # dictionary (because HTML form submission doesn't send any result for unchecked # checkboxes). self.assertFalse(w.value_from_datadict({}, {}, 'testing')) value = w.value_from_datadict({'testing': '0'}, {}, 'testing') self.assertIsInstance(value, bool) self.assertTrue(value) def test_select(self): w = Select() self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") # If the value is None, none of the options are selected: self.assertHTMLEqual(w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") # If the value corresponds to a label (but not to an option value), none of the options are selected: self.assertHTMLEqual(w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") # Only one option can be selected, see #8103: self.assertHTMLEqual(w.render('choices', '0', choices=(('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra'))), """""") # The value is compared to its str(): self.assertHTMLEqual(w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]), """""") self.assertHTMLEqual(w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]), """""") self.assertHTMLEqual(w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]), """""") # The 'choices' argument can be any iterable: from itertools import chain def get_choices(): for i in range(5): yield (i, i) self.assertHTMLEqual(w.render('num', 2, choices=get_choices()), """""") 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() self.assertHTMLEqual(f.as_table(), '') self.assertHTMLEqual(f.as_table(), '') f = SomeForm({'somechoice': 2}) self.assertHTMLEqual(f.as_table(), '') # You can also pass 'choices' to the constructor: w = Select(choices=[(1, 1), (2, 2), (3, 3)]) self.assertHTMLEqual(w.render('num', 2), """""") # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: self.assertHTMLEqual(w.render('num', 2, choices=[(4, 4), (5, 5)]), """""") # Choices are escaped correctly self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """""") # Unicode choices are correctly rendered as HTML self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '') # 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()) self.assertHTMLEqual(w.render('num', 2), """""") self.assertHTMLEqual(w.render('num', 3), """""") # Choices can be nested one level in order to create HTML optgroups: w.choices = ( ('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))), ) self.assertHTMLEqual(w.render('nestchoice', None), """""") self.assertHTMLEqual(w.render('nestchoice', 'outer1'), """""") self.assertHTMLEqual(w.render('nestchoice', 'inner1'), """""") def test_nullbooleanselect(self): w = NullBooleanSelect() self.assertTrue(w.render('is_cool', True), """""") self.assertHTMLEqual(w.render('is_cool', False), """""") self.assertHTMLEqual(w.render('is_cool', None), """""") self.assertHTMLEqual(w.render('is_cool', '2'), """""") self.assertHTMLEqual(w.render('is_cool', '3'), """""") def test_selectmultiple(self): w = SelectMultiple() self.assertHTMLEqual(w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") self.assertHTMLEqual(w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") self.assertHTMLEqual(w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") # If the value is None, none of the options are selected: self.assertHTMLEqual(w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") # If the value corresponds to a label (but not to an option value), none of the options are selected: self.assertHTMLEqual(w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") # Multiple options (with the same value) can be selected, see #8103: self.assertHTMLEqual(w.render('choices', ['0'], choices=(('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('0', 'extra'))), """""") # If multiple values are given, but some of them are not valid, the valid ones are selected: self.assertHTMLEqual(w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """""") # The value is compared to its str(): self.assertHTMLEqual(w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]), """""") self.assertHTMLEqual(w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]), """""") self.assertHTMLEqual(w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]), """""") # The 'choices' argument can be any iterable: def get_choices(): for i in range(5): yield (i, i) self.assertHTMLEqual(w.render('nums', [2], choices=get_choices()), """""") # You can also pass 'choices' to the constructor: w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) self.assertHTMLEqual(w.render('nums', [2]), """""") # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: self.assertHTMLEqual(w.render('nums', [2], choices=[(4, 4), (5, 5)]), """""") # Choices are escaped correctly self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """""") # Unicode choices are correctly rendered as HTML self.assertHTMLEqual(w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '') # Choices can be nested one level in order to create HTML optgroups: w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2')))) self.assertHTMLEqual(w.render('nestchoice', None), """""") self.assertHTMLEqual(w.render('nestchoice', ['outer1']), """""") self.assertHTMLEqual(w.render('nestchoice', ['inner1']), """""") self.assertHTMLEqual(w.render('nestchoice', ['outer1', 'inner2']), """""") def test_radioselect(self): w = RadioSelect() self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # If the value is None, none of the options are checked: self.assertHTMLEqual(w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # If the value corresponds to a label (but not to an option value), none of the options are checked: self.assertHTMLEqual(w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # The value is compared to its str(): self.assertHTMLEqual(w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')]), """
""") self.assertHTMLEqual(w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)]), """
""") self.assertHTMLEqual(w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)]), """
""") # The 'choices' argument can be any iterable: def get_choices(): for i in range(5): yield (i, i) self.assertHTMLEqual(w.render('num', 2, choices=get_choices()), """
""") # You can also pass 'choices' to the constructor: w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)]) self.assertHTMLEqual(w.render('num', 2), """
""") # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: self.assertHTMLEqual(w.render('num', 2, choices=[(4, 4), (5, 5)]), """
""") # Choices are escaped correctly w = RadioSelect() self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """
""") # Unicode choices are correctly rendered as HTML w = RadioSelect() self.assertHTMLEqual(six.text_type(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])), '
    \n
  • \n
  • \n
') # Attributes provided at instantiation are passed to the constituent inputs w = RadioSelect(attrs={'id': 'foo'}) self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # Attributes provided at render-time are passed to the constituent inputs w = RadioSelect() self.assertHTMLEqual(w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id': 'bar'}), """
""") def test_radiofieldrenderer(self): # 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'))) inp_set1 = [] inp_set2 = [] inp_set3 = [] inp_set4 = [] for inp in r: inp_set1.append(str(inp)) inp_set2.append('%s
' % inp) inp_set3.append('

%s %s

' % (inp.tag(), inp.choice_label)) inp_set4.append('%s %s %s %s %s' % (inp.name, inp.value, inp.choice_value, inp.choice_label, inp.is_checked())) self.assertHTMLEqual('\n'.join(inp_set1), """ """) self.assertHTMLEqual('\n'.join(inp_set2), """



""") self.assertHTMLEqual('\n'.join(inp_set3), """

John

Paul

George

Ringo

""") self.assertHTMLEqual('\n'.join(inp_set4), """beatle J J John True beatle J P Paul False beatle J G George False beatle J R Ringo False""") # A RadioFieldRenderer object also allows index access to individual RadioChoiceInput w = RadioSelect() r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) self.assertHTMLEqual(str(r[1]), '') self.assertHTMLEqual(str(r[0]), '') self.assertTrue(r[0].is_checked()) self.assertFalse(r[1].is_checked()) self.assertEqual((r[1].name, r[1].value, r[1].choice_value, r[1].choice_label), ('beatle', 'J', 'P', 'Paul')) # These individual widgets can accept extra attributes if manually rendered. self.assertHTMLEqual( r[1].render(attrs={'extra': 'value'}), '' ) with self.assertRaises(IndexError): r[10] # You can create your own custom renderers for RadioSelect to use. class MyRenderer(RadioFieldRenderer): def render(self): return '
\n'.join(six.text_type(choice) for choice in self) w = RadioSelect(renderer=MyRenderer) self.assertHTMLEqual(w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """


""") # Or you can use custom RadioSelect fields that use your custom renderer. class CustomRadioSelect(RadioSelect): renderer = MyRenderer w = CustomRadioSelect() self.assertHTMLEqual(w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """


""") # You can customize rendering with outer_html/inner_html renderer variables (#22950) class MyRenderer(RadioFieldRenderer): outer_html = str('{content}') # str is just to test some Python 2 issue with bytestrings inner_html = '

{choice_value}{sub_widgets}

' w = RadioSelect(renderer=MyRenderer) output = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id': 'bar'}) self.assertIsInstance(output, SafeData) self.assertHTMLEqual(output, """

""") def test_nested_choices(self): # Choices can be nested for radio buttons: w = RadioSelect() w.choices = ( ('unknown', 'Unknown'), ('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))), ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))), ) self.assertHTMLEqual(w.render('nestchoice', 'dvd', attrs={'id': 'media'}), """
  • Audio
  • Video
""") # Choices can be nested for checkboxes: w = CheckboxSelectMultiple() w.choices = ( ('unknown', 'Unknown'), ('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))), ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))), ) self.assertHTMLEqual(w.render('nestchoice', ('vinyl', 'dvd'), attrs={'id': 'media'}), """
  • Audio
  • Video
""") def test_checkboxselectmultiple(self): w = CheckboxSelectMultiple() self.assertHTMLEqual(w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") self.assertHTMLEqual(w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") self.assertHTMLEqual(w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # If the value is None, none of the options are selected: self.assertHTMLEqual(w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # If the value corresponds to a label (but not to an option value), none of the options are selected: self.assertHTMLEqual(w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # If multiple values are given, but some of them are not valid, the valid ones are selected: self.assertHTMLEqual(w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """
""") # The value is compared to its str(): self.assertHTMLEqual(w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')]), """
""") self.assertHTMLEqual(w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)]), """
""") self.assertHTMLEqual(w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)]), """
""") # The 'choices' argument can be any iterable: def get_choices(): for i in range(5): yield (i, i) self.assertHTMLEqual(w.render('nums', [2], choices=get_choices()), """
""") # You can also pass 'choices' to the constructor: w = CheckboxSelectMultiple(choices=[(1, 1), (2, 2), (3, 3)]) self.assertHTMLEqual(w.render('nums', [2]), """
""") # If 'choices' is passed to both the constructor and render(), then they'll both be in the output: self.assertHTMLEqual(w.render('nums', [2], choices=[(4, 4), (5, 5)]), """
""") # Choices are escaped correctly self.assertHTMLEqual(w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you > me')))), """
""") # Unicode choices are correctly rendered as HTML self.assertHTMLEqual(w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]), '
    \n
  • \n
  • \n
  • \n
  • \n
  • \n
') # Each input gets a separate ID self.assertHTMLEqual(CheckboxSelectMultiple().render('letters', list('ac'), choices=zip(list('abc'), list('ABC')), attrs={'id': 'abc'}), """
""") # Each input gets a separate ID when the ID is passed to the constructor self.assertHTMLEqual(CheckboxSelectMultiple(attrs={'id': 'abc'}).render('letters', list('ac'), choices=zip(list('abc'), list('ABC'))), """
""") w = CheckboxSelectMultiple() r = w.get_renderer('abc', 'b', choices=[(c, c.upper()) for c in 'abc']) # You can iterate over the CheckboxFieldRenderer to get individual elements expected = [ '', '', '', ] for output, expected in zip(r, expected): self.assertHTMLEqual(force_text(output), expected) # You can access individual elements self.assertHTMLEqual(force_text(r[1]), '') # Out-of-range errors are propagated with self.assertRaises(IndexError): r[42] def test_subwidget(self): # Each subwidget tag gets a separate ID when the widget has an ID specified self.assertHTMLEqual("\n".join(c.tag() for c in CheckboxSelectMultiple(attrs={'id': 'abc'}).subwidgets('letters', list('ac'), choices=zip(list('abc'), list('ABC')))), """ """) # Each subwidget tag does not get an ID if the widget does not have an ID specified self.assertHTMLEqual("\n".join(c.tag() for c in CheckboxSelectMultiple().subwidgets('letters', list('ac'), choices=zip(list('abc'), list('ABC')))), """ """) # The id_for_label property of the subwidget should return the ID that is used on the subwidget's tag self.assertHTMLEqual("\n".join('' % (c.choice_value, c.id_for_label) for c in CheckboxSelectMultiple(attrs={'id': 'abc'}).subwidgets('letters', [], choices=zip(list('abc'), list('ABC')))), """ """) def test_multi(self): class MyMultiWidget(MultiWidget): def decompress(self, value): if value: return value.split('__') return ['', ''] def format_output(self, rendered_widgets): return '
'.join(rendered_widgets) w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'}))) self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '
') self.assertHTMLEqual(w.render('name', 'john__lennon'), '
') self.assertHTMLEqual(w.render('name', 'john__lennon', attrs={'id': 'foo'}), '
') w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'}) self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '
') # Test needs_multipart_form=True if any widget needs it w = MyMultiWidget(widgets=(TextInput(), FileInput())) self.assertTrue(w.needs_multipart_form) # Test needs_multipart_form=False if no widget needs it w = MyMultiWidget(widgets=(TextInput(), TextInput())) self.assertFalse(w.needs_multipart_form) def test_splitdatetime(self): w = SplitDateTimeWidget() self.assertHTMLEqual(w.render('date', ''), '') self.assertHTMLEqual(w.render('date', None), '') self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '') self.assertHTMLEqual(w.render('date', [datetime.date(2006, 1, 10), datetime.time(7, 30)]), '') # You can also pass 'attrs' to the constructor. In this case, the attrs will be w = SplitDateTimeWidget(attrs={'class': 'pretty'}) self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '') # Use 'date_format' and 'time_format' to change the way a value is displayed. w = SplitDateTimeWidget(date_format='%d/%m/%Y', time_format='%H:%M') self.assertHTMLEqual(w.render('date', datetime.datetime(2006, 1, 10, 7, 30)), '') def test_datetimeinput(self): w = DateTimeInput() self.assertHTMLEqual(w.render('date', None), '') d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) self.assertEqual(str(d), '2007-09-17 12:51:34.482548') # The microseconds are trimmed on display, by default. self.assertHTMLEqual(w.render('date', d), '') self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34)), '') self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '') # Use 'format' to change the way a value is displayed. w = DateTimeInput(format='%d/%m/%Y %H:%M', attrs={'type': 'datetime'}) self.assertHTMLEqual(w.render('date', d), '') def test_dateinput(self): w = DateInput() self.assertHTMLEqual(w.render('date', None), '') d = datetime.date(2007, 9, 17) self.assertEqual(str(d), '2007-09-17') self.assertHTMLEqual(w.render('date', d), '') self.assertHTMLEqual(w.render('date', datetime.date(2007, 9, 17)), '') # We should be able to initialize from a unicode value. self.assertHTMLEqual(w.render('date', '2007-09-17'), '') # Use 'format' to change the way a value is displayed. w = DateInput(format='%d/%m/%Y', attrs={'type': 'date'}) self.assertHTMLEqual(w.render('date', d), '') def test_timeinput(self): w = TimeInput() self.assertHTMLEqual(w.render('time', None), '') t = datetime.time(12, 51, 34, 482548) self.assertEqual(str(t), '12:51:34.482548') # The microseconds are trimmed on display, by default. self.assertHTMLEqual(w.render('time', t), '') self.assertHTMLEqual(w.render('time', datetime.time(12, 51, 34)), '') self.assertHTMLEqual(w.render('time', datetime.time(12, 51)), '') # We should be able to initialize from a unicode value. self.assertHTMLEqual(w.render('time', '13:12:11'), '') # Use 'format' to change the way a value is displayed. w = TimeInput(format='%H:%M', attrs={'type': 'time'}) self.assertHTMLEqual(w.render('time', t), '') def test_splithiddendatetime(self): from django.forms.widgets import SplitHiddenDateTimeWidget w = SplitHiddenDateTimeWidget() self.assertHTMLEqual(w.render('date', ''), '') d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) self.assertHTMLEqual(str(d), '2007-09-17 12:51:34.482548') self.assertHTMLEqual(w.render('date', d), '') self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34)), '') self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '') def test_sub_widget_html_safe(self): widget = TextInput() subwidget = next(widget.subwidgets('username', 'John Doe')) self.assertTrue(hasattr(subwidget, '__html__')) self.assertEqual(force_text(subwidget), subwidget.__html__()) def test_choice_input_html_safe(self): widget = ChoiceInput('choices', 'CHOICE1', {}, ('CHOICE1', 'first choice'), 0) self.assertTrue(hasattr(ChoiceInput, '__html__')) self.assertEqual(force_text(widget), widget.__html__()) def test_choice_field_renderer_html_safe(self): renderer = ChoiceFieldRenderer('choices', 'CHOICE1', {}, [('CHOICE1', 'first_choice')]) renderer.choice_input_class = lambda *args: args self.assertTrue(hasattr(ChoiceFieldRenderer, '__html__')) self.assertEqual(force_text(renderer), renderer.__html__()) class NullBooleanSelectLazyForm(Form): """Form to test for lazy evaluation. Refs #17190""" bool = BooleanField(widget=NullBooleanSelect()) @override_settings(USE_L10N=True) class FormsI18NWidgetsTestCase(TestCase): def setUp(self): super(FormsI18NWidgetsTestCase, self).setUp() activate('de-at') def tearDown(self): deactivate() super(FormsI18NWidgetsTestCase, self).tearDown() def test_datetimeinput(self): w = DateTimeInput() d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) self.assertHTMLEqual(w.render('date', d), '') def test_dateinput(self): w = DateInput() d = datetime.date(2007, 9, 17) self.assertHTMLEqual(w.render('date', d), '') def test_timeinput(self): w = TimeInput() t = datetime.time(12, 51, 34, 482548) self.assertHTMLEqual(w.render('time', t), '') def test_datetime_locale_aware(self): w = DateTimeInput() d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548) with self.settings(USE_L10N=False): self.assertHTMLEqual(w.render('date', d), '') with override('es'): self.assertHTMLEqual(w.render('date', d), '') def test_splithiddendatetime(self): from django.forms.widgets import SplitHiddenDateTimeWidget w = SplitHiddenDateTimeWidget() self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '') def test_nullbooleanselect(self): """ Ensure that the NullBooleanSelect widget's options are lazily localized. Refs #17190 """ f = NullBooleanSelectLazyForm() self.assertHTMLEqual(f.fields['bool'].widget.render('id_bool', True), '') class SelectAndTextWidget(MultiWidget): """ MultiWidget subclass """ def __init__(self, choices=[]): widgets = [ RadioSelect(choices=choices), TextInput ] super(SelectAndTextWidget, self).__init__(widgets) def _set_choices(self, choices): """ When choices are set for this widget, we want to pass those along to the Select widget """ self.widgets[0].choices = choices def _get_choices(self): """ The choices for this widget are the Select widget's choices """ return self.widgets[0].choices choices = property(_get_choices, _set_choices) class WidgetTests(TestCase): def test_12048(self): # See ticket #12048. w1 = SelectAndTextWidget(choices=[1, 2, 3]) w2 = copy.deepcopy(w1) w2.choices = [4, 5, 6] # w2 ought to be independent of w1, since MultiWidget ought # to make a copy of its sub-widgets when it is copied. self.assertEqual(w1.choices, [1, 2, 3]) @ignore_warnings(category=RemovedInDjango19Warning) def test_13390(self): # See ticket #13390 class SplitDateForm(Form): field = DateTimeField(widget=SplitDateTimeWidget, required=False) form = SplitDateForm({'field': ''}) self.assertTrue(form.is_valid()) form = SplitDateForm({'field': ['', '']}) self.assertTrue(form.is_valid()) class SplitDateRequiredForm(Form): field = DateTimeField(widget=SplitDateTimeWidget, required=True) form = SplitDateRequiredForm({'field': ''}) self.assertFalse(form.is_valid()) form = SplitDateRequiredForm({'field': ['', '']}) self.assertFalse(form.is_valid()) @override_settings(ROOT_URLCONF='forms_tests.urls') class LiveWidgetTests(AdminSeleniumWebDriverTestCase): available_apps = ['forms_tests'] + AdminSeleniumWebDriverTestCase.available_apps def test_textarea_trailing_newlines(self): """ Test that a roundtrip on a ModelForm doesn't alter the TextField value """ article = Article.objects.create(content="\nTst\n") self.selenium.get('%s%s' % (self.live_server_url, reverse('article_form', args=[article.pk]))) self.selenium.find_element_by_id('submit').submit() article = Article.objects.get(pk=article.pk) # Should be "\nTst\n" after #19251 is fixed self.assertEqual(article.content, "\r\nTst\r\n") @python_2_unicode_compatible class FakeFieldFile(object): """ Quacks like a FieldFile (has a .url and unicode representation), but doesn't require us to care about storages etc. """ url = 'something' def __str__(self): return self.url class ClearableFileInputTests(TestCase): def test_clear_input_renders(self): """ A ClearableFileInput with is_required False and rendered with an initial value that is a file renders a clear checkbox. """ widget = ClearableFileInput() widget.is_required = False self.assertHTMLEqual(widget.render('myfile', FakeFieldFile()), 'Currently: something
Change: ') def test_html_escaped(self): """ A ClearableFileInput should escape name, filename and URL when rendering HTML. Refs #15182. """ @python_2_unicode_compatible class StrangeFieldFile(object): url = "something?chapter=1§=2©=3&lang=en" def __str__(self): return '''something
.jpg''' widget = ClearableFileInput() field = StrangeFieldFile() output = widget.render('my
file', field) self.assertNotIn(field.url, output) self.assertIn('href="something?chapter=1&sect=2&copy=3&lang=en"', output) self.assertNotIn(six.text_type(field), output) self.assertIn('something<div onclick="alert('oops')">.jpg', output) self.assertIn('my<div>file', output) self.assertNotIn('my
file', output) def test_clear_input_renders_only_if_not_required(self): """ A ClearableFileInput with is_required=False does not render a clear checkbox. """ widget = ClearableFileInput() widget.is_required = True self.assertHTMLEqual(widget.render('myfile', FakeFieldFile()), 'Currently: something
Change: ') def test_clear_input_renders_only_if_initial(self): """ A ClearableFileInput instantiated with no initial value does not render a clear checkbox. """ widget = ClearableFileInput() widget.is_required = False self.assertHTMLEqual(widget.render('myfile', None), '') def test_clear_input_checked_returns_false(self): """ ClearableFileInput.value_from_datadict returns False if the clear checkbox is checked, if not required. """ widget = ClearableFileInput() widget.is_required = False self.assertEqual(widget.value_from_datadict( data={'myfile-clear': True}, files={}, name='myfile'), False) def test_clear_input_checked_returns_false_only_if_not_required(self): """ ClearableFileInput.value_from_datadict never returns False if the field is required. """ widget = ClearableFileInput() widget.is_required = True f = SimpleUploadedFile('something.txt', b'content') self.assertEqual(widget.value_from_datadict( data={'myfile-clear': True}, files={'myfile': f}, name='myfile'), f) def test_render_custom_template(self): widget = ClearableFileInput() widget.template_with_initial = ( '%(initial_text)s: %(initial)s ' '%(clear_template)s
%(input_text)s: %(input)s' ) self.assertHTMLEqual( widget.render('myfile', FakeFieldFile()), 'Currently: something ' ' ' '
Change: ' ) PKE-G/|ee4django_tests/forms_tests/tests/test_input_formats.pyfrom datetime import date, datetime, time from django import forms from django.test import SimpleTestCase, override_settings from django.utils.translation import activate, deactivate @override_settings(TIME_INPUT_FORMATS=["%I:%M:%S %p", "%I:%M %p"], USE_L10N=True) class LocalizedTimeTests(SimpleTestCase): def setUp(self): # nl/formats.py has customized TIME_INPUT_FORMATS: # ('%H:%M:%S', '%H.%M:%S', '%H.%M', '%H:%M') activate('nl') def tearDown(self): deactivate() def test_timeField(self): "TimeFields can parse dates in the default format" f = forms.TimeField() # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') # Parse a time in a valid format, get a parsed result result = f.clean('13:30:05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip text = f.widget._format_value(result) self.assertEqual(text, '13:30:05') # Parse a time in a valid, but non-default format, get a parsed result result = f.clean('13:30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") # ISO formats are accepted, even if not specified in formats.py result = f.clean('13:30:05.000155') self.assertEqual(result, time(13, 30, 5, 155)) def test_localized_timeField(self): "Localized TimeFields act as unlocalized widgets" f = forms.TimeField(localize=True) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') # Parse a time in a valid format, get a parsed result result = f.clean('13:30:05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('13:30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") def test_timeField_with_inputformat(self): "TimeFields with manually specified input formats can accept those formats" f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"]) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('13.30.05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "13:30:05") # Parse a time in a valid format, get a parsed result result = f.clean('13.30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") def test_localized_timeField_with_inputformat(self): "Localized TimeFields with manually specified input formats can accept those formats" f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('13.30.05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "13:30:05") # Parse a time in a valid format, get a parsed result result = f.clean('13.30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") @override_settings(TIME_INPUT_FORMATS=["%I:%M:%S %p", "%I:%M %p"]) class CustomTimeInputFormatsTests(SimpleTestCase): def test_timeField(self): "TimeFields can parse dates in the default format" f = forms.TimeField() # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('1:30:05 PM') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip text = f.widget._format_value(result) self.assertEqual(text, '01:30:05 PM') # Parse a time in a valid, but non-default format, get a parsed result result = f.clean('1:30 PM') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM") def test_localized_timeField(self): "Localized TimeFields act as unlocalized widgets" f = forms.TimeField(localize=True) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('1:30:05 PM') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, '01:30:05 PM') # Parse a time in a valid format, get a parsed result result = f.clean('01:30 PM') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM") def test_timeField_with_inputformat(self): "TimeFields with manually specified input formats can accept those formats" f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"]) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('13.30.05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "01:30:05 PM") # Parse a time in a valid format, get a parsed result result = f.clean('13.30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM") def test_localized_timeField_with_inputformat(self): "Localized TimeFields with manually specified input formats can accept those formats" f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('13.30.05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "01:30:05 PM") # Parse a time in a valid format, get a parsed result result = f.clean('13.30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM") class SimpleTimeFormatTests(SimpleTestCase): def test_timeField(self): "TimeFields can parse dates in the default format" f = forms.TimeField() # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') # Parse a time in a valid format, get a parsed result result = f.clean('13:30:05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "13:30:05") # Parse a time in a valid, but non-default format, get a parsed result result = f.clean('13:30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") def test_localized_timeField(self): "Localized TimeFields in a non-localized environment act as unlocalized widgets" f = forms.TimeField() # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') # Parse a time in a valid format, get a parsed result result = f.clean('13:30:05') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "13:30:05") # Parse a time in a valid format, get a parsed result result = f.clean('13:30') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") def test_timeField_with_inputformat(self): "TimeFields with manually specified input formats can accept those formats" f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"]) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('1:30:05 PM') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "13:30:05") # Parse a time in a valid format, get a parsed result result = f.clean('1:30 PM') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") def test_localized_timeField_with_inputformat(self): "Localized TimeFields with manually specified input formats can accept those formats" f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"], localize=True) # Parse a time in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05') # Parse a time in a valid format, get a parsed result result = f.clean('1:30:05 PM') self.assertEqual(result, time(13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "13:30:05") # Parse a time in a valid format, get a parsed result result = f.clean('1:30 PM') self.assertEqual(result, time(13, 30, 0)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "13:30:00") @override_settings(DATE_INPUT_FORMATS=["%d/%m/%Y", "%d-%m-%Y"], USE_L10N=True) class LocalizedDateTests(SimpleTestCase): def setUp(self): activate('de') def tearDown(self): deactivate() def test_dateField(self): "DateFields can parse dates in the default format" f = forms.DateField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') # ISO formats are accepted, even if not specified in formats.py self.assertEqual(f.clean('2010-12-21'), date(2010, 12, 21)) # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip text = f.widget._format_value(result) self.assertEqual(text, '21.12.2010') # Parse a date in a valid, but non-default format, get a parsed result result = f.clean('21.12.10') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") def test_localized_dateField(self): "Localized DateFields act as unlocalized widgets" f = forms.DateField(localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, '21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.10') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") def test_dateField_with_inputformat(self): "DateFields with manually specified input formats can accept those formats" f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"]) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('12.21.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") # Parse a date in a valid format, get a parsed result result = f.clean('12-21-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") def test_localized_dateField_with_inputformat(self): "Localized DateFields with manually specified input formats can accept those formats" f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('12.21.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") # Parse a date in a valid format, get a parsed result result = f.clean('12-21-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") @override_settings(DATE_INPUT_FORMATS=["%d.%m.%Y", "%d-%m-%Y"]) class CustomDateInputFormatsTests(SimpleTestCase): def test_dateField(self): "DateFields can parse dates in the default format" f = forms.DateField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip text = f.widget._format_value(result) self.assertEqual(text, '21.12.2010') # Parse a date in a valid, but non-default format, get a parsed result result = f.clean('21-12-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") def test_localized_dateField(self): "Localized DateFields act as unlocalized widgets" f = forms.DateField(localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, '21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('21-12-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") def test_dateField_with_inputformat(self): "DateFields with manually specified input formats can accept those formats" f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"]) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') # Parse a date in a valid format, get a parsed result result = f.clean('12.21.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") # Parse a date in a valid format, get a parsed result result = f.clean('12-21-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") def test_localized_dateField_with_inputformat(self): "Localized DateFields with manually specified input formats can accept those formats" f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') # Parse a date in a valid format, get a parsed result result = f.clean('12.21.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") # Parse a date in a valid format, get a parsed result result = f.clean('12-21-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010") class SimpleDateFormatTests(SimpleTestCase): def test_dateField(self): "DateFields can parse dates in the default format" f = forms.DateField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('2010-12-21') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") # Parse a date in a valid, but non-default format, get a parsed result result = f.clean('12/21/2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") def test_localized_dateField(self): "Localized DateFields in a non-localized environment act as unlocalized widgets" f = forms.DateField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('2010-12-21') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") # Parse a date in a valid format, get a parsed result result = f.clean('12/21/2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") def test_dateField_with_inputformat(self): "DateFields with manually specified input formats can accept those formats" f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"]) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") # Parse a date in a valid format, get a parsed result result = f.clean('21-12-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") def test_localized_dateField_with_inputformat(self): "Localized DateFields with manually specified input formats can accept those formats" f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"], localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") # Parse a date in a valid format, get a parsed result result = f.clean('21-12-2010') self.assertEqual(result, date(2010, 12, 21)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21") @override_settings(DATETIME_INPUT_FORMATS=["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"], USE_L10N=True) class LocalizedDateTimeTests(SimpleTestCase): def setUp(self): activate('de') def tearDown(self): deactivate() def test_dateTimeField(self): "DateTimeFields can parse dates in the default format" f = forms.DateTimeField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') # ISO formats are accepted, even if not specified in formats.py self.assertEqual(f.clean('2010-12-21 13:30:05'), datetime(2010, 12, 21, 13, 30, 5)) # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip text = f.widget._format_value(result) self.assertEqual(text, '21.12.2010 13:30:05') # Parse a date in a valid, but non-default format, get a parsed result result = f.clean('21.12.2010 13:30') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010 13:30:00") def test_localized_dateTimeField(self): "Localized DateTimeFields act as unlocalized widgets" f = forms.DateTimeField(localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, '21.12.2010 13:30:05') # Parse a date in a valid format, get a parsed result result = f.clean('21.12.2010 13:30') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010 13:30:00") def test_dateTimeField_with_inputformat(self): "DateTimeFields with manually specified input formats can accept those formats" f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"]) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05 13:30:05') self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('13.30.05 12.21.2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010 13:30:05") # Parse a date in a valid format, get a parsed result result = f.clean('13.30 12-21-2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010 13:30:00") def test_localized_dateTimeField_with_inputformat(self): "Localized DateTimeFields with manually specified input formats can accept those formats" f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"], localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('13.30.05 12.21.2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010 13:30:05") # Parse a date in a valid format, get a parsed result result = f.clean('13.30 12-21-2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "21.12.2010 13:30:00") @override_settings(DATETIME_INPUT_FORMATS=["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"]) class CustomDateTimeInputFormatsTests(SimpleTestCase): def test_dateTimeField(self): "DateTimeFields can parse dates in the default format" f = forms.DateTimeField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') # Parse a date in a valid format, get a parsed result result = f.clean('1:30:05 PM 21/12/2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip text = f.widget._format_value(result) self.assertEqual(text, '01:30:05 PM 21/12/2010') # Parse a date in a valid, but non-default format, get a parsed result result = f.clean('1:30 PM 21-12-2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM 21/12/2010") def test_localized_dateTimeField(self): "Localized DateTimeFields act as unlocalized widgets" f = forms.DateTimeField(localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') # Parse a date in a valid format, get a parsed result result = f.clean('1:30:05 PM 21/12/2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, '01:30:05 PM 21/12/2010') # Parse a date in a valid format, get a parsed result result = f.clean('1:30 PM 21-12-2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM 21/12/2010") def test_dateTimeField_with_inputformat(self): "DateTimeFields with manually specified input formats can accept those formats" f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"]) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') # Parse a date in a valid format, get a parsed result result = f.clean('12.21.2010 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "01:30:05 PM 21/12/2010") # Parse a date in a valid format, get a parsed result result = f.clean('12-21-2010 13:30') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM 21/12/2010") def test_localized_dateTimeField_with_inputformat(self): "Localized DateTimeFields with manually specified input formats can accept those formats" f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"], localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') # Parse a date in a valid format, get a parsed result result = f.clean('12.21.2010 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "01:30:05 PM 21/12/2010") # Parse a date in a valid format, get a parsed result result = f.clean('12-21-2010 13:30') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "01:30:00 PM 21/12/2010") class SimpleDateTimeFormatTests(SimpleTestCase): def test_dateTimeField(self): "DateTimeFields can parse dates in the default format" f = forms.DateTimeField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('2010-12-21 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:05") # Parse a date in a valid, but non-default format, get a parsed result result = f.clean('12/21/2010 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:05") def test_localized_dateTimeField(self): "Localized DateTimeFields in a non-localized environment act as unlocalized widgets" f = forms.DateTimeField() # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') # Parse a date in a valid format, get a parsed result result = f.clean('2010-12-21 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:05") # Parse a date in a valid format, get a parsed result result = f.clean('12/21/2010 13:30:05') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:05") def test_dateTimeField_with_inputformat(self): "DateTimeFields with manually specified input formats can accept those formats" f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"]) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') # Parse a date in a valid format, get a parsed result result = f.clean('1:30:05 PM 21.12.2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:05") # Parse a date in a valid format, get a parsed result result = f.clean('1:30 PM 21-12-2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:00") def test_localized_dateTimeField_with_inputformat(self): "Localized DateTimeFields with manually specified input formats can accept those formats" f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"], localize=True) # Parse a date in an unaccepted format; get an error self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') # Parse a date in a valid format, get a parsed result result = f.clean('1:30:05 PM 21.12.2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30, 5)) # Check that the parsed result does a round trip to the same format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:05") # Parse a date in a valid format, get a parsed result result = f.clean('1:30 PM 21-12-2010') self.assertEqual(result, datetime(2010, 12, 21, 13, 30)) # Check that the parsed result does a round trip to default format text = f.widget._format_value(result) self.assertEqual(text, "2010-12-21 13:30:00") PKE-G!=bb2django_tests/forms_tests/tests/test_regressions.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from forms_tests.models import Cheese from django.forms import ( CharField, ChoiceField, Form, HiddenInput, IntegerField, ModelForm, ModelMultipleChoiceField, MultipleChoiceField, RadioSelect, Select, TextInput, ) from django.test import TestCase, ignore_warnings from django.utils import translation from django.utils.translation import gettext_lazy, ugettext_lazy class FormsRegressionsTestCase(TestCase): def test_class(self): # Tests to prevent against recurrences of earlier bugs. extra_attrs = {'class': 'special'} class TestForm(Form): f1 = CharField(max_length=10, widget=TextInput(attrs=extra_attrs)) f2 = CharField(widget=TextInput(attrs=extra_attrs)) self.assertHTMLEqual(TestForm(auto_id=False).as_p(), '

F1:

\n

F2:

') def test_regression_3600(self): # Tests for form i18n # # There were some problems with form translations in #3600 class SomeForm(Form): username = CharField(max_length=10, label=ugettext_lazy('username')) f = SomeForm() self.assertHTMLEqual(f.as_p(), '

') # Translations are done at rendering time, so multi-lingual apps can define forms) with translation.override('de'): self.assertHTMLEqual(f.as_p(), '

') with translation.override('pl'): self.assertHTMLEqual(f.as_p(), '

') def test_regression_5216(self): # There was some problems with form translations in #5216 class SomeForm(Form): field_1 = CharField(max_length=10, label=ugettext_lazy('field_1')) field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'})) f = SomeForm() self.assertHTMLEqual(f['field_1'].label_tag(), '') self.assertHTMLEqual(f['field_2'].label_tag(), '') # Unicode decoding problems... GENDERS = (('\xc5', 'En tied\xe4'), ('\xf8', 'Mies'), ('\xdf', 'Nainen')) class SomeForm(Form): somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect(), label='\xc5\xf8\xdf') f = SomeForm() self.assertHTMLEqual(f.as_p(), '

    \n
  • \n
  • \n
  • \n

') # Translated error messages used to be buggy. with translation.override('ru'): f = SomeForm({}) self.assertHTMLEqual(f.as_p(), '
  • \u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.
\n

    \n
  • \n
  • \n
  • \n

') # Deep copying translated text shouldn't raise an error) class CopyForm(Form): degree = IntegerField(widget=Select(choices=((1, gettext_lazy('test')),))) f = CopyForm() @ignore_warnings(category=UnicodeWarning) def test_regression_5216_b(self): # Testing choice validation with UTF-8 bytestrings as input (these are the # Russian abbreviations "мес." and "шт.". UNITS = ((b'\xd0\xbc\xd0\xb5\xd1\x81.', b'\xd0\xbc\xd0\xb5\xd1\x81.'), (b'\xd1\x88\xd1\x82.', b'\xd1\x88\xd1\x82.')) f = ChoiceField(choices=UNITS) self.assertEqual(f.clean('\u0448\u0442.'), '\u0448\u0442.') self.assertEqual(f.clean(b'\xd1\x88\xd1\x82.'), '\u0448\u0442.') def test_misc(self): # There once was a problem with Form fields called "data". Let's make sure that # doesn't come back. class DataForm(Form): data = CharField(max_length=10) f = DataForm({'data': 'xyzzy'}) self.assertTrue(f.is_valid()) self.assertEqual(f.cleaned_data, {'data': 'xyzzy'}) # A form with *only* hidden fields that has errors is going to be very unusual. class HiddenForm(Form): data = IntegerField(widget=HiddenInput) f = HiddenForm({}) self.assertHTMLEqual(f.as_p(), '
  • (Hidden field data) This field is required.
\n

') self.assertHTMLEqual(f.as_table(), '
  • (Hidden field data) This field is required.
') def test_xss_error_messages(self): ################################################### # Tests for XSS vulnerabilities in error messages # ################################################### # The forms layer doesn't escape input values directly because error messages # might be presented in non-HTML contexts. Instead, the message is just marked # for escaping by the template engine. So we'll need to construct a little # silly template to trigger the escaping. from django.template import Template, Context t = Template('{{ form.errors }}') class SomeForm(Form): field = ChoiceField(choices=[('one', 'One')]) f = SomeForm({'field': ' """) class Foo: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') m3 = Media(Foo) self.assertEqual(str(m3), """ """) # A widget can exist without a media definition class MyWidget(TextInput): pass w = MyWidget() self.assertEqual(str(w.media), '') def test_media_dsl(self): ############################################################### # DSL Class-based media definitions ############################################################### # A widget can define media if it needs to. # Any absolute path will be preserved; relative paths are combined # with the value of settings.MEDIA_URL class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') w1 = MyWidget1() self.assertEqual(str(w1.media), """ """) # Media objects can be interrogated by media type self.assertEqual(str(w1.media['css']), """ """) self.assertEqual(str(w1.media['js']), """ """) def test_combine_media(self): # Media objects can be combined. Any given media resource will appear only # once. Duplicated media definitions are ignored. class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget2(TextInput): class Media: css = { 'all': ('/path/to/css2', '/path/to/css3') } js = ('/path/to/js1', '/path/to/js4') class MyWidget3(TextInput): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w1 = MyWidget1() w2 = MyWidget2() w3 = MyWidget3() self.assertEqual(str(w1.media + w2.media + w3.media), """ """) # Check that media addition hasn't affected the original objects self.assertEqual(str(w1.media), """ """) # Regression check for #12879: specifying the same CSS or JS file # multiple times in a single Media instance should result in that file # only being included once. class MyWidget4(TextInput): class Media: css = {'all': ('/path/to/css1', '/path/to/css1')} js = ('/path/to/js1', '/path/to/js1') w4 = MyWidget4() self.assertEqual(str(w4.media), """ """) def test_media_property(self): ############################################################### # Property-based media definitions ############################################################### # Widget media can be defined as a property class MyWidget4(TextInput): def _media(self): return Media(css={'all': ('/some/path',)}, js=('/some/js',)) media = property(_media) w4 = MyWidget4() self.assertEqual(str(w4.media), """ """) # Media properties can reference the media of their parents class MyWidget5(MyWidget4): def _media(self): return super(MyWidget5, self).media + Media(css={'all': ('/other/path',)}, js=('/other/js',)) media = property(_media) w5 = MyWidget5() self.assertEqual(str(w5.media), """ """) def test_media_property_parent_references(self): # Media properties can reference the media of their parents, # even if the parent media was defined using a class class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget6(MyWidget1): def _media(self): return super(MyWidget6, self).media + Media(css={'all': ('/other/path',)}, js=('/other/js',)) media = property(_media) w6 = MyWidget6() self.assertEqual(str(w6.media), """ """) def test_media_inheritance(self): ############################################################### # Inheritance of media ############################################################### # If a widget extends another but provides no media definition, it inherits the parent widget's media class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget7(MyWidget1): pass w7 = MyWidget7() self.assertEqual(str(w7.media), """ """) # If a widget extends another but defines media, it extends the parent widget's media by default class MyWidget8(MyWidget1): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w8 = MyWidget8() self.assertEqual(str(w8.media), """ """) def test_media_inheritance_from_property(self): # If a widget extends another but defines media, it extends the parents widget's media, # even if the parent defined media using a property. class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget4(TextInput): def _media(self): return Media(css={'all': ('/some/path',)}, js=('/some/js',)) media = property(_media) class MyWidget9(MyWidget4): class Media: css = { 'all': ('/other/path',) } js = ('/other/js',) w9 = MyWidget9() self.assertEqual(str(w9.media), """ """) # A widget can disable media inheritance by specifying 'extend=False' class MyWidget10(MyWidget1): class Media: extend = False css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w10 = MyWidget10() self.assertEqual(str(w10.media), """ """) def test_media_inheritance_extends(self): # A widget can explicitly enable full media inheritance by specifying 'extend=True' class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget11(MyWidget1): class Media: extend = True css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w11 = MyWidget11() self.assertEqual(str(w11.media), """ """) def test_media_inheritance_single_type(self): # A widget can enable inheritance of one media type by specifying extend as a tuple class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget12(MyWidget1): class Media: extend = ('css',) css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w12 = MyWidget12() self.assertEqual(str(w12.media), """ """) def test_multi_media(self): ############################################################### # Multi-media handling for CSS ############################################################### # A widget can define CSS media for multiple output media types class MultimediaWidget(TextInput): class Media: css = { 'screen, print': ('/file1', '/file2'), 'screen': ('/file3',), 'print': ('/file4',) } js = ('/path/to/js1', '/path/to/js4') multimedia = MultimediaWidget() self.assertEqual(str(multimedia.media), """ """) def test_multi_widget(self): ############################################################### # Multiwidget media handling ############################################################### class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget2(TextInput): class Media: css = { 'all': ('/path/to/css2', '/path/to/css3') } js = ('/path/to/js1', '/path/to/js4') class MyWidget3(TextInput): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') # MultiWidgets have a default media definition that gets all the # media from the component widgets class MyMultiWidget(MultiWidget): def __init__(self, attrs=None): widgets = [MyWidget1, MyWidget2, MyWidget3] super(MyMultiWidget, self).__init__(widgets, attrs) mymulti = MyMultiWidget() self.assertEqual(str(mymulti.media), """ """) def test_form_media(self): ############################################################### # Media processing for forms ############################################################### class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget2(TextInput): class Media: css = { 'all': ('/path/to/css2', '/path/to/css3') } js = ('/path/to/js1', '/path/to/js4') class MyWidget3(TextInput): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') # You can ask a form for the media required by its widgets. class MyForm(Form): field1 = CharField(max_length=20, widget=MyWidget1()) field2 = CharField(max_length=20, widget=MyWidget2()) f1 = MyForm() self.assertEqual(str(f1.media), """ """) # Form media can be combined to produce a single media definition. class AnotherForm(Form): field3 = CharField(max_length=20, widget=MyWidget3()) f2 = AnotherForm() self.assertEqual(str(f1.media + f2.media), """ """) # Forms can also define media, following the same rules as widgets. class FormWithMedia(Form): field1 = CharField(max_length=20, widget=MyWidget1()) field2 = CharField(max_length=20, widget=MyWidget2()) class Media: js = ('/some/form/javascript',) css = { 'all': ('/some/form/css',) } f3 = FormWithMedia() self.assertEqual(str(f3.media), """ """) # Media works in templates self.assertEqual(Template("{{ form.media.js }}{{ form.media.css }}").render(Context({'form': f3})), """ """) def test_html_safe(self): media = Media(css={'all': ['/path/to/css']}, js=['/path/to/js']) self.assertTrue(hasattr(Media, '__html__')) self.assertEqual(force_text(media), media.__html__()) @override_settings( STATIC_URL='http://media.example.com/static/', MEDIA_URL='http://media.example.com/media/', ) class StaticFormsMediaTestCase(TestCase): """Tests for the media handling on widgets and forms""" def test_construction(self): # Check construction of media objects m = Media(css={'all': ('path/to/css1', '/path/to/css2')}, js=('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3')) self.assertEqual(str(m), """ """) class Foo: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') m3 = Media(Foo) self.assertEqual(str(m3), """ """) # A widget can exist without a media definition class MyWidget(TextInput): pass w = MyWidget() self.assertEqual(str(w.media), '') def test_media_dsl(self): ############################################################### # DSL Class-based media definitions ############################################################### # A widget can define media if it needs to. # Any absolute path will be preserved; relative paths are combined # with the value of settings.MEDIA_URL class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') w1 = MyWidget1() self.assertEqual(str(w1.media), """ """) # Media objects can be interrogated by media type self.assertEqual(str(w1.media['css']), """ """) self.assertEqual(str(w1.media['js']), """ """) def test_combine_media(self): # Media objects can be combined. Any given media resource will appear only # once. Duplicated media definitions are ignored. class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget2(TextInput): class Media: css = { 'all': ('/path/to/css2', '/path/to/css3') } js = ('/path/to/js1', '/path/to/js4') class MyWidget3(TextInput): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w1 = MyWidget1() w2 = MyWidget2() w3 = MyWidget3() self.assertEqual(str(w1.media + w2.media + w3.media), """ """) # Check that media addition hasn't affected the original objects self.assertEqual(str(w1.media), """ """) # Regression check for #12879: specifying the same CSS or JS file # multiple times in a single Media instance should result in that file # only being included once. class MyWidget4(TextInput): class Media: css = {'all': ('/path/to/css1', '/path/to/css1')} js = ('/path/to/js1', '/path/to/js1') w4 = MyWidget4() self.assertEqual(str(w4.media), """ """) def test_media_property(self): ############################################################### # Property-based media definitions ############################################################### # Widget media can be defined as a property class MyWidget4(TextInput): def _media(self): return Media(css={'all': ('/some/path',)}, js=('/some/js',)) media = property(_media) w4 = MyWidget4() self.assertEqual(str(w4.media), """ """) # Media properties can reference the media of their parents class MyWidget5(MyWidget4): def _media(self): return super(MyWidget5, self).media + Media(css={'all': ('/other/path',)}, js=('/other/js',)) media = property(_media) w5 = MyWidget5() self.assertEqual(str(w5.media), """ """) def test_media_property_parent_references(self): # Media properties can reference the media of their parents, # even if the parent media was defined using a class class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget6(MyWidget1): def _media(self): return super(MyWidget6, self).media + Media(css={'all': ('/other/path',)}, js=('/other/js',)) media = property(_media) w6 = MyWidget6() self.assertEqual(str(w6.media), """ """) def test_media_inheritance(self): ############################################################### # Inheritance of media ############################################################### # If a widget extends another but provides no media definition, it inherits the parent widget's media class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget7(MyWidget1): pass w7 = MyWidget7() self.assertEqual(str(w7.media), """ """) # If a widget extends another but defines media, it extends the parent widget's media by default class MyWidget8(MyWidget1): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w8 = MyWidget8() self.assertEqual(str(w8.media), """ """) def test_media_inheritance_from_property(self): # If a widget extends another but defines media, it extends the parents widget's media, # even if the parent defined media using a property. class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget4(TextInput): def _media(self): return Media(css={'all': ('/some/path',)}, js=('/some/js',)) media = property(_media) class MyWidget9(MyWidget4): class Media: css = { 'all': ('/other/path',) } js = ('/other/js',) w9 = MyWidget9() self.assertEqual(str(w9.media), """ """) # A widget can disable media inheritance by specifying 'extend=False' class MyWidget10(MyWidget1): class Media: extend = False css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w10 = MyWidget10() self.assertEqual(str(w10.media), """ """) def test_media_inheritance_extends(self): # A widget can explicitly enable full media inheritance by specifying 'extend=True' class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget11(MyWidget1): class Media: extend = True css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w11 = MyWidget11() self.assertEqual(str(w11.media), """ """) def test_media_inheritance_single_type(self): # A widget can enable inheritance of one media type by specifying extend as a tuple class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget12(MyWidget1): class Media: extend = ('css',) css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') w12 = MyWidget12() self.assertEqual(str(w12.media), """ """) def test_multi_media(self): ############################################################### # Multi-media handling for CSS ############################################################### # A widget can define CSS media for multiple output media types class MultimediaWidget(TextInput): class Media: css = { 'screen, print': ('/file1', '/file2'), 'screen': ('/file3',), 'print': ('/file4',) } js = ('/path/to/js1', '/path/to/js4') multimedia = MultimediaWidget() self.assertEqual(str(multimedia.media), """ """) def test_multi_widget(self): ############################################################### # Multiwidget media handling ############################################################### class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget2(TextInput): class Media: css = { 'all': ('/path/to/css2', '/path/to/css3') } js = ('/path/to/js1', '/path/to/js4') class MyWidget3(TextInput): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') # MultiWidgets have a default media definition that gets all the # media from the component widgets class MyMultiWidget(MultiWidget): def __init__(self, attrs=None): widgets = [MyWidget1, MyWidget2, MyWidget3] super(MyMultiWidget, self).__init__(widgets, attrs) mymulti = MyMultiWidget() self.assertEqual(str(mymulti.media), """ """) def test_form_media(self): ############################################################### # Media processing for forms ############################################################### class MyWidget1(TextInput): class Media: css = { 'all': ('path/to/css1', '/path/to/css2') } js = ('/path/to/js1', 'http://media.other.com/path/to/js2', 'https://secure.other.com/path/to/js3') class MyWidget2(TextInput): class Media: css = { 'all': ('/path/to/css2', '/path/to/css3') } js = ('/path/to/js1', '/path/to/js4') class MyWidget3(TextInput): class Media: css = { 'all': ('/path/to/css3', 'path/to/css1') } js = ('/path/to/js1', '/path/to/js4') # You can ask a form for the media required by its widgets. class MyForm(Form): field1 = CharField(max_length=20, widget=MyWidget1()) field2 = CharField(max_length=20, widget=MyWidget2()) f1 = MyForm() self.assertEqual(str(f1.media), """ """) # Form media can be combined to produce a single media definition. class AnotherForm(Form): field3 = CharField(max_length=20, widget=MyWidget3()) f2 = AnotherForm() self.assertEqual(str(f1.media + f2.media), """ """) # Forms can also define media, following the same rules as widgets. class FormWithMedia(Form): field1 = CharField(max_length=20, widget=MyWidget1()) field2 = CharField(max_length=20, widget=MyWidget2()) class Media: js = ('/some/form/javascript',) css = { 'all': ('/some/form/css',) } f3 = FormWithMedia() self.assertEqual(str(f3.media), """ """) # Media works in templates self.assertEqual(Template("{{ form.media.js }}{{ form.media.css }}").render(Context({'form': f3})), """ """) PKE-Gcv..-django_tests/forms_tests/tests/test_fields.py# -*- coding: utf-8 -*- """ ########## # Fields # ########## Each Field class does some sort of validation. Each Field has a clean() method, which either raises django.forms.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. """ from __future__ import unicode_literals import datetime import os import pickle import re import uuid from decimal import Decimal from unittest import skipIf from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import ( BooleanField, CharField, ChoiceField, ComboField, DateField, DateTimeField, DecimalField, DurationField, EmailField, Field, FileField, FilePathField, FloatField, Form, HiddenInput, ImageField, IntegerField, MultipleChoiceField, NullBooleanField, NumberInput, PasswordInput, RadioSelect, RegexField, SplitDateTimeField, Textarea, TextInput, TimeField, TypedChoiceField, TypedMultipleChoiceField, URLField, UUIDField, ValidationError, Widget, forms, ) from django.test import SimpleTestCase, ignore_warnings from django.utils import formats, six, translation from django.utils._os import upath from django.utils.deprecation import RemovedInDjango110Warning from django.utils.duration import duration_string try: from PIL import Image except ImportError: Image = None def fix_os_paths(x): if isinstance(x, six.string_types): return x.replace('\\', '/') elif isinstance(x, tuple): return tuple(fix_os_paths(list(x))) elif isinstance(x, list): return [fix_os_paths(y) for y in x] else: return x class FieldsTests(SimpleTestCase): def assertWidgetRendersTo(self, field, to): class _Form(Form): f = field self.assertHTMLEqual(str(_Form()['f']), to) def test_field_sets_widget_is_required(self): self.assertTrue(Field(required=True).widget.is_required) self.assertFalse(Field(required=False).widget.is_required) def test_cooperative_multiple_inheritance(self): class A(object): def __init__(self): self.class_a_var = True super(A, self).__init__() class ComplexField(Field, A): def __init__(self): super(ComplexField, self).__init__() f = ComplexField() self.assertTrue(f.class_a_var) # CharField ################################################################### def test_charfield_1(self): f = CharField() self.assertEqual('1', f.clean(1)) self.assertEqual('hello', f.clean('hello')) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertEqual('[1, 2, 3]', f.clean([1, 2, 3])) self.assertEqual(f.max_length, None) self.assertEqual(f.min_length, None) def test_charfield_2(self): f = CharField(required=False) self.assertEqual('1', f.clean(1)) self.assertEqual('hello', f.clean('hello')) self.assertEqual('', f.clean(None)) self.assertEqual('', f.clean('')) self.assertEqual('[1, 2, 3]', f.clean([1, 2, 3])) self.assertEqual(f.max_length, None) self.assertEqual(f.min_length, None) def test_charfield_3(self): f = CharField(max_length=10, required=False) self.assertEqual('12345', f.clean('12345')) self.assertEqual('1234567890', f.clean('1234567890')) self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 10 characters (it has 11).'", f.clean, '1234567890a') self.assertEqual(f.max_length, 10) self.assertEqual(f.min_length, None) def test_charfield_4(self): f = CharField(min_length=10, required=False) self.assertEqual('', f.clean('')) self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 10 characters (it has 5).'", f.clean, '12345') self.assertEqual('1234567890', f.clean('1234567890')) self.assertEqual('1234567890a', f.clean('1234567890a')) self.assertEqual(f.max_length, None) self.assertEqual(f.min_length, 10) def test_charfield_5(self): f = CharField(min_length=10, required=True) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 10 characters (it has 5).'", f.clean, '12345') self.assertEqual('1234567890', f.clean('1234567890')) self.assertEqual('1234567890a', f.clean('1234567890a')) self.assertEqual(f.max_length, None) self.assertEqual(f.min_length, 10) def test_charfield_length_not_int(self): """ Ensure that setting min_length or max_length to something that is not a number returns an exception. """ self.assertRaises(ValueError, CharField, min_length='a') self.assertRaises(ValueError, CharField, max_length='a') self.assertRaises(ValueError, CharField, 'a') def test_charfield_widget_attrs(self): """ Ensure that CharField.widget_attrs() always returns a dictionary. Refs #15912 """ # Return an empty dictionary if max_length is None f = CharField() self.assertEqual(f.widget_attrs(TextInput()), {}) self.assertEqual(f.widget_attrs(Textarea()), {}) # Otherwise, return a maxlength attribute equal to max_length f = CharField(max_length=10) self.assertEqual(f.widget_attrs(TextInput()), {'maxlength': '10'}) self.assertEqual(f.widget_attrs(PasswordInput()), {'maxlength': '10'}) self.assertEqual(f.widget_attrs(Textarea()), {'maxlength': '10'}) # IntegerField ################################################################ def test_integerfield_1(self): f = IntegerField() self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual(1, f.clean('1')) self.assertIsInstance(f.clean('1'), int) self.assertEqual(23, f.clean('23')) self.assertRaisesMessage(ValidationError, "'Enter a whole number.'", f.clean, 'a') self.assertEqual(42, f.clean(42)) self.assertRaisesMessage(ValidationError, "'Enter a whole number.'", f.clean, 3.14) self.assertEqual(1, f.clean('1 ')) self.assertEqual(1, f.clean(' 1')) self.assertEqual(1, f.clean(' 1 ')) self.assertRaisesMessage(ValidationError, "'Enter a whole number.'", f.clean, '1a') self.assertEqual(f.max_value, None) self.assertEqual(f.min_value, None) def test_integerfield_2(self): f = IntegerField(required=False) self.assertIsNone(f.clean('')) self.assertEqual('None', repr(f.clean(''))) self.assertIsNone(f.clean(None)) self.assertEqual('None', repr(f.clean(None))) self.assertEqual(1, f.clean('1')) self.assertIsInstance(f.clean('1'), int) self.assertEqual(23, f.clean('23')) self.assertRaisesMessage(ValidationError, "'Enter a whole number.'", f.clean, 'a') self.assertEqual(1, f.clean('1 ')) self.assertEqual(1, f.clean(' 1')) self.assertEqual(1, f.clean(' 1 ')) self.assertRaisesMessage(ValidationError, "'Enter a whole number.'", f.clean, '1a') self.assertEqual(f.max_value, None) self.assertEqual(f.min_value, None) def test_integerfield_3(self): f = IntegerField(max_value=10) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual(1, f.clean(1)) self.assertEqual(10, f.clean(10)) self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 10.'", f.clean, 11) self.assertEqual(10, f.clean('10')) self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 10.'", f.clean, '11') self.assertEqual(f.max_value, 10) self.assertEqual(f.min_value, None) def test_integerfield_4(self): f = IntegerField(min_value=10) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1) self.assertEqual(10, f.clean(10)) self.assertEqual(11, f.clean(11)) self.assertEqual(10, f.clean('10')) self.assertEqual(11, f.clean('11')) self.assertEqual(f.max_value, None) self.assertEqual(f.min_value, 10) def test_integerfield_5(self): f = IntegerField(min_value=10, max_value=20) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'", f.clean, 1) self.assertEqual(10, f.clean(10)) self.assertEqual(11, f.clean(11)) self.assertEqual(10, f.clean('10')) self.assertEqual(11, f.clean('11')) self.assertEqual(20, f.clean(20)) self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 20.'", f.clean, 21) self.assertEqual(f.max_value, 20) self.assertEqual(f.min_value, 10) def test_integerfield_localized(self): """ Make sure localized IntegerField's widget renders to a text input with no number input specific attributes. """ f1 = IntegerField(localize=True) self.assertWidgetRendersTo(f1, '') def test_integerfield_subclass(self): """ Test that class-defined widget is not overwritten by __init__ (#22245). """ class MyIntegerField(IntegerField): widget = Textarea f = MyIntegerField() self.assertEqual(f.widget.__class__, Textarea) f = MyIntegerField(localize=True) self.assertEqual(f.widget.__class__, Textarea) # FloatField ################################################################## def test_floatfield_1(self): f = FloatField() self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual(1.0, f.clean('1')) self.assertIsInstance(f.clean('1'), float) self.assertEqual(23.0, f.clean('23')) self.assertEqual(3.1400000000000001, f.clean('3.14')) self.assertEqual(3.1400000000000001, f.clean(3.14)) self.assertEqual(42.0, f.clean(42)) self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, 'a') self.assertEqual(1.0, f.clean('1.0 ')) self.assertEqual(1.0, f.clean(' 1.0')) self.assertEqual(1.0, f.clean(' 1.0 ')) self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, '1.0a') self.assertEqual(f.max_value, None) self.assertEqual(f.min_value, None) self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, 'Infinity') self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, 'NaN') self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, '-Inf') def test_floatfield_2(self): f = FloatField(required=False) self.assertIsNone(f.clean('')) self.assertIsNone(f.clean(None)) self.assertEqual(1.0, f.clean('1')) self.assertEqual(f.max_value, None) self.assertEqual(f.min_value, None) def test_floatfield_3(self): f = FloatField(max_value=1.5, min_value=0.5) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6') self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4') self.assertEqual(1.5, f.clean('1.5')) self.assertEqual(0.5, f.clean('0.5')) self.assertEqual(f.max_value, 1.5) self.assertEqual(f.min_value, 0.5) def test_floatfield_widget_attrs(self): f = FloatField(widget=NumberInput(attrs={'step': 0.01, 'max': 1.0, 'min': 0.0})) self.assertWidgetRendersTo(f, '') def test_floatfield_localized(self): """ Make sure localized FloatField's widget renders to a text input with no number input specific attributes. """ f = FloatField(localize=True) self.assertWidgetRendersTo(f, '') def test_floatfield_changed(self): f = FloatField() n = 4.35 self.assertFalse(f.has_changed(n, '4.3500')) with translation.override('fr'), self.settings(USE_L10N=True): f = FloatField(localize=True) localized_n = formats.localize_input(n) # -> '4,35' in French self.assertFalse(f.has_changed(n, localized_n)) # DecimalField ################################################################ def test_decimalfield_1(self): f = DecimalField(max_digits=4, decimal_places=2) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual(f.clean('1'), Decimal("1")) self.assertIsInstance(f.clean('1'), Decimal) self.assertEqual(f.clean('23'), Decimal("23")) self.assertEqual(f.clean('3.14'), Decimal("3.14")) self.assertEqual(f.clean(3.14), Decimal("3.14")) self.assertEqual(f.clean(Decimal('3.14')), Decimal("3.14")) self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, 'NaN') self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, 'Inf') self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, '-Inf') self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, 'a') self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, 'łąść') self.assertEqual(f.clean('1.0 '), Decimal("1.0")) self.assertEqual(f.clean(' 1.0'), Decimal("1.0")) self.assertEqual(f.clean(' 1.0 '), Decimal("1.0")) self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, '1.0a') self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 4 digits in total.'", f.clean, '123.45') self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 2 decimal places.'", f.clean, '1.234') self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 2 digits before the decimal point.'", f.clean, '123.4') self.assertEqual(f.clean('-12.34'), Decimal("-12.34")) self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 4 digits in total.'", f.clean, '-123.45') self.assertEqual(f.clean('-.12'), Decimal("-0.12")) self.assertEqual(f.clean('-00.12'), Decimal("-0.12")) self.assertEqual(f.clean('-000.12'), Decimal("-0.12")) self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 2 decimal places.'", f.clean, '-000.123') self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 4 digits in total.'", f.clean, '-000.12345') self.assertRaisesMessage(ValidationError, "'Enter a number.'", f.clean, '--0.12') self.assertEqual(f.max_digits, 4) self.assertEqual(f.decimal_places, 2) self.assertEqual(f.max_value, None) self.assertEqual(f.min_value, None) def test_decimalfield_2(self): f = DecimalField(max_digits=4, decimal_places=2, required=False) self.assertIsNone(f.clean('')) self.assertIsNone(f.clean(None)) self.assertEqual(f.clean('1'), Decimal("1")) self.assertEqual(f.max_digits, 4) self.assertEqual(f.decimal_places, 2) self.assertEqual(f.max_value, None) self.assertEqual(f.min_value, None) def test_decimalfield_3(self): f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5')) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6') self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4') self.assertEqual(f.clean('1.5'), Decimal("1.5")) self.assertEqual(f.clean('0.5'), Decimal("0.5")) self.assertEqual(f.clean('.5'), Decimal("0.5")) self.assertEqual(f.clean('00.50'), Decimal("0.50")) self.assertEqual(f.max_digits, 4) self.assertEqual(f.decimal_places, 2) self.assertEqual(f.max_value, Decimal('1.5')) self.assertEqual(f.min_value, Decimal('0.5')) def test_decimalfield_4(self): f = DecimalField(decimal_places=2) self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 2 decimal places.'", f.clean, '0.00000001') def test_decimalfield_5(self): f = DecimalField(max_digits=3) # Leading whole zeros "collapse" to one digit. self.assertEqual(f.clean('0000000.10'), Decimal("0.1")) # But a leading 0 before the . doesn't count towards max_digits self.assertEqual(f.clean('0000000.100'), Decimal("0.100")) # Only leading whole zeros "collapse" to one digit. self.assertEqual(f.clean('000000.02'), Decimal('0.02')) self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 3 digits in total.'", f.clean, '000000.0002') self.assertEqual(f.clean('.002'), Decimal("0.002")) def test_decimalfield_6(self): f = DecimalField(max_digits=2, decimal_places=2) self.assertEqual(f.clean('.01'), Decimal(".01")) self.assertRaisesMessage(ValidationError, "'Ensure that there are no more than 0 digits before the decimal point.'", f.clean, '1.1') def test_decimalfield_widget_attrs(self): f = DecimalField(max_digits=6, decimal_places=2) self.assertEqual(f.widget_attrs(Widget()), {}) self.assertEqual(f.widget_attrs(NumberInput()), {'step': '0.01'}) f = DecimalField(max_digits=10, decimal_places=0) self.assertEqual(f.widget_attrs(NumberInput()), {'step': '1'}) f = DecimalField(max_digits=19, decimal_places=19) self.assertEqual(f.widget_attrs(NumberInput()), {'step': '1e-19'}) f = DecimalField(max_digits=20) self.assertEqual(f.widget_attrs(NumberInput()), {'step': 'any'}) f = DecimalField(max_digits=6, widget=NumberInput(attrs={'step': '0.01'})) self.assertWidgetRendersTo(f, '') def test_decimalfield_localized(self): """ Make sure localized DecimalField's widget renders to a text input with no number input specific attributes. """ f = DecimalField(localize=True) self.assertWidgetRendersTo(f, '') def test_decimalfield_changed(self): f = DecimalField(max_digits=2, decimal_places=2) d = Decimal("0.1") self.assertFalse(f.has_changed(d, '0.10')) self.assertTrue(f.has_changed(d, '0.101')) with translation.override('fr'), self.settings(USE_L10N=True): f = DecimalField(max_digits=2, decimal_places=2, localize=True) localized_d = formats.localize_input(d) # -> '0,1' in French self.assertFalse(f.has_changed(d, localized_d)) # DateField ################################################################### def test_datefield_1(self): f = DateField() self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.date(2006, 10, 25))) self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.datetime(2006, 10, 25, 14, 30))) self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))) self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))) self.assertEqual(datetime.date(2006, 10, 25), f.clean('2006-10-25')) self.assertEqual(datetime.date(2006, 10, 25), f.clean('10/25/2006')) self.assertEqual(datetime.date(2006, 10, 25), f.clean('10/25/06')) self.assertEqual(datetime.date(2006, 10, 25), f.clean('Oct 25 2006')) self.assertEqual(datetime.date(2006, 10, 25), f.clean('October 25 2006')) self.assertEqual(datetime.date(2006, 10, 25), f.clean('October 25, 2006')) self.assertEqual(datetime.date(2006, 10, 25), f.clean('25 October 2006')) self.assertEqual(datetime.date(2006, 10, 25), f.clean('25 October, 2006')) self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, '2006-4-31') self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, '200a-10-25') self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, '25/10/06') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) def test_datefield_2(self): f = DateField(required=False) self.assertIsNone(f.clean(None)) self.assertEqual('None', repr(f.clean(None))) self.assertIsNone(f.clean('')) self.assertEqual('None', repr(f.clean(''))) def test_datefield_3(self): f = DateField(input_formats=['%Y %m %d']) self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.date(2006, 10, 25))) self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.datetime(2006, 10, 25, 14, 30))) self.assertEqual(datetime.date(2006, 10, 25), f.clean('2006 10 25')) self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, '2006-10-25') self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, '10/25/2006') self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, '10/25/06') def test_datefield_4(self): # Test whitespace stripping behavior (#5714) f = DateField() self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 10/25/2006 ')) self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 10/25/06 ')) self.assertEqual(datetime.date(2006, 10, 25), f.clean(' Oct 25 2006 ')) self.assertEqual(datetime.date(2006, 10, 25), f.clean(' October 25 2006 ')) self.assertEqual(datetime.date(2006, 10, 25), f.clean(' October 25, 2006 ')) self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 25 October 2006 ')) self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, ' ') def test_datefield_5(self): # Test null bytes (#18982) f = DateField() self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, 'a\x00b') @ignore_warnings(category=RemovedInDjango110Warning) # for _has_changed def test_datefield_changed(self): format = '%d/%m/%Y' f = DateField(input_formats=[format]) d = datetime.date(2007, 9, 17) self.assertFalse(f.has_changed(d, '17/09/2007')) # Test for deprecated behavior _has_changed self.assertFalse(f._has_changed(d, '17/09/2007')) def test_datefield_strptime(self): """Test that field.strptime doesn't raise an UnicodeEncodeError (#16123)""" f = DateField() try: f.strptime('31 мая 2011', '%d-%b-%y') except Exception as e: # assertIsInstance or assertRaises cannot be used because UnicodeEncodeError # is a subclass of ValueError self.assertEqual(e.__class__, ValueError) # TimeField ################################################################### def test_timefield_1(self): f = TimeField() self.assertEqual(datetime.time(14, 25), f.clean(datetime.time(14, 25))) self.assertEqual(datetime.time(14, 25, 59), f.clean(datetime.time(14, 25, 59))) self.assertEqual(datetime.time(14, 25), f.clean('14:25')) self.assertEqual(datetime.time(14, 25, 59), f.clean('14:25:59')) self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, 'hello') self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, '1:24 p.m.') def test_timefield_2(self): f = TimeField(input_formats=['%I:%M %p']) self.assertEqual(datetime.time(14, 25), f.clean(datetime.time(14, 25))) self.assertEqual(datetime.time(14, 25, 59), f.clean(datetime.time(14, 25, 59))) self.assertEqual(datetime.time(4, 25), f.clean('4:25 AM')) self.assertEqual(datetime.time(16, 25), f.clean('4:25 PM')) self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, '14:30:45') def test_timefield_3(self): f = TimeField() # Test whitespace stripping behavior (#5714) self.assertEqual(datetime.time(14, 25), f.clean(' 14:25 ')) self.assertEqual(datetime.time(14, 25, 59), f.clean(' 14:25:59 ')) self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, ' ') def test_timefield_changed(self): t1 = datetime.time(12, 51, 34, 482548) t2 = datetime.time(12, 51) f = TimeField(input_formats=['%H:%M', '%H:%M %p']) self.assertTrue(f.has_changed(t1, '12:51')) self.assertFalse(f.has_changed(t2, '12:51')) self.assertFalse(f.has_changed(t2, '12:51 PM')) # DateTimeField ############################################################### def test_datetimefield_1(self): f = DateTimeField() self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(datetime.date(2006, 10, 25))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean(datetime.datetime(2006, 10, 25, 14, 30))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 59), f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 59, 200), f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45, 200), f.clean('2006-10-25 14:30:45.000200')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45, 200), f.clean('2006-10-25 14:30:45.0002')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean('2006-10-25 14:30:45')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('2006-10-25 14:30:00')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('2006-10-25 14:30')) self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean('2006-10-25')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45, 200), f.clean('10/25/2006 14:30:45.000200')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean('10/25/2006 14:30:45')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('10/25/2006 14:30:00')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('10/25/2006 14:30')) self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean('10/25/2006')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45, 200), f.clean('10/25/06 14:30:45.000200')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean('10/25/06 14:30:45')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('10/25/06 14:30:00')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('10/25/06 14:30')) self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean('10/25/06')) self.assertRaisesMessage(ValidationError, "'Enter a valid date/time.'", f.clean, 'hello') self.assertRaisesMessage(ValidationError, "'Enter a valid date/time.'", f.clean, '2006-10-25 4:30 p.m.') def test_datetimefield_2(self): f = DateTimeField(input_formats=['%Y %m %d %I:%M %p']) self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(datetime.date(2006, 10, 25))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean(datetime.datetime(2006, 10, 25, 14, 30))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 59), f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 59, 200), f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('2006 10 25 2:30 PM')) self.assertRaisesMessage(ValidationError, "'Enter a valid date/time.'", f.clean, '2006-10-25 14:30:45') def test_datetimefield_3(self): f = DateTimeField(required=False) self.assertIsNone(f.clean(None)) self.assertEqual('None', repr(f.clean(None))) self.assertIsNone(f.clean('')) self.assertEqual('None', repr(f.clean(''))) def test_datetimefield_4(self): f = DateTimeField() # Test whitespace stripping behavior (#5714) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 2006-10-25 14:30:45 ')) self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 2006-10-25 ')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 10/25/2006 14:30:45 ')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean(' 10/25/2006 14:30 ')) self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 10/25/2006 ')) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45), f.clean(' 10/25/06 14:30:45 ')) self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(' 10/25/06 ')) self.assertRaisesMessage(ValidationError, "'Enter a valid date/time.'", f.clean, ' ') def test_datetimefield_5(self): f = DateTimeField(input_formats=['%Y.%m.%d %H:%M:%S.%f']) self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30, 45, 200), f.clean('2006.10.25 14:30:45.0002')) def test_datetimefield_changed(self): format = '%Y %m %d %I:%M %p' f = DateTimeField(input_formats=[format]) d = datetime.datetime(2006, 9, 17, 14, 30, 0) self.assertFalse(f.has_changed(d, '2006 09 17 2:30 PM')) # DurationField ########################################################### def test_durationfield_1(self): f = DurationField() self.assertEqual(datetime.timedelta(seconds=30), f.clean('30')) self.assertEqual( datetime.timedelta(minutes=15, seconds=30), f.clean('15:30') ) self.assertEqual( datetime.timedelta(hours=1, minutes=15, seconds=30), f.clean('1:15:30') ) self.assertEqual( datetime.timedelta( days=1, hours=1, minutes=15, seconds=30, milliseconds=300), f.clean('1 1:15:30.3') ) def test_durationfield_2(self): class DurationForm(Form): duration = DurationField(initial=datetime.timedelta(hours=1)) f = DurationForm() self.assertHTMLEqual( '', str(f['duration']) ) def test_durationfield_prepare_value(self): field = DurationField() td = datetime.timedelta(minutes=15, seconds=30) self.assertEqual(field.prepare_value(td), duration_string(td)) self.assertEqual(field.prepare_value('arbitrary'), 'arbitrary') self.assertIsNone(field.prepare_value(None)) # RegexField ################################################################## def test_regexfield_1(self): f = RegexField('^[0-9][A-F][0-9]$') self.assertEqual('2A2', f.clean('2A2')) self.assertEqual('3F3', f.clean('3F3')) self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, '3G3') self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, ' 2A2') self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, '2A2 ') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') def test_regexfield_2(self): f = RegexField('^[0-9][A-F][0-9]$', required=False) self.assertEqual('2A2', f.clean('2A2')) self.assertEqual('3F3', f.clean('3F3')) self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, '3G3') self.assertEqual('', f.clean('')) def test_regexfield_3(self): f = RegexField(re.compile('^[0-9][A-F][0-9]$')) self.assertEqual('2A2', f.clean('2A2')) self.assertEqual('3F3', f.clean('3F3')) self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, '3G3') self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, ' 2A2') self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, '2A2 ') @ignore_warnings(category=RemovedInDjango110Warning) # error_message deprecation def test_regexfield_4(self): f = RegexField('^[0-9][0-9][0-9][0-9]$', error_message='Enter a four-digit number.') self.assertEqual('1234', f.clean('1234')) self.assertRaisesMessage(ValidationError, "'Enter a four-digit number.'", f.clean, '123') self.assertRaisesMessage(ValidationError, "'Enter a four-digit number.'", f.clean, 'abcd') def test_regexfield_5(self): f = RegexField('^[0-9]+$', min_length=5, max_length=10) self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 5 characters (it has 3).'", f.clean, '123') six.assertRaisesRegex(self, ValidationError, "'Ensure this value has at least 5 characters \(it has 3\)\.', u?'Enter a valid value\.'", f.clean, 'abc') self.assertEqual('12345', f.clean('12345')) self.assertEqual('1234567890', f.clean('1234567890')) self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 10 characters (it has 11).'", f.clean, '12345678901') self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, '12345a') def test_regexfield_6(self): """ Ensure that it works with unicode characters. Refs #. """ f = RegexField('^\w+$') self.assertEqual('éèøçÎÎ你好', f.clean('éèøçÎÎ你好')) def test_change_regex_after_init(self): f = RegexField('^[a-z]+$') f.regex = '^[0-9]+$' self.assertEqual('1234', f.clean('1234')) self.assertRaisesMessage(ValidationError, "'Enter a valid value.'", f.clean, 'abcd') # EmailField ################################################################## # See also validators tests for validate_email specific tests def test_emailfield_1(self): f = EmailField() self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual('person@example.com', f.clean('person@example.com')) self.assertRaisesMessage(ValidationError, "'Enter a valid email address.'", f.clean, 'foo') self.assertEqual('local@domain.with.idn.xyz\xe4\xf6\xfc\xdfabc.part.com', f.clean('local@domain.with.idn.xyzäöüßabc.part.com')) def test_email_regexp_for_performance(self): f = EmailField() # Check for runaway regex security problem. This will take for-freeking-ever # if the security fix isn't in place. addr = 'viewx3dtextx26qx3d@yahoo.comx26latlngx3d15854521645943074058' self.assertEqual(addr, f.clean(addr)) def test_emailfield_not_required(self): f = EmailField(required=False) self.assertEqual('', f.clean('')) self.assertEqual('', f.clean(None)) self.assertEqual('person@example.com', f.clean('person@example.com')) self.assertEqual('example@example.com', f.clean(' example@example.com \t \t ')) self.assertRaisesMessage(ValidationError, "'Enter a valid email address.'", f.clean, 'foo') def test_emailfield_min_max_length(self): f = EmailField(min_length=10, max_length=15) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 10 characters (it has 9).'", f.clean, 'a@foo.com') self.assertEqual('alf@foo.com', f.clean('alf@foo.com')) self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 15 characters (it has 20).'", f.clean, 'alf123456788@foo.com') # FileField ################################################################## def test_filefield_1(self): f = FileField() self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '', '') self.assertEqual('files/test1.pdf', f.clean('', 'files/test1.pdf')) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None, '') self.assertEqual('files/test2.pdf', f.clean(None, 'files/test2.pdf')) self.assertRaisesMessage(ValidationError, "'No file was submitted. Check the encoding type on the form.'", f.clean, SimpleUploadedFile('', b'')) self.assertRaisesMessage(ValidationError, "'No file was submitted. Check the encoding type on the form.'", f.clean, SimpleUploadedFile('', b''), '') self.assertEqual('files/test3.pdf', f.clean(None, 'files/test3.pdf')) self.assertRaisesMessage(ValidationError, "'No file was submitted. Check the encoding type on the form.'", f.clean, 'some content that is not a file') self.assertRaisesMessage(ValidationError, "'The submitted file is empty.'", f.clean, SimpleUploadedFile('name', None)) self.assertRaisesMessage(ValidationError, "'The submitted file is empty.'", f.clean, SimpleUploadedFile('name', b'')) self.assertEqual(SimpleUploadedFile, type(f.clean(SimpleUploadedFile('name', b'Some File Content')))) self.assertEqual(SimpleUploadedFile, type(f.clean(SimpleUploadedFile('我隻氣墊船裝滿晒鱔.txt', 'मेरी मँडराने वाली नाव सर्पमीनों से भरी ह'.encode('utf-8'))))) self.assertEqual(SimpleUploadedFile, type(f.clean(SimpleUploadedFile('name', b'Some File Content'), 'files/test4.pdf'))) def test_filefield_2(self): f = FileField(max_length=5) self.assertRaisesMessage(ValidationError, "'Ensure this filename has at most 5 characters (it has 18).'", f.clean, SimpleUploadedFile('test_maxlength.txt', b'hello world')) self.assertEqual('files/test1.pdf', f.clean('', 'files/test1.pdf')) self.assertEqual('files/test2.pdf', f.clean(None, 'files/test2.pdf')) self.assertEqual(SimpleUploadedFile, type(f.clean(SimpleUploadedFile('name', b'Some File Content')))) def test_filefield_3(self): f = FileField(allow_empty_file=True) self.assertEqual(SimpleUploadedFile, type(f.clean(SimpleUploadedFile('name', b'')))) def test_filefield_changed(self): ''' Test for the behavior of has_changed for FileField. The value of data will more than likely come from request.FILES. The value of initial data will likely be a filename stored in the database. Since its value is of no use to a FileField it is ignored. ''' f = FileField() # No file was uploaded and no initial data. self.assertFalse(f.has_changed('', None)) # A file was uploaded and no initial data. self.assertTrue(f.has_changed('', {'filename': 'resume.txt', 'content': 'My resume'})) # A file was not uploaded, but there is initial data self.assertFalse(f.has_changed('resume.txt', None)) # A file was uploaded and there is initial data (file identity is not dealt # with here) self.assertTrue(f.has_changed('resume.txt', {'filename': 'resume.txt', 'content': 'My resume'})) # ImageField ################################################################## @skipIf(Image is None, "Pillow is required to test ImageField") def test_imagefield_annotate_with_image_after_clean(self): f = ImageField() img_path = os.path.dirname(upath(__file__)) + '/filepath_test_files/1x1.png' with open(img_path, 'rb') as img_file: img_data = img_file.read() img_file = SimpleUploadedFile('1x1.png', img_data) img_file.content_type = 'text/plain' uploaded_file = f.clean(img_file) self.assertEqual('PNG', uploaded_file.image.format) self.assertEqual('image/png', uploaded_file.content_type) @skipIf(Image is None, "Pillow is required to test ImageField") def test_imagefield_annotate_with_bitmap_image_after_clean(self): """ This also tests the situation when Pillow doesn't detect the MIME type of the image (#24948). """ f = ImageField() img_path = os.path.dirname(upath(__file__)) + '/filepath_test_files/1x1.bmp' with open(img_path, 'rb') as img_file: img_data = img_file.read() img_file = SimpleUploadedFile('1x1.bmp', img_data) img_file.content_type = 'text/plain' uploaded_file = f.clean(img_file) self.assertEqual('BMP', uploaded_file.image.format) self.assertIsNone(uploaded_file.content_type) # URLField ################################################################## def test_urlfield_1(self): f = URLField() self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual('http://localhost', f.clean('http://localhost')) self.assertEqual('http://example.com', f.clean('http://example.com')) self.assertEqual('http://example.com.', f.clean('http://example.com.')) self.assertEqual('http://www.example.com', f.clean('http://www.example.com')) self.assertEqual('http://www.example.com:8000/test', f.clean('http://www.example.com:8000/test')) self.assertEqual('http://valid-with-hyphens.com', f.clean('valid-with-hyphens.com')) self.assertEqual('http://subdomain.domain.com', f.clean('subdomain.domain.com')) self.assertEqual('http://200.8.9.10', f.clean('http://200.8.9.10')) self.assertEqual('http://200.8.9.10:8000/test', f.clean('http://200.8.9.10:8000/test')) self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'foo') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://example') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://example.') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'com.') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, '.') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://.com') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://invalid-.com') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://-invalid.com') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://inv-.alid-.com') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://inv-.-alid.com') self.assertEqual('http://valid-----hyphens.com', f.clean('http://valid-----hyphens.com')) self.assertEqual('http://some.idn.xyz\xe4\xf6\xfc\xdfabc.domain.com:123/blah', f.clean('http://some.idn.xyzäöüßabc.domain.com:123/blah')) self.assertEqual('http://www.example.com/s/http://code.djangoproject.com/ticket/13804', f.clean('www.example.com/s/http://code.djangoproject.com/ticket/13804')) self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, '[a') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://[a') def test_url_regex_ticket11198(self): f = URLField() # hangs "forever" if catastrophic backtracking in ticket:#11198 not fixed self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://%s' % ("X" * 200,)) # a second test, to make sure the problem is really addressed, even on # domains that don't fail the domain label length check in the regex self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://%s' % ("X" * 60,)) def test_urlfield_2(self): f = URLField(required=False) self.assertEqual('', f.clean('')) self.assertEqual('', f.clean(None)) self.assertEqual('http://example.com', f.clean('http://example.com')) self.assertEqual('http://www.example.com', f.clean('http://www.example.com')) self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'foo') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://example') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://example.') self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 'http://.com') def test_urlfield_5(self): f = URLField(min_length=15, max_length=20) self.assertWidgetRendersTo(f, '') self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 15 characters (it has 12).'", f.clean, 'http://f.com') self.assertEqual('http://example.com', f.clean('http://example.com')) self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 20 characters (it has 37).'", f.clean, 'http://abcdefghijklmnopqrstuvwxyz.com') def test_urlfield_6(self): f = URLField(required=False) self.assertEqual('http://example.com', f.clean('example.com')) self.assertEqual('', f.clean('')) self.assertEqual('https://example.com', f.clean('https://example.com')) def test_urlfield_7(self): f = URLField() self.assertEqual('http://example.com', f.clean('http://example.com')) self.assertEqual('http://example.com/test', f.clean('http://example.com/test')) self.assertEqual('http://example.com?some_param=some_value', f.clean('http://example.com?some_param=some_value')) def test_urlfield_9(self): f = URLField() urls = ( 'http://עברית.idn.icann.org/', 'http://sãopaulo.com/', 'http://sãopaulo.com.br/', 'http://пример.испытание/', 'http://مثال.إختبار/', 'http://例子.测试/', 'http://例子.測試/', 'http://उदाहरण.परीक्षा/', 'http://例え.テスト/', 'http://مثال.آزمایشی/', 'http://실례.테스트/', 'http://العربية.idn.icann.org/', ) for url in urls: # Valid IDN self.assertEqual(url, f.clean(url)) def test_urlfield_10(self): """Test URLField correctly validates IPv6 (#18779).""" f = URLField() urls = ( 'http://[12:34::3a53]/', 'http://[a34:9238::]:8080/', ) for url in urls: self.assertEqual(url, f.clean(url)) def test_urlfield_not_string(self): f = URLField(required=False) self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 23) # BooleanField ################################################################ def test_booleanfield_1(self): f = BooleanField() self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual(True, f.clean(True)) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, False) self.assertEqual(True, f.clean(1)) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, 0) self.assertEqual(True, f.clean('Django rocks')) self.assertEqual(True, f.clean('True')) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, 'False') def test_booleanfield_2(self): f = BooleanField(required=False) self.assertEqual(False, f.clean('')) self.assertEqual(False, f.clean(None)) self.assertEqual(True, f.clean(True)) self.assertEqual(False, f.clean(False)) self.assertEqual(True, f.clean(1)) self.assertEqual(False, f.clean(0)) self.assertEqual(True, f.clean('1')) self.assertEqual(False, f.clean('0')) self.assertEqual(True, f.clean('Django rocks')) self.assertEqual(False, f.clean('False')) self.assertEqual(False, f.clean('false')) self.assertEqual(False, f.clean('FaLsE')) def test_boolean_picklable(self): self.assertIsInstance(pickle.loads(pickle.dumps(BooleanField())), BooleanField) def test_booleanfield_changed(self): f = BooleanField() self.assertFalse(f.has_changed(None, None)) self.assertFalse(f.has_changed(None, '')) self.assertFalse(f.has_changed('', None)) self.assertFalse(f.has_changed('', '')) self.assertTrue(f.has_changed(False, 'on')) self.assertFalse(f.has_changed(True, 'on')) self.assertTrue(f.has_changed(True, '')) # Initial value may have mutated to a string due to show_hidden_initial (#19537) self.assertTrue(f.has_changed('False', 'on')) # ChoiceField ################################################################# def test_choicefield_1(self): f = ChoiceField(choices=[('1', 'One'), ('2', 'Two')]) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual('1', f.clean(1)) self.assertEqual('1', f.clean('1')) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 3 is not one of the available choices.'", f.clean, '3') def test_choicefield_2(self): f = ChoiceField(choices=[('1', 'One'), ('2', 'Two')], required=False) self.assertEqual('', f.clean('')) self.assertEqual('', f.clean(None)) self.assertEqual('1', f.clean(1)) self.assertEqual('1', f.clean('1')) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 3 is not one of the available choices.'", f.clean, '3') def test_choicefield_3(self): f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')]) self.assertEqual('J', f.clean('J')) self.assertRaisesMessage(ValidationError, "'Select a valid choice. John is not one of the available choices.'", f.clean, 'John') def test_choicefield_4(self): f = ChoiceField(choices=[('Numbers', (('1', 'One'), ('2', 'Two'))), ('Letters', (('3', 'A'), ('4', 'B'))), ('5', 'Other')]) self.assertEqual('1', f.clean(1)) self.assertEqual('1', f.clean('1')) self.assertEqual('3', f.clean(3)) self.assertEqual('3', f.clean('3')) self.assertEqual('5', f.clean(5)) self.assertEqual('5', f.clean('5')) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 6 is not one of the available choices.'", f.clean, '6') def test_choicefield_callable(self): choices = lambda: [('J', 'John'), ('P', 'Paul')] f = ChoiceField(choices=choices) self.assertEqual('J', f.clean('J')) def test_choicefield_callable_may_evaluate_to_different_values(self): choices = [] def choices_as_callable(): return choices class ChoiceFieldForm(Form): choicefield = ChoiceField(choices=choices_as_callable) choices = [('J', 'John')] form = ChoiceFieldForm() self.assertEqual([('J', 'John')], list(form.fields['choicefield'].choices)) choices = [('P', 'Paul')] form = ChoiceFieldForm() self.assertEqual([('P', 'Paul')], list(form.fields['choicefield'].choices)) # TypedChoiceField ############################################################ # TypedChoiceField is just like ChoiceField, except that coerced types will # be returned: def test_typedchoicefield_1(self): f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) self.assertEqual(1, f.clean('1')) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 2 is not one of the available choices.'", f.clean, '2') def test_typedchoicefield_2(self): # Different coercion, same validation. f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=float) self.assertEqual(1.0, f.clean('1')) def test_typedchoicefield_3(self): # This can also cause weirdness: be careful (bool(-1) == True, remember) f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=bool) self.assertEqual(True, f.clean('-1')) def test_typedchoicefield_4(self): # Even more weirdness: if you have a valid choice but your coercion function # can't coerce, you'll still get a validation error. Don't do this! f = TypedChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int) self.assertRaisesMessage(ValidationError, "'Select a valid choice. B is not one of the available choices.'", f.clean, 'B') # Required fields require values self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') def test_typedchoicefield_5(self): # Non-required fields aren't required f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False) self.assertEqual('', f.clean('')) # If you want cleaning an empty value to return a different type, tell the field def test_typedchoicefield_6(self): f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None) self.assertIsNone(f.clean('')) def test_typedchoicefield_has_changed(self): # has_changed should not trigger required validation f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True) self.assertFalse(f.has_changed(None, '')) self.assertFalse(f.has_changed(1, '1')) def test_typedchoicefield_special_coerce(self): """ Test a coerce function which results in a value not present in choices. Refs #21397. """ def coerce_func(val): return Decimal('1.%s' % val) f = TypedChoiceField(choices=[(1, "1"), (2, "2")], coerce=coerce_func, required=True) self.assertEqual(Decimal('1.2'), f.clean('2')) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'Select a valid choice. 3 is not one of the available choices.'", f.clean, '3') # NullBooleanField ############################################################ def test_nullbooleanfield_1(self): f = NullBooleanField() self.assertIsNone(f.clean('')) self.assertEqual(True, f.clean(True)) self.assertEqual(False, f.clean(False)) self.assertIsNone(f.clean(None)) self.assertEqual(False, f.clean('0')) self.assertEqual(True, f.clean('1')) self.assertIsNone(f.clean('2')) self.assertIsNone(f.clean('3')) self.assertIsNone(f.clean('hello')) self.assertEqual(True, f.clean('true')) self.assertEqual(False, f.clean('false')) def test_nullbooleanfield_2(self): # Make sure that the internal value is preserved if using HiddenInput (#7753) class HiddenNullBooleanForm(Form): hidden_nullbool1 = NullBooleanField(widget=HiddenInput, initial=True) hidden_nullbool2 = NullBooleanField(widget=HiddenInput, initial=False) f = HiddenNullBooleanForm() self.assertHTMLEqual('', str(f)) def test_nullbooleanfield_3(self): class HiddenNullBooleanForm(Form): hidden_nullbool1 = NullBooleanField(widget=HiddenInput, initial=True) hidden_nullbool2 = NullBooleanField(widget=HiddenInput, initial=False) f = HiddenNullBooleanForm({'hidden_nullbool1': 'True', 'hidden_nullbool2': 'False'}) self.assertIsNone(f.full_clean()) self.assertEqual(True, f.cleaned_data['hidden_nullbool1']) self.assertEqual(False, f.cleaned_data['hidden_nullbool2']) def test_nullbooleanfield_4(self): # Make sure we're compatible with MySQL, which uses 0 and 1 for its boolean # values. (#9609) NULLBOOL_CHOICES = (('1', 'Yes'), ('0', 'No'), ('', 'Unknown')) class MySQLNullBooleanForm(Form): nullbool0 = NullBooleanField(widget=RadioSelect(choices=NULLBOOL_CHOICES)) nullbool1 = NullBooleanField(widget=RadioSelect(choices=NULLBOOL_CHOICES)) nullbool2 = NullBooleanField(widget=RadioSelect(choices=NULLBOOL_CHOICES)) f = MySQLNullBooleanForm({'nullbool0': '1', 'nullbool1': '0', 'nullbool2': ''}) self.assertIsNone(f.full_clean()) self.assertEqual(True, f.cleaned_data['nullbool0']) self.assertEqual(False, f.cleaned_data['nullbool1']) self.assertIsNone(f.cleaned_data['nullbool2']) def test_nullbooleanfield_changed(self): f = NullBooleanField() self.assertTrue(f.has_changed(False, None)) self.assertTrue(f.has_changed(None, False)) self.assertFalse(f.has_changed(None, None)) self.assertFalse(f.has_changed(False, False)) self.assertTrue(f.has_changed(True, False)) self.assertTrue(f.has_changed(True, None)) self.assertTrue(f.has_changed(True, False)) # MultipleChoiceField ######################################################### def test_multiplechoicefield_1(self): f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')]) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual(['1'], f.clean([1])) self.assertEqual(['1'], f.clean(['1'])) self.assertEqual(['1', '2'], f.clean(['1', '2'])) self.assertEqual(['1', '2'], f.clean([1, '2'])) self.assertEqual(['1', '2'], f.clean((1, '2'))) self.assertRaisesMessage(ValidationError, "'Enter a list of values.'", f.clean, 'hello') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, []) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, ()) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 3 is not one of the available choices.'", f.clean, ['3']) def test_multiplechoicefield_2(self): f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')], required=False) self.assertEqual([], f.clean('')) self.assertEqual([], f.clean(None)) self.assertEqual(['1'], f.clean([1])) self.assertEqual(['1'], f.clean(['1'])) self.assertEqual(['1', '2'], f.clean(['1', '2'])) self.assertEqual(['1', '2'], f.clean([1, '2'])) self.assertEqual(['1', '2'], f.clean((1, '2'))) self.assertRaisesMessage(ValidationError, "'Enter a list of values.'", f.clean, 'hello') self.assertEqual([], f.clean([])) self.assertEqual([], f.clean(())) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 3 is not one of the available choices.'", f.clean, ['3']) def test_multiplechoicefield_3(self): f = MultipleChoiceField(choices=[('Numbers', (('1', 'One'), ('2', 'Two'))), ('Letters', (('3', 'A'), ('4', 'B'))), ('5', 'Other')]) self.assertEqual(['1'], f.clean([1])) self.assertEqual(['1'], f.clean(['1'])) self.assertEqual(['1', '5'], f.clean([1, 5])) self.assertEqual(['1', '5'], f.clean([1, '5'])) self.assertEqual(['1', '5'], f.clean(['1', 5])) self.assertEqual(['1', '5'], f.clean(['1', '5'])) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 6 is not one of the available choices.'", f.clean, ['6']) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 6 is not one of the available choices.'", f.clean, ['1', '6']) def test_multiplechoicefield_changed(self): f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two'), ('3', 'Three')]) self.assertFalse(f.has_changed(None, None)) self.assertFalse(f.has_changed([], None)) self.assertTrue(f.has_changed(None, ['1'])) self.assertFalse(f.has_changed([1, 2], ['1', '2'])) self.assertFalse(f.has_changed([2, 1], ['1', '2'])) self.assertTrue(f.has_changed([1, 2], ['1'])) self.assertTrue(f.has_changed([1, 2], ['1', '3'])) # TypedMultipleChoiceField ############################################################ # TypedMultipleChoiceField is just like MultipleChoiceField, except that coerced types # will be returned: def test_typedmultiplechoicefield_1(self): f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) self.assertEqual([1], f.clean(['1'])) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 2 is not one of the available choices.'", f.clean, ['2']) def test_typedmultiplechoicefield_2(self): # Different coercion, same validation. f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=float) self.assertEqual([1.0], f.clean(['1'])) def test_typedmultiplechoicefield_3(self): # This can also cause weirdness: be careful (bool(-1) == True, remember) f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=bool) self.assertEqual([True], f.clean(['-1'])) def test_typedmultiplechoicefield_4(self): f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) self.assertEqual([1, -1], f.clean(['1', '-1'])) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 2 is not one of the available choices.'", f.clean, ['1', '2']) def test_typedmultiplechoicefield_5(self): # Even more weirdness: if you have a valid choice but your coercion function # can't coerce, you'll still get a validation error. Don't do this! f = TypedMultipleChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int) self.assertRaisesMessage(ValidationError, "'Select a valid choice. B is not one of the available choices.'", f.clean, ['B']) # Required fields require values self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, []) def test_typedmultiplechoicefield_6(self): # Non-required fields aren't required f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False) self.assertEqual([], f.clean([])) def test_typedmultiplechoicefield_7(self): # If you want cleaning an empty value to return a different type, tell the field f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None) self.assertIsNone(f.clean([])) def test_typedmultiplechoicefield_has_changed(self): # has_changed should not trigger required validation f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True) self.assertFalse(f.has_changed(None, '')) def test_typedmultiplechoicefield_special_coerce(self): """ Test a coerce function which results in a value not present in choices. Refs #21397. """ def coerce_func(val): return Decimal('1.%s' % val) f = TypedMultipleChoiceField( choices=[(1, "1"), (2, "2")], coerce=coerce_func, required=True) self.assertEqual([Decimal('1.2')], f.clean(['2'])) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, []) self.assertRaisesMessage(ValidationError, "'Select a valid choice. 3 is not one of the available choices.'", f.clean, ['3']) # ComboField ################################################################## def test_combofield_1(self): f = ComboField(fields=[CharField(max_length=20), EmailField()]) self.assertEqual('test@example.com', f.clean('test@example.com')) self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 20 characters (it has 28).'", f.clean, 'longemailaddress@example.com') self.assertRaisesMessage(ValidationError, "'Enter a valid email address.'", f.clean, 'not an email') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) def test_combofield_2(self): f = ComboField(fields=[CharField(max_length=20), EmailField()], required=False) self.assertEqual('test@example.com', f.clean('test@example.com')) self.assertRaisesMessage(ValidationError, "'Ensure this value has at most 20 characters (it has 28).'", f.clean, 'longemailaddress@example.com') self.assertRaisesMessage(ValidationError, "'Enter a valid email address.'", f.clean, 'not an email') self.assertEqual('', f.clean('')) self.assertEqual('', f.clean(None)) # FilePathField ############################################################### def test_filepathfield_1(self): path = os.path.abspath(upath(forms.__file__)) path = os.path.dirname(path) + '/' self.assertTrue(fix_os_paths(path).endswith('/django/forms/')) def test_filepathfield_2(self): path = upath(forms.__file__) path = os.path.dirname(os.path.abspath(path)) + '/' f = FilePathField(path=path) f.choices = [p for p in f.choices if p[0].endswith('.py')] f.choices.sort() expected = [ ('/django/forms/__init__.py', '__init__.py'), ('/django/forms/fields.py', 'fields.py'), ('/django/forms/forms.py', 'forms.py'), ('/django/forms/formsets.py', 'formsets.py'), ('/django/forms/models.py', 'models.py'), ('/django/forms/util.py', 'util.py'), ('/django/forms/utils.py', 'utils.py'), ('/django/forms/widgets.py', 'widgets.py') ] for exp, got in zip(expected, fix_os_paths(f.choices)): self.assertEqual(exp[1], got[1]) self.assertTrue(got[0].endswith(exp[0])) self.assertRaisesMessage(ValidationError, "'Select a valid choice. fields.py is not one of the available choices.'", f.clean, 'fields.py') assert fix_os_paths(f.clean(path + 'fields.py')).endswith('/django/forms/fields.py') def test_filepathfield_3(self): path = upath(forms.__file__) path = os.path.dirname(os.path.abspath(path)) + '/' f = FilePathField(path=path, match='^.*?\.py$') f.choices.sort() expected = [ ('/django/forms/__init__.py', '__init__.py'), ('/django/forms/fields.py', 'fields.py'), ('/django/forms/forms.py', 'forms.py'), ('/django/forms/formsets.py', 'formsets.py'), ('/django/forms/models.py', 'models.py'), ('/django/forms/util.py', 'util.py'), ('/django/forms/utils.py', 'utils.py'), ('/django/forms/widgets.py', 'widgets.py') ] for exp, got in zip(expected, fix_os_paths(f.choices)): self.assertEqual(exp[1], got[1]) self.assertTrue(got[0].endswith(exp[0])) def test_filepathfield_4(self): path = os.path.abspath(upath(forms.__file__)) path = os.path.dirname(path) + '/' f = FilePathField(path=path, recursive=True, match='^.*?\.py$') f.choices.sort() expected = [ ('/django/forms/__init__.py', '__init__.py'), ('/django/forms/extras/__init__.py', 'extras/__init__.py'), ('/django/forms/extras/widgets.py', 'extras/widgets.py'), ('/django/forms/fields.py', 'fields.py'), ('/django/forms/forms.py', 'forms.py'), ('/django/forms/formsets.py', 'formsets.py'), ('/django/forms/models.py', 'models.py'), ('/django/forms/util.py', 'util.py'), ('/django/forms/utils.py', 'utils.py'), ('/django/forms/widgets.py', 'widgets.py') ] for exp, got in zip(expected, fix_os_paths(f.choices)): self.assertEqual(exp[1], got[1]) self.assertTrue(got[0].endswith(exp[0])) def test_filepathfield_folders(self): path = os.path.dirname(upath(__file__)) + '/filepath_test_files/' f = FilePathField(path=path, allow_folders=True, allow_files=False) f.choices.sort() expected = [ ('/tests/forms_tests/tests/filepath_test_files/directory', 'directory'), ] for exp, got in zip(expected, fix_os_paths(f.choices)): self.assertEqual(exp[1], got[1]) self.assertTrue(got[0].endswith(exp[0])) f = FilePathField(path=path, allow_folders=True, allow_files=True) f.choices.sort() expected = [ ('/tests/forms_tests/tests/filepath_test_files/.dot-file', '.dot-file'), ('/tests/forms_tests/tests/filepath_test_files/1x1.bmp', '1x1.bmp'), ('/tests/forms_tests/tests/filepath_test_files/1x1.png', '1x1.png'), ('/tests/forms_tests/tests/filepath_test_files/directory', 'directory'), ('/tests/forms_tests/tests/filepath_test_files/fake-image.jpg', 'fake-image.jpg'), ('/tests/forms_tests/tests/filepath_test_files/real-text-file.txt', 'real-text-file.txt'), ] actual = fix_os_paths(f.choices) self.assertEqual(len(expected), len(actual)) for exp, got in zip(expected, actual): self.assertEqual(exp[1], got[1]) self.assertTrue(got[0].endswith(exp[0])) # SplitDateTimeField ########################################################## def test_splitdatetimefield_1(self): from django.forms.widgets import SplitDateTimeWidget f = SplitDateTimeField() self.assertIsInstance(f.widget, SplitDateTimeWidget) self.assertEqual(datetime.datetime(2006, 1, 10, 7, 30), f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)])) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'Enter a list of values.'", f.clean, 'hello') six.assertRaisesRegex(self, ValidationError, "'Enter a valid date\.', u?'Enter a valid time\.'", f.clean, ['hello', 'there']) self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, ['2006-01-10', 'there']) self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, ['hello', '07:30']) def test_splitdatetimefield_2(self): f = SplitDateTimeField(required=False) self.assertEqual(datetime.datetime(2006, 1, 10, 7, 30), f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)])) self.assertEqual(datetime.datetime(2006, 1, 10, 7, 30), f.clean(['2006-01-10', '07:30'])) self.assertIsNone(f.clean(None)) self.assertIsNone(f.clean('')) self.assertIsNone(f.clean([''])) self.assertIsNone(f.clean(['', ''])) self.assertRaisesMessage(ValidationError, "'Enter a list of values.'", f.clean, 'hello') six.assertRaisesRegex(self, ValidationError, "'Enter a valid date\.', u?'Enter a valid time\.'", f.clean, ['hello', 'there']) self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, ['2006-01-10', 'there']) self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, ['hello', '07:30']) self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, ['2006-01-10', '']) self.assertRaisesMessage(ValidationError, "'Enter a valid time.'", f.clean, ['2006-01-10']) self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, ['', '07:30']) def test_splitdatetimefield_changed(self): f = SplitDateTimeField(input_date_formats=['%d/%m/%Y']) self.assertFalse(f.has_changed(['11/01/2012', '09:18:15'], ['11/01/2012', '09:18:15'])) self.assertTrue(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['2008-05-06', '12:40:00'])) self.assertFalse(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:40'])) self.assertTrue(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:41'])) def test_uuidfield_1(self): field = UUIDField() value = field.clean('550e8400e29b41d4a716446655440000') self.assertEqual(value, uuid.UUID('550e8400e29b41d4a716446655440000')) def test_uuidfield_2(self): field = UUIDField(required=False) value = field.clean('') self.assertEqual(value, None) def test_uuidfield_3(self): field = UUIDField() with self.assertRaises(ValidationError) as cm: field.clean('550e8400') self.assertEqual(cm.exception.messages[0], 'Enter a valid UUID.') def test_uuidfield_4(self): field = UUIDField() value = field.prepare_value(uuid.UUID('550e8400e29b41d4a716446655440000')) self.assertEqual(value, '550e8400e29b41d4a716446655440000') PKE-GWy,django_tests/forms_tests/tests/test_forms.py# -*- coding: utf-8 -*- from __future__ import unicode_literals import copy import datetime import json import uuid from django.core.exceptions import NON_FIELD_ERRORS from django.core.files.uploadedfile import SimpleUploadedFile from django.core.validators import RegexValidator from django.forms import ( BooleanField, CharField, CheckboxSelectMultiple, ChoiceField, DateField, DateTimeField, EmailField, FileField, FloatField, Form, HiddenInput, IntegerField, MultipleChoiceField, MultipleHiddenInput, MultiValueField, NullBooleanField, PasswordInput, RadioSelect, Select, SplitDateTimeField, Textarea, TextInput, TimeField, ValidationError, forms, widgets, ) from django.forms.utils import ErrorList from django.http import QueryDict from django.template import Context, Template from django.test import TestCase, ignore_warnings from django.test.utils import str_prefix from django.utils import six from django.utils.datastructures import MergeDict, MultiValueDict from django.utils.deprecation import RemovedInDjango19Warning from django.utils.encoding import force_text from django.utils.html import format_html from django.utils.safestring import SafeData, mark_safe class Person(Form): first_name = CharField() last_name = CharField() birthday = DateField() class PersonNew(Form): first_name = CharField(widget=TextInput(attrs={'id': 'first_name_id'})) last_name = CharField() birthday = DateField() class FormsTestCase(TestCase): # 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. def test_form(self): # Pass a dictionary to a Form's __init__(). p = Person({'first_name': 'John', 'last_name': 'Lennon', 'birthday': '1940-10-9'}) self.assertTrue(p.is_bound) self.assertEqual(p.errors, {}) self.assertTrue(p.is_valid()) self.assertHTMLEqual(p.errors.as_ul(), '') self.assertEqual(p.errors.as_text(), '') self.assertEqual(p.cleaned_data["first_name"], 'John') self.assertEqual(p.cleaned_data["last_name"], 'Lennon') self.assertEqual(p.cleaned_data["birthday"], datetime.date(1940, 10, 9)) self.assertHTMLEqual(str(p['first_name']), '') self.assertHTMLEqual(str(p['last_name']), '') self.assertHTMLEqual(str(p['birthday']), '') nonexistenterror = "Key u?'nonexistentfield' not found in 'Person'" with six.assertRaisesRegex(self, KeyError, nonexistenterror): p['nonexistentfield'] self.fail('Attempts to access non-existent fields should fail.') form_output = [] for boundfield in p: form_output.append(str(boundfield)) self.assertHTMLEqual('\n'.join(form_output), """ """) form_output = [] for boundfield in p: form_output.append([boundfield.label, boundfield.data]) self.assertEqual(form_output, [ ['First name', 'John'], ['Last name', 'Lennon'], ['Birthday', '1940-10-9'] ]) self.assertHTMLEqual(str(p), """ """) def test_empty_dict(self): # Empty dictionaries are valid, too. p = Person({}) self.assertTrue(p.is_bound) self.assertEqual(p.errors['first_name'], ['This field is required.']) self.assertEqual(p.errors['last_name'], ['This field is required.']) self.assertEqual(p.errors['birthday'], ['This field is required.']) self.assertFalse(p.is_valid()) self.assertEqual(p.cleaned_data, {}) self.assertHTMLEqual(str(p), """
  • This field is required.
  • This field is required.
  • This field is required.
""") self.assertHTMLEqual(p.as_table(), """
  • This field is required.
  • This field is required.
  • This field is required.
""") self.assertHTMLEqual(p.as_ul(), """
    • This field is required.
    • This field is required.
    • This field is required.
  • """) self.assertHTMLEqual(p.as_p(), """
    • This field is required.

    • This field is required.

    • This field is required.

    """) def test_unbound_form(self): # 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() self.assertFalse(p.is_bound) self.assertEqual(p.errors, {}) self.assertFalse(p.is_valid()) try: p.cleaned_data self.fail('Attempts to access cleaned_data when validation fails should fail.') except AttributeError: pass self.assertHTMLEqual(str(p), """ """) self.assertHTMLEqual(p.as_table(), """ """) self.assertHTMLEqual(p.as_ul(), """
  • """) self.assertHTMLEqual(p.as_p(), """

    """) def test_unicode_values(self): # Unicode values are handled properly. p = Person({'first_name': 'John', 'last_name': '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'}) self.assertHTMLEqual(p.as_table(), '\n\n') self.assertHTMLEqual(p.as_ul(), '
  • \n
  • \n
  • ') self.assertHTMLEqual(p.as_p(), '

    \n

    \n

    ') p = Person({'last_name': 'Lennon'}) self.assertEqual(p.errors['first_name'], ['This field is required.']) self.assertEqual(p.errors['birthday'], ['This field is required.']) self.assertFalse(p.is_valid()) self.assertDictEqual(p.errors, {'birthday': ['This field is required.'], 'first_name': ['This field is required.']}) self.assertEqual(p.cleaned_data, {'last_name': 'Lennon'}) self.assertEqual(p['first_name'].errors, ['This field is required.']) self.assertHTMLEqual(p['first_name'].errors.as_ul(), '
    • This field is required.
    ') self.assertEqual(p['first_name'].errors.as_text(), '* This field is required.') p = Person() self.assertHTMLEqual(str(p['first_name']), '') self.assertHTMLEqual(str(p['last_name']), '') self.assertHTMLEqual(str(p['birthday']), '') def test_cleaned_data_only_fields(self): # 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': 'John', 'last_name': 'Lennon', 'birthday': '1940-10-9', 'extra1': 'hello', 'extra2': 'hello'} p = Person(data) self.assertTrue(p.is_valid()) self.assertEqual(p.cleaned_data['first_name'], 'John') self.assertEqual(p.cleaned_data['last_name'], 'Lennon') self.assertEqual(p.cleaned_data['birthday'], datetime.date(1940, 10, 9)) def test_optional_data(self): # 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': 'John', 'last_name': 'Lennon'} f = OptionalPersonForm(data) self.assertTrue(f.is_valid()) self.assertEqual(f.cleaned_data['nick_name'], '') self.assertEqual(f.cleaned_data['first_name'], 'John') self.assertEqual(f.cleaned_data['last_name'], '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': 'John', 'last_name': 'Lennon'} f = OptionalPersonForm(data) self.assertTrue(f.is_valid()) self.assertEqual(f.cleaned_data['birth_date'], None) self.assertEqual(f.cleaned_data['first_name'], 'John') self.assertEqual(f.cleaned_data['last_name'], 'Lennon') def test_auto_id(self): # "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