mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
boulder-oracle-sprint: Merged to [4065]
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@4066 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
2ef60b546b
commit
19773e0d70
@ -101,6 +101,7 @@ DATABASE_USER = '' # Not used with sqlite3.
|
|||||||
DATABASE_PASSWORD = '' # Not used with sqlite3.
|
DATABASE_PASSWORD = '' # Not used with sqlite3.
|
||||||
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
|
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
|
||||||
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||||
|
DATABASE_OPTIONS = {} # Set to empty dictionary for default.
|
||||||
|
|
||||||
# Host for sending e-mail.
|
# Host for sending e-mail.
|
||||||
EMAIL_HOST = 'localhost'
|
EMAIL_HOST = 'localhost'
|
||||||
@ -228,6 +229,10 @@ MONTH_DAY_FORMAT = 'F j'
|
|||||||
# Hint: you really don't!
|
# Hint: you really don't!
|
||||||
TRANSACTIONS_MANAGED = False
|
TRANSACTIONS_MANAGED = False
|
||||||
|
|
||||||
|
# The User-Agent string to use when checking for URL validity through the
|
||||||
|
# isExistingURL validator.
|
||||||
|
URL_VALIDATOR_USER_AGENT = "Django/0.96pre (http://www.djangoproject.com)"
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# MIDDLEWARE #
|
# MIDDLEWARE #
|
||||||
##############
|
##############
|
||||||
|
@ -169,8 +169,8 @@ var dateParsePatterns = [
|
|||||||
handler: function(bits) {
|
handler: function(bits) {
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
d.setYear(parseInt(bits[1]));
|
d.setYear(parseInt(bits[1]));
|
||||||
d.setDate(parseInt(bits[3], 10));
|
|
||||||
d.setMonth(parseInt(bits[2], 10) - 1);
|
d.setMonth(parseInt(bits[2], 10) - 1);
|
||||||
|
d.setDate(parseInt(bits[3], 10));
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -177,8 +177,8 @@ def output_all(form_fields):
|
|||||||
output_all = register.simple_tag(output_all)
|
output_all = register.simple_tag(output_all)
|
||||||
|
|
||||||
def auto_populated_field_script(auto_pop_fields, change = False):
|
def auto_populated_field_script(auto_pop_fields, change = False):
|
||||||
|
t = []
|
||||||
for field in auto_pop_fields:
|
for field in auto_pop_fields:
|
||||||
t = []
|
|
||||||
if change:
|
if change:
|
||||||
t.append('document.getElementById("id_%s")._changed = true;' % field.name)
|
t.append('document.getElementById("id_%s")._changed = true;' % field.name)
|
||||||
else:
|
else:
|
||||||
|
@ -34,7 +34,7 @@ class CommentManager(models.Manager):
|
|||||||
"""
|
"""
|
||||||
Given a rating_string, this returns a tuple of (rating_range, options).
|
Given a rating_string, this returns a tuple of (rating_range, options).
|
||||||
>>> s = "scale:1-10|First_category|Second_category"
|
>>> s = "scale:1-10|First_category|Second_category"
|
||||||
>>> get_rating_options(s)
|
>>> Comment.objects.get_rating_options(s)
|
||||||
([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ['First category', 'Second category'])
|
([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ['First category', 'Second category'])
|
||||||
"""
|
"""
|
||||||
rating_range, options = rating_string.split('|', 1)
|
rating_range, options = rating_string.split('|', 1)
|
||||||
|
@ -4,6 +4,11 @@ from django.conf import settings
|
|||||||
from email.MIMEText import MIMEText
|
from email.MIMEText import MIMEText
|
||||||
from email.Header import Header
|
from email.Header import Header
|
||||||
import smtplib, rfc822
|
import smtplib, rfc822
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
|
DNS_NAME = socket.getfqdn() # Cache the hostname
|
||||||
|
|
||||||
class BadHeaderError(ValueError):
|
class BadHeaderError(ValueError):
|
||||||
pass
|
pass
|
||||||
@ -50,6 +55,11 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=settings.EMAIL_HOST
|
|||||||
msg['From'] = from_email
|
msg['From'] = from_email
|
||||||
msg['To'] = ', '.join(recipient_list)
|
msg['To'] = ', '.join(recipient_list)
|
||||||
msg['Date'] = rfc822.formatdate()
|
msg['Date'] = rfc822.formatdate()
|
||||||
|
try:
|
||||||
|
random_bits = str(random.getrandbits(64))
|
||||||
|
except AttributeError: # Python 2.3 doesn't have random.getrandbits().
|
||||||
|
random_bits = ''.join([random.choice('1234567890') for i in range(19)])
|
||||||
|
msg['Message-ID'] = "<%d.%s@%s>" % (time.time(), random_bits, DNS_NAME)
|
||||||
try:
|
try:
|
||||||
server.sendmail(from_email, recipient_list, msg.as_string())
|
server.sendmail(from_email, recipient_list, msg.as_string())
|
||||||
num_sent += 1
|
num_sent += 1
|
||||||
|
@ -393,6 +393,8 @@ def get_sql_initial_data_for_model(model):
|
|||||||
if os.path.exists(sql_file):
|
if os.path.exists(sql_file):
|
||||||
fp = open(sql_file, 'U')
|
fp = open(sql_file, 'U')
|
||||||
for statement in statements.split(fp.read()):
|
for statement in statements.split(fp.read()):
|
||||||
|
# Remove any comments from the file
|
||||||
|
statement = re.sub(r"--.*[\n\Z]", "", statement)
|
||||||
if statement.strip():
|
if statement.strip():
|
||||||
output.append(statement + ";")
|
output.append(statement + ";")
|
||||||
fp.close()
|
fp.close()
|
||||||
|
@ -1,54 +1,46 @@
|
|||||||
from math import ceil
|
|
||||||
|
|
||||||
class InvalidPage(Exception):
|
class InvalidPage(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ObjectPaginator(object):
|
class ObjectPaginator(object):
|
||||||
"""
|
"""
|
||||||
This class makes pagination easy. Feed it a QuerySet, plus the number of
|
This class makes pagination easy. Feed it a QuerySet or list, plus the number
|
||||||
objects you want on each page. Then read the hits and pages properties to
|
of objects you want on each page. Then read the hits and pages properties to
|
||||||
see how many pages it involves. Call get_page with a page number (starting
|
see how many pages it involves. Call get_page with a page number (starting
|
||||||
at 0) to get back a list of objects for that page.
|
at 0) to get back a list of objects for that page.
|
||||||
|
|
||||||
Finally, check if a page number has a next/prev page using
|
Finally, check if a page number has a next/prev page using
|
||||||
has_next_page(page_number) and has_previous_page(page_number).
|
has_next_page(page_number) and has_previous_page(page_number).
|
||||||
|
|
||||||
|
Use orphans to avoid small final pages. For example:
|
||||||
|
13 records, num_per_page=10, orphans=2 --> pages==2, len(self.get_page(0))==10
|
||||||
|
12 records, num_per_page=10, orphans=2 --> pages==1, len(self.get_page(0))==12
|
||||||
"""
|
"""
|
||||||
def __init__(self, query_set, num_per_page):
|
def __init__(self, query_set, num_per_page, orphans=0):
|
||||||
self.query_set = query_set
|
self.query_set = query_set
|
||||||
self.num_per_page = num_per_page
|
self.num_per_page = num_per_page
|
||||||
self._hits, self._pages = None, None
|
self.orphans = orphans
|
||||||
self._has_next = {} # Caches page_number -> has_next_boolean
|
self._hits = self._pages = None
|
||||||
|
|
||||||
def get_page(self, page_number):
|
def validate_page_number(self, page_number):
|
||||||
try:
|
try:
|
||||||
page_number = int(page_number)
|
page_number = int(page_number)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidPage
|
raise InvalidPage
|
||||||
if page_number < 0:
|
if page_number < 0 or page_number > self.pages - 1:
|
||||||
raise InvalidPage
|
raise InvalidPage
|
||||||
|
return page_number
|
||||||
|
|
||||||
# Retrieve one extra record, and check for the existence of that extra
|
def get_page(self, page_number):
|
||||||
# record to determine whether there's a next page.
|
page_number = self.validate_page_number(page_number)
|
||||||
limit = self.num_per_page + 1
|
bottom = page_number * self.num_per_page
|
||||||
offset = page_number * self.num_per_page
|
top = bottom + self.num_per_page
|
||||||
|
if top + self.orphans >= self.hits:
|
||||||
object_list = list(self.query_set[offset:offset+limit])
|
top = self.hits
|
||||||
|
return self.query_set[bottom:top]
|
||||||
if not object_list:
|
|
||||||
raise InvalidPage
|
|
||||||
|
|
||||||
self._has_next[page_number] = (len(object_list) > self.num_per_page)
|
|
||||||
return object_list[:self.num_per_page]
|
|
||||||
|
|
||||||
def has_next_page(self, page_number):
|
def has_next_page(self, page_number):
|
||||||
"Does page $page_number have a 'next' page?"
|
"Does page $page_number have a 'next' page?"
|
||||||
if not self._has_next.has_key(page_number):
|
return page_number < self.pages - 1
|
||||||
if self._pages is None:
|
|
||||||
offset = (page_number + 1) * self.num_per_page
|
|
||||||
self._has_next[page_number] = len(self.query_set[offset:offset+1]) > 0
|
|
||||||
else:
|
|
||||||
self._has_next[page_number] = page_number < (self.pages - 1)
|
|
||||||
return self._has_next[page_number]
|
|
||||||
|
|
||||||
def has_previous_page(self, page_number):
|
def has_previous_page(self, page_number):
|
||||||
return page_number > 0
|
return page_number > 0
|
||||||
@ -58,8 +50,7 @@ class ObjectPaginator(object):
|
|||||||
Returns the 1-based index of the first object on the given page,
|
Returns the 1-based index of the first object on the given page,
|
||||||
relative to total objects found (hits).
|
relative to total objects found (hits).
|
||||||
"""
|
"""
|
||||||
if page_number == 0:
|
page_number = self.validate_page_number(page_number)
|
||||||
return 1
|
|
||||||
return (self.num_per_page * page_number) + 1
|
return (self.num_per_page * page_number) + 1
|
||||||
|
|
||||||
def last_on_page(self, page_number):
|
def last_on_page(self, page_number):
|
||||||
@ -67,20 +58,30 @@ class ObjectPaginator(object):
|
|||||||
Returns the 1-based index of the last object on the given page,
|
Returns the 1-based index of the last object on the given page,
|
||||||
relative to total objects found (hits).
|
relative to total objects found (hits).
|
||||||
"""
|
"""
|
||||||
if page_number == 0 and self.num_per_page >= self._hits:
|
page_number = self.validate_page_number(page_number)
|
||||||
return self._hits
|
page_number += 1 # 1-base
|
||||||
elif page_number == (self._pages - 1) and (page_number + 1) * self.num_per_page > self._hits:
|
if page_number == self.pages:
|
||||||
return self._hits
|
return self.hits
|
||||||
return (page_number + 1) * self.num_per_page
|
return page_number * self.num_per_page
|
||||||
|
|
||||||
def _get_hits(self):
|
def _get_hits(self):
|
||||||
if self._hits is None:
|
if self._hits is None:
|
||||||
self._hits = self.query_set.count()
|
# Try .count() or fall back to len().
|
||||||
|
try:
|
||||||
|
self._hits = int(self.query_set.count())
|
||||||
|
except (AttributeError, TypeError, ValueError):
|
||||||
|
# AttributeError if query_set has no object count.
|
||||||
|
# TypeError if query_set.count() required arguments.
|
||||||
|
# ValueError if int() fails.
|
||||||
|
self._hits = len(self.query_set)
|
||||||
return self._hits
|
return self._hits
|
||||||
|
|
||||||
def _get_pages(self):
|
def _get_pages(self):
|
||||||
if self._pages is None:
|
if self._pages is None:
|
||||||
self._pages = int(ceil(self.hits / float(self.num_per_page)))
|
hits = (self.hits - 1 - self.orphans)
|
||||||
|
if hits < 1:
|
||||||
|
hits = 0
|
||||||
|
self._pages = hits // self.num_per_page + 1
|
||||||
return self._pages
|
return self._pages
|
||||||
|
|
||||||
hits = property(_get_hits)
|
hits = property(_get_hits)
|
||||||
|
@ -28,6 +28,7 @@ class Serializer(object):
|
|||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
self.stream = options.get("stream", StringIO())
|
self.stream = options.get("stream", StringIO())
|
||||||
|
self.selected_fields = options.get("fields")
|
||||||
|
|
||||||
self.start_serialization()
|
self.start_serialization()
|
||||||
for obj in queryset:
|
for obj in queryset:
|
||||||
@ -36,11 +37,14 @@ class Serializer(object):
|
|||||||
if field is obj._meta.pk:
|
if field is obj._meta.pk:
|
||||||
continue
|
continue
|
||||||
elif field.rel is None:
|
elif field.rel is None:
|
||||||
self.handle_field(obj, field)
|
if self.selected_fields is None or field.attname in self.selected_fields:
|
||||||
|
self.handle_field(obj, field)
|
||||||
else:
|
else:
|
||||||
self.handle_fk_field(obj, field)
|
if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
|
||||||
|
self.handle_fk_field(obj, field)
|
||||||
for field in obj._meta.many_to_many:
|
for field in obj._meta.many_to_many:
|
||||||
self.handle_m2m_field(obj, field)
|
if self.selected_fields is None or field.attname in self.selected_fields:
|
||||||
|
self.handle_m2m_field(obj, field)
|
||||||
self.end_object(obj)
|
self.end_object(obj)
|
||||||
self.end_serialization()
|
self.end_serialization()
|
||||||
return self.getvalue()
|
return self.getvalue()
|
||||||
|
@ -76,7 +76,7 @@ def Deserializer(object_list, **options):
|
|||||||
m2m_data[field.name] = field.rel.to._default_manager.in_bulk(field_value).values()
|
m2m_data[field.name] = field.rel.to._default_manager.in_bulk(field_value).values()
|
||||||
|
|
||||||
# Handle FK fields
|
# Handle FK fields
|
||||||
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
|
elif field.rel and isinstance(field.rel, models.ManyToOneRel) and field_value is not None:
|
||||||
try:
|
try:
|
||||||
data[field.name] = field.rel.to._default_manager.get(pk=field_value)
|
data[field.name] = field.rel.to._default_manager.get(pk=field_value)
|
||||||
except field.rel.to.DoesNotExist:
|
except field.rel.to.DoesNotExist:
|
||||||
|
@ -166,7 +166,11 @@ class Deserializer(base.Deserializer):
|
|||||||
# If it doesn't exist, set the field to None (which might trigger
|
# If it doesn't exist, set the field to None (which might trigger
|
||||||
# validation error, but that's expected).
|
# validation error, but that's expected).
|
||||||
RelatedModel = self._get_model_from_node(node, "to")
|
RelatedModel = self._get_model_from_node(node, "to")
|
||||||
return RelatedModel.objects.get(pk=getInnerText(node).strip().encode(self.encoding))
|
# Check if there is a child node named 'None', returning None if so.
|
||||||
|
if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return RelatedModel.objects.get(pk=getInnerText(node).strip().encode(self.encoding))
|
||||||
|
|
||||||
def _handle_m2m_field_node(self, node):
|
def _handle_m2m_field_node(self, node):
|
||||||
"""
|
"""
|
||||||
|
@ -33,9 +33,9 @@ Optional Fcgi settings: (setting=value)
|
|||||||
method=IMPL prefork or threaded (default prefork)
|
method=IMPL prefork or threaded (default prefork)
|
||||||
maxrequests=NUMBER number of requests a child handles before it is
|
maxrequests=NUMBER number of requests a child handles before it is
|
||||||
killed and a new child is forked (0 = no limit).
|
killed and a new child is forked (0 = no limit).
|
||||||
maxspare=NUMBER max number of spare processes to keep running.
|
maxspare=NUMBER max number of spare processes / threads
|
||||||
minspare=NUMBER min number of spare processes to prefork.
|
minspare=NUMBER min number of spare processes / threads.
|
||||||
maxchildren=NUMBER hard limit number of processes in prefork mode.
|
maxchildren=NUMBER hard limit number of processes / threads
|
||||||
daemonize=BOOL whether to detach from terminal.
|
daemonize=BOOL whether to detach from terminal.
|
||||||
pidfile=FILE write the spawned process-id to this file.
|
pidfile=FILE write the spawned process-id to this file.
|
||||||
workdir=DIRECTORY change to this directory when daemonizing
|
workdir=DIRECTORY change to this directory when daemonizing
|
||||||
@ -110,7 +110,11 @@ def runfastcgi(argset=[], **kwargs):
|
|||||||
}
|
}
|
||||||
elif options['method'] in ('thread', 'threaded'):
|
elif options['method'] in ('thread', 'threaded'):
|
||||||
from flup.server.fcgi import WSGIServer
|
from flup.server.fcgi import WSGIServer
|
||||||
wsgi_opts = {}
|
wsgi_opts = {
|
||||||
|
'maxSpare': int(options["maxspare"]),
|
||||||
|
'minSpare': int(options["minspare"]),
|
||||||
|
'maxThreads': int(options["maxchildren"]),
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
|
return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
|
||||||
|
|
||||||
|
@ -21,7 +21,10 @@ class NoReverseMatch(Exception):
|
|||||||
def get_mod_func(callback):
|
def get_mod_func(callback):
|
||||||
# Converts 'django.views.news.stories.story_detail' to
|
# Converts 'django.views.news.stories.story_detail' to
|
||||||
# ['django.views.news.stories', 'story_detail']
|
# ['django.views.news.stories', 'story_detail']
|
||||||
dot = callback.rindex('.')
|
try:
|
||||||
|
dot = callback.rindex('.')
|
||||||
|
except ValueError:
|
||||||
|
return callback, ''
|
||||||
return callback[:dot], callback[dot+1:]
|
return callback[:dot], callback[dot+1:]
|
||||||
|
|
||||||
def reverse_helper(regex, *args, **kwargs):
|
def reverse_helper(regex, *args, **kwargs):
|
||||||
|
@ -8,6 +8,7 @@ validator will *always* be run, regardless of whether its associated
|
|||||||
form field is required.
|
form field is required.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import urllib2
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext, gettext_lazy, ngettext
|
from django.utils.translation import gettext, gettext_lazy, ngettext
|
||||||
from django.utils.functional import Promise, lazy
|
from django.utils.functional import Promise, lazy
|
||||||
@ -223,17 +224,25 @@ def isWellFormedXmlFragment(field_data, all_data):
|
|||||||
isWellFormedXml('<root>%s</root>' % field_data, all_data)
|
isWellFormedXml('<root>%s</root>' % field_data, all_data)
|
||||||
|
|
||||||
def isExistingURL(field_data, all_data):
|
def isExistingURL(field_data, all_data):
|
||||||
import urllib2
|
|
||||||
try:
|
try:
|
||||||
u = urllib2.urlopen(field_data)
|
headers = {
|
||||||
|
"Accept" : "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
|
||||||
|
"Accept-Language" : "en-us,en;q=0.5",
|
||||||
|
"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
|
||||||
|
"Connection" : "close",
|
||||||
|
"User-Agent": settings.URL_VALIDATOR_USER_AGENT
|
||||||
|
}
|
||||||
|
req = urllib2.Request(field_data,None, headers)
|
||||||
|
u = urllib2.urlopen(req)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValidationError, gettext("Invalid URL: %s") % field_data
|
raise ValidationError, _("Invalid URL: %s") % field_data
|
||||||
except urllib2.HTTPError, e:
|
except urllib2.HTTPError, e:
|
||||||
# 401s are valid; they just mean authorization is required.
|
# 401s are valid; they just mean authorization is required.
|
||||||
if e.code not in ('401',):
|
# 301 and 302 are redirects; they just mean look somewhere else.
|
||||||
raise ValidationError, gettext("The URL %s is a broken link.") % field_data
|
if str(e.code) not in ('401','301','302'):
|
||||||
|
raise ValidationError, _("The URL %s is a broken link.") % field_data
|
||||||
except: # urllib2.URLError, httplib.InvalidURL, etc.
|
except: # urllib2.URLError, httplib.InvalidURL, etc.
|
||||||
raise ValidationError, gettext("The URL %s is a broken link.") % field_data
|
raise ValidationError, _("The URL %s is a broken link.") % field_data
|
||||||
|
|
||||||
def isValidUSState(field_data, all_data):
|
def isValidUSState(field_data, all_data):
|
||||||
"Checks that the given string is a valid two-letter U.S. state abbreviation"
|
"Checks that the given string is a valid two-letter U.S. state abbreviation"
|
||||||
@ -344,6 +353,38 @@ class UniqueAmongstFieldsWithPrefix(object):
|
|||||||
if field_name != self.field_name and value == field_data:
|
if field_name != self.field_name and value == field_data:
|
||||||
raise ValidationError, self.error_message
|
raise ValidationError, self.error_message
|
||||||
|
|
||||||
|
class NumberIsInRange(object):
|
||||||
|
"""
|
||||||
|
Validator that tests if a value is in a range (inclusive).
|
||||||
|
"""
|
||||||
|
def __init__(self, lower=None, upper=None, error_message=''):
|
||||||
|
self.lower, self.upper = lower, upper
|
||||||
|
if not error_message:
|
||||||
|
if lower and upper:
|
||||||
|
self.error_message = gettext("This value must be between %s and %s.") % (lower, upper)
|
||||||
|
elif lower:
|
||||||
|
self.error_message = gettext("This value must be at least %s.") % lower
|
||||||
|
elif upper:
|
||||||
|
self.error_message = gettext("This value must be no more than %s.") % upper
|
||||||
|
else:
|
||||||
|
self.error_message = error_message
|
||||||
|
|
||||||
|
def __call__(self, field_data, all_data):
|
||||||
|
# Try to make the value numeric. If this fails, we assume another
|
||||||
|
# validator will catch the problem.
|
||||||
|
try:
|
||||||
|
val = float(field_data)
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Now validate
|
||||||
|
if self.lower and self.upper and (val < self.lower or val > self.upper):
|
||||||
|
raise ValidationError(self.error_message)
|
||||||
|
elif self.lower and val < self.lower:
|
||||||
|
raise ValidationError(self.error_message)
|
||||||
|
elif self.upper and val > self.upper:
|
||||||
|
raise ValidationError(self.error_message)
|
||||||
|
|
||||||
class IsAPowerOf(object):
|
class IsAPowerOf(object):
|
||||||
"""
|
"""
|
||||||
>>> v = IsAPowerOf(2)
|
>>> v = IsAPowerOf(2)
|
||||||
|
@ -35,7 +35,8 @@ get_query_module = backend_module_accessor("query")
|
|||||||
get_client_module = backend_module_accessor("client")
|
get_client_module = backend_module_accessor("client")
|
||||||
runshell = lambda: get_client_module().runshell()
|
runshell = lambda: get_client_module().runshell()
|
||||||
|
|
||||||
connection = backend.DatabaseWrapper()
|
connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
|
||||||
|
|
||||||
DatabaseError = backend.DatabaseError
|
DatabaseError = backend.DatabaseError
|
||||||
|
|
||||||
# Register an event that closes the database connection
|
# Register an event that closes the database connection
|
||||||
|
@ -55,7 +55,7 @@ except ImportError:
|
|||||||
from django.utils._threading_local import local
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(local):
|
||||||
def __init__(self):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.queries = []
|
self.queries = []
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@ class DatabaseWrapper:
|
|||||||
_commit = complain
|
_commit = complain
|
||||||
_rollback = complain
|
_rollback = complain
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
pass # close()
|
pass # close()
|
||||||
|
|
||||||
|
@ -65,10 +65,11 @@ except ImportError:
|
|||||||
from django.utils._threading_local import local
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(local):
|
||||||
def __init__(self):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.queries = []
|
self.queries = []
|
||||||
self.server_version = None
|
self.server_version = None
|
||||||
|
self.options = kwargs
|
||||||
|
|
||||||
def _valid_connection(self):
|
def _valid_connection(self):
|
||||||
if self.connection is not None:
|
if self.connection is not None:
|
||||||
@ -95,6 +96,7 @@ class DatabaseWrapper(local):
|
|||||||
kwargs['host'] = settings.DATABASE_HOST
|
kwargs['host'] = settings.DATABASE_HOST
|
||||||
if settings.DATABASE_PORT:
|
if settings.DATABASE_PORT:
|
||||||
kwargs['port'] = int(settings.DATABASE_PORT)
|
kwargs['port'] = int(settings.DATABASE_PORT)
|
||||||
|
kwargs.update(self.options)
|
||||||
self.connection = Database.connect(**kwargs)
|
self.connection = Database.connect(**kwargs)
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
if self.connection.get_server_info() >= '4.1':
|
if self.connection.get_server_info() >= '4.1':
|
||||||
@ -180,6 +182,9 @@ def get_drop_foreignkey_sql():
|
|||||||
def get_pk_default_value():
|
def get_pk_default_value():
|
||||||
return "DEFAULT"
|
return "DEFAULT"
|
||||||
|
|
||||||
|
def get_max_name_length():
|
||||||
|
return 64;
|
||||||
|
|
||||||
OPERATOR_MAPPING = {
|
OPERATOR_MAPPING = {
|
||||||
'exact': '= %s',
|
'exact': '= %s',
|
||||||
'iexact': 'LIKE %s',
|
'iexact': 'LIKE %s',
|
||||||
|
@ -21,9 +21,10 @@ except ImportError:
|
|||||||
from django.utils._threading_local import local
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(local):
|
||||||
def __init__(self):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.queries = []
|
self.queries = []
|
||||||
|
self.options = kwargs
|
||||||
|
|
||||||
def _valid_connection(self):
|
def _valid_connection(self):
|
||||||
return self.connection is not None
|
return self.connection is not None
|
||||||
@ -35,10 +36,10 @@ class DatabaseWrapper(local):
|
|||||||
settings.DATABASE_HOST = 'localhost'
|
settings.DATABASE_HOST = 'localhost'
|
||||||
if len(settings.DATABASE_PORT.strip()) != 0:
|
if len(settings.DATABASE_PORT.strip()) != 0:
|
||||||
dsn = Database.makedsn(settings.DATABASE_HOST, int(settings.DATABASE_PORT), settings.DATABASE_NAME)
|
dsn = Database.makedsn(settings.DATABASE_HOST, int(settings.DATABASE_PORT), settings.DATABASE_NAME)
|
||||||
self.connection = Database.connect(settings.DATABASE_USER, settings.DATABASE_PASSWORD, dsn)
|
self.connection = Database.connect(settings.DATABASE_USER, settings.DATABASE_PASSWORD, dsn, **self.options)
|
||||||
else:
|
else:
|
||||||
conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
|
conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
|
||||||
self.connection = Database.connect(conn_string)
|
self.connection = Database.connect(conn_string, **self.options)
|
||||||
# set oracle date to ansi date format
|
# set oracle date to ansi date format
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'")
|
cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'")
|
||||||
|
@ -21,9 +21,10 @@ except ImportError:
|
|||||||
from django.utils._threading_local import local
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(local):
|
||||||
def __init__(self):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.queries = []
|
self.queries = []
|
||||||
|
self.options = kwargs
|
||||||
|
|
||||||
def cursor(self):
|
def cursor(self):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -40,7 +41,7 @@ class DatabaseWrapper(local):
|
|||||||
conn_string += " host=%s" % settings.DATABASE_HOST
|
conn_string += " host=%s" % settings.DATABASE_HOST
|
||||||
if settings.DATABASE_PORT:
|
if settings.DATABASE_PORT:
|
||||||
conn_string += " port=%s" % settings.DATABASE_PORT
|
conn_string += " port=%s" % settings.DATABASE_PORT
|
||||||
self.connection = Database.connect(conn_string)
|
self.connection = Database.connect(conn_string, **self.options)
|
||||||
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||||
|
@ -21,9 +21,10 @@ except ImportError:
|
|||||||
from django.utils._threading_local import local
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(local):
|
||||||
def __init__(self):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.queries = []
|
self.queries = []
|
||||||
|
self.options = kwargs
|
||||||
|
|
||||||
def cursor(self):
|
def cursor(self):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -40,7 +41,7 @@ class DatabaseWrapper(local):
|
|||||||
conn_string += " host=%s" % settings.DATABASE_HOST
|
conn_string += " host=%s" % settings.DATABASE_HOST
|
||||||
if settings.DATABASE_PORT:
|
if settings.DATABASE_PORT:
|
||||||
conn_string += " port=%s" % settings.DATABASE_PORT
|
conn_string += " port=%s" % settings.DATABASE_PORT
|
||||||
self.connection = Database.connect(conn_string)
|
self.connection = Database.connect(conn_string, **self.options)
|
||||||
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
cursor.tzinfo_factory = None
|
cursor.tzinfo_factory = None
|
||||||
|
@ -42,16 +42,20 @@ except ImportError:
|
|||||||
from django.utils._threading_local import local
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(local):
|
||||||
def __init__(self):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.queries = []
|
self.queries = []
|
||||||
|
self.options = kwargs
|
||||||
|
|
||||||
def cursor(self):
|
def cursor(self):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
if self.connection is None:
|
if self.connection is None:
|
||||||
self.connection = Database.connect(settings.DATABASE_NAME,
|
kwargs = {
|
||||||
detect_types=Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES)
|
'database': settings.DATABASE_NAME,
|
||||||
|
'detect_types': Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES,
|
||||||
|
}
|
||||||
|
kwargs.update(self.options)
|
||||||
|
self.connection = Database.connect(**kwargs)
|
||||||
# Register extract and date_trunc functions.
|
# Register extract and date_trunc functions.
|
||||||
self.connection.create_function("django_extract", 2, _sqlite_extract)
|
self.connection.create_function("django_extract", 2, _sqlite_extract)
|
||||||
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
|
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
|
||||||
|
@ -18,7 +18,7 @@ class CursorDebugWrapper(object):
|
|||||||
if not isinstance(params, (tuple, dict)):
|
if not isinstance(params, (tuple, dict)):
|
||||||
params = tuple(params)
|
params = tuple(params)
|
||||||
self.db.queries.append({
|
self.db.queries.append({
|
||||||
'sql': sql % tuple(params),
|
'sql': sql % params,
|
||||||
'time': "%.3f" % (stop - start),
|
'time': "%.3f" % (stop - start),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -605,7 +605,7 @@ class FileField(Field):
|
|||||||
# If the raw path is passed in, validate it's under the MEDIA_ROOT.
|
# If the raw path is passed in, validate it's under the MEDIA_ROOT.
|
||||||
def isWithinMediaRoot(field_data, all_data):
|
def isWithinMediaRoot(field_data, all_data):
|
||||||
f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
|
f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
|
||||||
if not f.startswith(os.path.normpath(settings.MEDIA_ROOT)):
|
if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
|
||||||
raise validators.ValidationError, _("Enter a valid filename.")
|
raise validators.ValidationError, _("Enter a valid filename.")
|
||||||
field_list[1].validator_list.append(isWithinMediaRoot)
|
field_list[1].validator_list.append(isWithinMediaRoot)
|
||||||
return field_list
|
return field_list
|
||||||
|
@ -286,7 +286,7 @@ def manipulator_validator_unique_together(field_name_list, opts, self, field_dat
|
|||||||
# This is really not going to work for fields that have different
|
# This is really not going to work for fields that have different
|
||||||
# form fields, e.g. DateTime.
|
# form fields, e.g. DateTime.
|
||||||
# This validation needs to occur after html2python to be effective.
|
# This validation needs to occur after html2python to be effective.
|
||||||
field_val = all_data.get(f.attname, None)
|
field_val = all_data.get(f.name, None)
|
||||||
if field_val is None:
|
if field_val is None:
|
||||||
# This will be caught by another validator, assuming the field
|
# This will be caught by another validator, assuming the field
|
||||||
# doesn't have blank=True.
|
# doesn't have blank=True.
|
||||||
|
@ -170,7 +170,6 @@ class _QuerySet(object):
|
|||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
full_query = None
|
|
||||||
select, sql, params, full_query = self._get_sql_clause()
|
select, sql, params, full_query = self._get_sql_clause()
|
||||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||||
|
|
||||||
|
@ -108,8 +108,13 @@ class FormWrapper(object):
|
|||||||
This allows dictionary-style lookups of formfields. It also handles feeding
|
This allows dictionary-style lookups of formfields. It also handles feeding
|
||||||
prepopulated data and validation error messages to the formfield objects.
|
prepopulated data and validation error messages to the formfield objects.
|
||||||
"""
|
"""
|
||||||
def __init__(self, manipulator, data, error_dict, edit_inline=True):
|
def __init__(self, manipulator, data=None, error_dict=None, edit_inline=True):
|
||||||
self.manipulator, self.data = manipulator, data
|
self.manipulator = manipulator
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
if error_dict is None:
|
||||||
|
error_dict = {}
|
||||||
|
self.data = data
|
||||||
self.error_dict = error_dict
|
self.error_dict = error_dict
|
||||||
self._inline_collections = None
|
self._inline_collections = None
|
||||||
self.edit_inline = edit_inline
|
self.edit_inline = edit_inline
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
Django validation and HTML form handling.
|
Django validation and HTML form handling.
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
Validation not tied to a particular field
|
|
||||||
Default value for field
|
Default value for field
|
||||||
Field labels
|
Field labels
|
||||||
Nestable Forms
|
Nestable Forms
|
||||||
@ -11,6 +10,7 @@ TODO:
|
|||||||
"This form field requires foo.js" and form.js_includes()
|
"This form field requires foo.js" and form.js_includes()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from util import ValidationError
|
||||||
from widgets import *
|
from widgets import *
|
||||||
from fields import *
|
from fields import *
|
||||||
from forms import Form
|
from forms import Form
|
||||||
|
@ -14,6 +14,7 @@ __all__ = (
|
|||||||
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
|
||||||
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
'RegexField', 'EmailField', 'URLField', 'BooleanField',
|
||||||
'ChoiceField', 'MultipleChoiceField',
|
'ChoiceField', 'MultipleChoiceField',
|
||||||
|
'ComboField',
|
||||||
)
|
)
|
||||||
|
|
||||||
# These values, if given to to_python(), will trigger the self.required check.
|
# These values, if given to to_python(), will trigger the self.required check.
|
||||||
@ -34,9 +35,9 @@ class Field(object):
|
|||||||
widget = widget()
|
widget = widget()
|
||||||
self.widget = widget
|
self.widget = widget
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates the given value and returns its "normalized" value as an
|
Validates the given value and returns its "cleaned" value as an
|
||||||
appropriate Python object.
|
appropriate Python object.
|
||||||
|
|
||||||
Raises ValidationError for any errors.
|
Raises ValidationError for any errors.
|
||||||
@ -50,9 +51,9 @@ class CharField(Field):
|
|||||||
Field.__init__(self, required, widget)
|
Field.__init__(self, required, widget)
|
||||||
self.max_length, self.min_length = max_length, min_length
|
self.max_length, self.min_length = max_length, min_length
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"Validates max_length and min_length. Returns a Unicode object."
|
"Validates max_length and min_length. Returns a Unicode object."
|
||||||
Field.to_python(self, value)
|
Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES: value = u''
|
||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
value = unicode(str(value), DEFAULT_ENCODING)
|
value = unicode(str(value), DEFAULT_ENCODING)
|
||||||
@ -65,12 +66,12 @@ class CharField(Field):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
class IntegerField(Field):
|
class IntegerField(Field):
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that int() can be called on the input. Returns the result
|
Validates that int() can be called on the input. Returns the result
|
||||||
of int().
|
of int().
|
||||||
"""
|
"""
|
||||||
super(IntegerField, self).to_python(value)
|
super(IntegerField, self).clean(value)
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
@ -89,12 +90,12 @@ class DateField(Field):
|
|||||||
Field.__init__(self, required, widget)
|
Field.__init__(self, required, widget)
|
||||||
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input can be converted to a date. Returns a Python
|
Validates that the input can be converted to a date. Returns a Python
|
||||||
datetime.date object.
|
datetime.date object.
|
||||||
"""
|
"""
|
||||||
Field.to_python(self, value)
|
Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
@ -125,12 +126,12 @@ class DateTimeField(Field):
|
|||||||
Field.__init__(self, required, widget)
|
Field.__init__(self, required, widget)
|
||||||
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input can be converted to a datetime. Returns a
|
Validates that the input can be converted to a datetime. Returns a
|
||||||
Python datetime.datetime object.
|
Python datetime.datetime object.
|
||||||
"""
|
"""
|
||||||
Field.to_python(self, value)
|
Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES:
|
if value in EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
@ -157,12 +158,12 @@ class RegexField(Field):
|
|||||||
self.regex = regex
|
self.regex = regex
|
||||||
self.error_message = error_message or u'Enter a valid value.'
|
self.error_message = error_message or u'Enter a valid value.'
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input matches the regular expression. Returns a
|
Validates that the input matches the regular expression. Returns a
|
||||||
Unicode object.
|
Unicode object.
|
||||||
"""
|
"""
|
||||||
Field.to_python(self, value)
|
Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES: value = u''
|
||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
value = unicode(str(value), DEFAULT_ENCODING)
|
value = unicode(str(value), DEFAULT_ENCODING)
|
||||||
@ -187,17 +188,35 @@ url_re = re.compile(
|
|||||||
r'(?::\d+)?' # optional port
|
r'(?::\d+)?' # optional port
|
||||||
r'(?:/?|/\S+)$', re.IGNORECASE)
|
r'(?:/?|/\S+)$', re.IGNORECASE)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from django.conf import settings
|
||||||
|
URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
|
||||||
|
except ImportError:
|
||||||
|
# It's OK if Django settings aren't configured.
|
||||||
|
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||||
|
|
||||||
class URLField(RegexField):
|
class URLField(RegexField):
|
||||||
def __init__(self, required=True, verify_exists=False, widget=None):
|
def __init__(self, required=True, verify_exists=False, widget=None,
|
||||||
|
validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
||||||
RegexField.__init__(self, url_re, u'Enter a valid URL.', required, widget)
|
RegexField.__init__(self, url_re, u'Enter a valid URL.', required, widget)
|
||||||
self.verify_exists = verify_exists
|
self.verify_exists = verify_exists
|
||||||
|
self.user_agent = validator_user_agent
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
value = RegexField.to_python(self, value)
|
value = RegexField.clean(self, value)
|
||||||
if self.verify_exists:
|
if self.verify_exists:
|
||||||
import urllib2
|
import urllib2
|
||||||
|
from django.conf import settings
|
||||||
|
headers = {
|
||||||
|
"Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
|
||||||
|
"Accept-Language": "en-us,en;q=0.5",
|
||||||
|
"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
|
||||||
|
"Connection": "close",
|
||||||
|
"User-Agent": self.user_agent,
|
||||||
|
}
|
||||||
try:
|
try:
|
||||||
u = urllib2.urlopen(value)
|
req = urllib2.Request(value, None, headers)
|
||||||
|
u = urllib2.urlopen(req)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValidationError(u'Enter a valid URL.')
|
raise ValidationError(u'Enter a valid URL.')
|
||||||
except: # urllib2.URLError, httplib.InvalidURL, etc.
|
except: # urllib2.URLError, httplib.InvalidURL, etc.
|
||||||
@ -207,9 +226,9 @@ class URLField(RegexField):
|
|||||||
class BooleanField(Field):
|
class BooleanField(Field):
|
||||||
widget = CheckboxInput
|
widget = CheckboxInput
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"Returns a Python boolean object."
|
"Returns a Python boolean object."
|
||||||
Field.to_python(self, value)
|
Field.clean(self, value)
|
||||||
return bool(value)
|
return bool(value)
|
||||||
|
|
||||||
class ChoiceField(Field):
|
class ChoiceField(Field):
|
||||||
@ -219,11 +238,11 @@ class ChoiceField(Field):
|
|||||||
Field.__init__(self, required, widget)
|
Field.__init__(self, required, widget)
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input is in self.choices.
|
Validates that the input is in self.choices.
|
||||||
"""
|
"""
|
||||||
value = Field.to_python(self, value)
|
value = Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES: value = u''
|
||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
value = unicode(str(value), DEFAULT_ENCODING)
|
value = unicode(str(value), DEFAULT_ENCODING)
|
||||||
@ -238,7 +257,7 @@ class MultipleChoiceField(ChoiceField):
|
|||||||
def __init__(self, choices=(), required=True, widget=SelectMultiple):
|
def __init__(self, choices=(), required=True, widget=SelectMultiple):
|
||||||
ChoiceField.__init__(self, choices, required, widget)
|
ChoiceField.__init__(self, choices, required, widget)
|
||||||
|
|
||||||
def to_python(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input is a list or tuple.
|
Validates that the input is a list or tuple.
|
||||||
"""
|
"""
|
||||||
@ -259,3 +278,18 @@ class MultipleChoiceField(ChoiceField):
|
|||||||
if val not in valid_values:
|
if val not in valid_values:
|
||||||
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % val)
|
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % val)
|
||||||
return new_value
|
return new_value
|
||||||
|
|
||||||
|
class ComboField(Field):
|
||||||
|
def __init__(self, fields=(), required=True, widget=None):
|
||||||
|
Field.__init__(self, required, widget)
|
||||||
|
self.fields = fields
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
"""
|
||||||
|
Validates the given value against all of self.fields, which is a
|
||||||
|
list of Field instances.
|
||||||
|
"""
|
||||||
|
Field.clean(self, value)
|
||||||
|
for field in self.fields:
|
||||||
|
value = field.clean(value)
|
||||||
|
return value
|
||||||
|
@ -6,6 +6,13 @@ from fields import Field
|
|||||||
from widgets import TextInput, Textarea
|
from widgets import TextInput, Textarea
|
||||||
from util import ErrorDict, ErrorList, ValidationError
|
from util import ErrorDict, ErrorList, ValidationError
|
||||||
|
|
||||||
|
NON_FIELD_ERRORS = '__all__'
|
||||||
|
|
||||||
|
def pretty_name(name):
|
||||||
|
"Converts 'first_name' to 'First name'"
|
||||||
|
name = name[0].upper() + name[1:]
|
||||||
|
return name.replace('_', ' ')
|
||||||
|
|
||||||
class DeclarativeFieldsMetaclass(type):
|
class DeclarativeFieldsMetaclass(type):
|
||||||
"Metaclass that converts Field attributes to a dictionary called 'fields'."
|
"Metaclass that converts Field attributes to a dictionary called 'fields'."
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
@ -18,22 +25,33 @@ class Form(object):
|
|||||||
|
|
||||||
def __init__(self, data=None): # TODO: prefix stuff
|
def __init__(self, data=None): # TODO: prefix stuff
|
||||||
self.data = data or {}
|
self.data = data or {}
|
||||||
self.__data_python = None # Stores the data after to_python() has been called.
|
self.clean_data = None # Stores the data after clean() has been called.
|
||||||
self.__errors = None # Stores the errors after to_python() has been called.
|
self.__errors = None # Stores the errors after clean() has been called.
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.as_table()
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
yield BoundField(self, field, name)
|
yield BoundField(self, field, name)
|
||||||
|
|
||||||
def to_python(self):
|
def __getitem__(self, name):
|
||||||
|
"Returns a BoundField with the given name."
|
||||||
|
try:
|
||||||
|
field = self.fields[name]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError('Key %r not found in Form' % name)
|
||||||
|
return BoundField(self, field, name)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
if self.__errors is None:
|
if self.__errors is None:
|
||||||
self._validate()
|
self.full_clean()
|
||||||
return self.__data_python
|
return self.clean_data
|
||||||
|
|
||||||
def errors(self):
|
def errors(self):
|
||||||
"Returns an ErrorDict for self.data"
|
"Returns an ErrorDict for self.data"
|
||||||
if self.__errors is None:
|
if self.__errors is None:
|
||||||
self._validate()
|
self.full_clean()
|
||||||
return self.__errors
|
return self.__errors
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
@ -44,27 +62,75 @@ class Form(object):
|
|||||||
"""
|
"""
|
||||||
return not bool(self.errors())
|
return not bool(self.errors())
|
||||||
|
|
||||||
def __getitem__(self, name):
|
def as_table(self):
|
||||||
"Returns a BoundField with the given name."
|
"Returns this form rendered as an HTML <table>."
|
||||||
try:
|
output = u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
|
||||||
field = self.fields[name]
|
return '<table>\n%s\n</table>' % output
|
||||||
except KeyError:
|
|
||||||
raise KeyError('Key %r not found in Form' % name)
|
|
||||||
return BoundField(self, field, name)
|
|
||||||
|
|
||||||
def _validate(self):
|
def as_ul(self):
|
||||||
data_python = {}
|
"Returns this form rendered as an HTML <ul>."
|
||||||
|
output = u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
|
||||||
|
return '<ul>\n%s\n</ul>' % output
|
||||||
|
|
||||||
|
def as_table_with_errors(self):
|
||||||
|
"Returns this form rendered as an HTML <table>, with errors."
|
||||||
|
output = []
|
||||||
|
if self.errors().get(NON_FIELD_ERRORS):
|
||||||
|
# Errors not corresponding to a particular field are displayed at the top.
|
||||||
|
output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]]))
|
||||||
|
for name, field in self.fields.items():
|
||||||
|
bf = BoundField(self, field, name)
|
||||||
|
if bf.errors:
|
||||||
|
output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors]))
|
||||||
|
output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf))
|
||||||
|
return '<table>\n%s\n</table>' % '\n'.join(output)
|
||||||
|
|
||||||
|
def as_ul_with_errors(self):
|
||||||
|
"Returns this form rendered as an HTML <ul>, with errors."
|
||||||
|
output = []
|
||||||
|
if self.errors().get(NON_FIELD_ERRORS):
|
||||||
|
# Errors not corresponding to a particular field are displayed at the top.
|
||||||
|
output.append('<li><ul>%s</ul></li>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]]))
|
||||||
|
for name, field in self.fields.items():
|
||||||
|
bf = BoundField(self, field, name)
|
||||||
|
line = '<li>'
|
||||||
|
if bf.errors:
|
||||||
|
line += '<ul>%s</ul>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])
|
||||||
|
line += '%s: %s</li>' % (pretty_name(name), bf)
|
||||||
|
output.append(line)
|
||||||
|
return '<ul>\n%s\n</ul>' % '\n'.join(output)
|
||||||
|
|
||||||
|
def full_clean(self):
|
||||||
|
"""
|
||||||
|
Cleans all of self.data and populates self.__errors and self.clean_data.
|
||||||
|
"""
|
||||||
|
self.clean_data = {}
|
||||||
errors = ErrorDict()
|
errors = ErrorDict()
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
|
value = self.data.get(name, None)
|
||||||
try:
|
try:
|
||||||
value = field.to_python(self.data.get(name, None))
|
value = field.clean(value)
|
||||||
data_python[name] = value
|
self.clean_data[name] = value
|
||||||
|
if hasattr(self, 'clean_%s' % name):
|
||||||
|
value = getattr(self, 'clean_%s' % name)()
|
||||||
|
self.clean_data[name] = value
|
||||||
except ValidationError, e:
|
except ValidationError, e:
|
||||||
errors[name] = e.messages
|
errors[name] = e.messages
|
||||||
if not errors: # Only set self.data_python if there weren't errors.
|
try:
|
||||||
self.__data_python = data_python
|
self.clean_data = self.clean()
|
||||||
|
except ValidationError, e:
|
||||||
|
errors[NON_FIELD_ERRORS] = e.messages
|
||||||
|
if errors:
|
||||||
|
self.clean_data = None
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""
|
||||||
|
Hook for doing any extra form-wide cleaning after Field.clean() been
|
||||||
|
called on every field.
|
||||||
|
"""
|
||||||
|
return self.clean_data
|
||||||
|
|
||||||
class BoundField(object):
|
class BoundField(object):
|
||||||
"A Field plus data"
|
"A Field plus data"
|
||||||
def __init__(self, form, field, name):
|
def __init__(self, form, field, name):
|
||||||
|
@ -868,8 +868,11 @@ class Library(object):
|
|||||||
dict = func(*args)
|
dict = func(*args)
|
||||||
|
|
||||||
if not getattr(self, 'nodelist', False):
|
if not getattr(self, 'nodelist', False):
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template, select_template
|
||||||
t = get_template(file_name)
|
if hasattr(file_name, '__iter__'):
|
||||||
|
t = select_template(file_name)
|
||||||
|
else:
|
||||||
|
t = get_template(file_name)
|
||||||
self.nodelist = t.nodelist
|
self.nodelist = t.nodelist
|
||||||
return self.nodelist.render(context_class(dict))
|
return self.nodelist.render(context_class(dict))
|
||||||
|
|
||||||
|
@ -421,7 +421,11 @@ def filesizeformat(bytes):
|
|||||||
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
|
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
|
||||||
bytes, etc).
|
bytes, etc).
|
||||||
"""
|
"""
|
||||||
bytes = float(bytes)
|
try:
|
||||||
|
bytes = float(bytes)
|
||||||
|
except TypeError:
|
||||||
|
return "0 bytes"
|
||||||
|
|
||||||
if bytes < 1024:
|
if bytes < 1024:
|
||||||
return "%d byte%s" % (bytes, bytes != 1 and 's' or '')
|
return "%d byte%s" % (bytes, bytes != 1 and 's' or '')
|
||||||
if bytes < 1024 * 1024:
|
if bytes < 1024 * 1024:
|
||||||
|
@ -124,17 +124,27 @@ class ForNode(Node):
|
|||||||
return nodelist.render(context)
|
return nodelist.render(context)
|
||||||
|
|
||||||
class IfChangedNode(Node):
|
class IfChangedNode(Node):
|
||||||
def __init__(self, nodelist):
|
def __init__(self, nodelist, *varlist):
|
||||||
self.nodelist = nodelist
|
self.nodelist = nodelist
|
||||||
self._last_seen = None
|
self._last_seen = None
|
||||||
|
self._varlist = varlist
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
if context.has_key('forloop') and context['forloop']['first']:
|
if context.has_key('forloop') and context['forloop']['first']:
|
||||||
self._last_seen = None
|
self._last_seen = None
|
||||||
content = self.nodelist.render(context)
|
try:
|
||||||
if content != self._last_seen:
|
if self._varlist:
|
||||||
|
# Consider multiple parameters.
|
||||||
|
# This automatically behaves like a OR evaluation of the multiple variables.
|
||||||
|
compare_to = [resolve_variable(var, context) for var in self._varlist]
|
||||||
|
else:
|
||||||
|
compare_to = self.nodelist.render(context)
|
||||||
|
except VariableDoesNotExist:
|
||||||
|
compare_to = None
|
||||||
|
|
||||||
|
if compare_to != self._last_seen:
|
||||||
firstloop = (self._last_seen == None)
|
firstloop = (self._last_seen == None)
|
||||||
self._last_seen = content
|
self._last_seen = compare_to
|
||||||
context.push()
|
context.push()
|
||||||
context['ifchanged'] = {'firstloop': firstloop}
|
context['ifchanged'] = {'firstloop': firstloop}
|
||||||
content = self.nodelist.render(context)
|
content = self.nodelist.render(context)
|
||||||
@ -634,23 +644,34 @@ def ifchanged(parser, token):
|
|||||||
"""
|
"""
|
||||||
Check if a value has changed from the last iteration of a loop.
|
Check if a value has changed from the last iteration of a loop.
|
||||||
|
|
||||||
The 'ifchanged' block tag is used within a loop. It checks its own rendered
|
The 'ifchanged' block tag is used within a loop. It has two possible uses.
|
||||||
contents against its previous state and only displays its content if the
|
|
||||||
value has changed::
|
|
||||||
|
|
||||||
<h1>Archive for {{ year }}</h1>
|
1. Checks its own rendered contents against its previous state and only
|
||||||
|
displays the content if it has changed. For example, this displays a list of
|
||||||
|
days, only displaying the month if it changes::
|
||||||
|
|
||||||
{% for date in days %}
|
<h1>Archive for {{ year }}</h1>
|
||||||
{% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
|
|
||||||
<a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
|
{% for date in days %}
|
||||||
{% endfor %}
|
{% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
|
||||||
|
<a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
2. If given a variable, check whether that variable has changed. For example, the
|
||||||
|
following shows the date every time it changes, but only shows the hour if both
|
||||||
|
the hour and the date have changed::
|
||||||
|
|
||||||
|
{% for date in days %}
|
||||||
|
{% ifchanged date.date %} {{ date.date }} {% endifchanged %}
|
||||||
|
{% ifchanged date.hour date.date %}
|
||||||
|
{{ date.hour }}
|
||||||
|
{% endifchanged %}
|
||||||
|
{% endfor %}
|
||||||
"""
|
"""
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
if len(bits) != 1:
|
|
||||||
raise TemplateSyntaxError, "'ifchanged' tag takes no arguments"
|
|
||||||
nodelist = parser.parse(('endifchanged',))
|
nodelist = parser.parse(('endifchanged',))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
return IfChangedNode(nodelist)
|
return IfChangedNode(nodelist, *bits[1:])
|
||||||
ifchanged = register.tag(ifchanged)
|
ifchanged = register.tag(ifchanged)
|
||||||
|
|
||||||
#@register.tag
|
#@register.tag
|
||||||
|
@ -610,6 +610,15 @@ fails. If no message is passed in, a default message is used.
|
|||||||
string "123" is less than the string "2", for example. If you don't want
|
string "123" is less than the string "2", for example. If you don't want
|
||||||
string comparison here, you will need to write your own validator.
|
string comparison here, you will need to write your own validator.
|
||||||
|
|
||||||
|
``NumberIsInRange``
|
||||||
|
Takes two boundary numbers, ``lower`` and ``upper``, and checks that the
|
||||||
|
field is greater than ``lower`` (if given) and less than ``upper`` (if
|
||||||
|
given).
|
||||||
|
|
||||||
|
Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow
|
||||||
|
values of both 10 and 20. This validator only checks numeric values
|
||||||
|
(e.g., float and integer values).
|
||||||
|
|
||||||
``IsAPowerOf``
|
``IsAPowerOf``
|
||||||
Takes an integer argument and when called as a validator, checks that the
|
Takes an integer argument and when called as a validator, checks that the
|
||||||
field being validated is a power of the integer.
|
field being validated is a power of the integer.
|
||||||
|
@ -265,6 +265,14 @@ Default: ``''`` (Empty string)
|
|||||||
The name of the database to use. For SQLite, it's the full path to the database
|
The name of the database to use. For SQLite, it's the full path to the database
|
||||||
file.
|
file.
|
||||||
|
|
||||||
|
DATABASE_OPTIONS
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Default: ``{}`` (Empty dictionary)
|
||||||
|
|
||||||
|
Extra parameters to use when connecting to the database. Consult backend
|
||||||
|
module's document for available keywords.
|
||||||
|
|
||||||
DATABASE_PASSWORD
|
DATABASE_PASSWORD
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@ -814,6 +822,16 @@ manual configuration option (see below), Django will *not* touch the ``TZ``
|
|||||||
environment variable, and it'll be up to you to ensure your processes are
|
environment variable, and it'll be up to you to ensure your processes are
|
||||||
running in the correct environment.
|
running in the correct environment.
|
||||||
|
|
||||||
|
URL_VALIDATOR_USER_AGENT
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Default: ``Django/<version> (http://www.djangoproject.com/)``
|
||||||
|
|
||||||
|
The string to use as the ``User-Agent`` header when checking to see if URLs
|
||||||
|
exist (see the ``verify_exists`` option on URLField_).
|
||||||
|
|
||||||
|
.. URLField: ../model_api/#urlfield
|
||||||
|
|
||||||
USE_ETAGS
|
USE_ETAGS
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ latest five news items::
|
|||||||
from django.contrib.syndication.feeds import Feed
|
from django.contrib.syndication.feeds import Feed
|
||||||
from chicagocrime.models import NewsItem
|
from chicagocrime.models import NewsItem
|
||||||
|
|
||||||
class SiteNewsFeed(Feed):
|
class LatestEntries(Feed):
|
||||||
title = "Chicagocrime.org site news"
|
title = "Chicagocrime.org site news"
|
||||||
link = "/sitenews/"
|
link = "/sitenews/"
|
||||||
description = "Updates on changes and additions to chicagocrime.org."
|
description = "Updates on changes and additions to chicagocrime.org."
|
||||||
@ -120,8 +120,8 @@ One thing's left to do. In an RSS feed, each ``<item>`` has a ``<title>``,
|
|||||||
put into those elements.
|
put into those elements.
|
||||||
|
|
||||||
* To specify the contents of ``<title>`` and ``<description>``, create
|
* To specify the contents of ``<title>`` and ``<description>``, create
|
||||||
`Django templates`_ called ``feeds/sitenews_title.html`` and
|
`Django templates`_ called ``feeds/latest_title.html`` and
|
||||||
``feeds/sitenews_description.html``, where ``sitenews`` is the ``slug``
|
``feeds/latest_description.html``, where ``latest`` is the ``slug``
|
||||||
specified in the URLconf for the given feed. Note the ``.html`` extension
|
specified in the URLconf for the given feed. Note the ``.html`` extension
|
||||||
is required. The RSS system renders that template for each item, passing
|
is required. The RSS system renders that template for each item, passing
|
||||||
it two template context variables:
|
it two template context variables:
|
||||||
@ -145,6 +145,16 @@ put into those elements.
|
|||||||
Both ``get_absolute_url()`` and ``item_link()`` should return the item's
|
Both ``get_absolute_url()`` and ``item_link()`` should return the item's
|
||||||
URL as a normal Python string.
|
URL as a normal Python string.
|
||||||
|
|
||||||
|
* For the LatestEntries example above, we could have very simple feed templates:
|
||||||
|
|
||||||
|
* latest_title.html::
|
||||||
|
|
||||||
|
{{ obj.title }}
|
||||||
|
|
||||||
|
* latest_description.html::
|
||||||
|
|
||||||
|
{{ obj.description }}
|
||||||
|
|
||||||
.. _chicagocrime.org: http://www.chicagocrime.org/
|
.. _chicagocrime.org: http://www.chicagocrime.org/
|
||||||
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/
|
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/
|
||||||
.. _Django templates: http://www.djangoproject.com/documentation/templates/
|
.. _Django templates: http://www.djangoproject.com/documentation/templates/
|
||||||
|
@ -473,7 +473,7 @@ block are output::
|
|||||||
In the above, if ``athlete_list`` is not empty, the number of athletes will be
|
In the above, if ``athlete_list`` is not empty, the number of athletes will be
|
||||||
displayed by the ``{{ athlete_list|length }}`` variable.
|
displayed by the ``{{ athlete_list|length }}`` variable.
|
||||||
|
|
||||||
As you can see, the ``if`` tag can take an option ``{% else %}`` clause that
|
As you can see, the ``if`` tag can take an optional ``{% else %}`` clause that
|
||||||
will be displayed if the test fails.
|
will be displayed if the test fails.
|
||||||
|
|
||||||
``if`` tags may use ``and``, ``or`` or ``not`` to test a number of variables or
|
``if`` tags may use ``and``, ``or`` or ``not`` to test a number of variables or
|
||||||
@ -525,16 +525,29 @@ ifchanged
|
|||||||
|
|
||||||
Check if a value has changed from the last iteration of a loop.
|
Check if a value has changed from the last iteration of a loop.
|
||||||
|
|
||||||
The ``ifchanged`` block tag is used within a loop. It checks its own rendered
|
The 'ifchanged' block tag is used within a loop. It has two possible uses.
|
||||||
contents against its previous state and only displays its content if the value
|
|
||||||
has changed::
|
|
||||||
|
|
||||||
<h1>Archive for {{ year }}</h1>
|
1. Checks its own rendered contents against its previous state and only
|
||||||
|
displays the content if it has changed. For example, this displays a list of
|
||||||
|
days, only displaying the month if it changes::
|
||||||
|
|
||||||
{% for day in days %}
|
<h1>Archive for {{ year }}</h1>
|
||||||
{% ifchanged %}<h3>{{ day|date:"F" }}</h3>{% endifchanged %}
|
|
||||||
<a href="{{ day|date:"M/d"|lower }}/">{{ day|date:"j" }}</a>
|
{% for date in days %}
|
||||||
{% endfor %}
|
{% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
|
||||||
|
<a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
2. **New in Django development version.** If given a variable, check whether that
|
||||||
|
variable has changed. For example, the following shows the date every time it
|
||||||
|
changes, but only shows the hour if both the hour and the date has changed::
|
||||||
|
|
||||||
|
{% for date in days %}
|
||||||
|
{% ifchanged date.date %} {{ date.date }} {% endifchanged %}
|
||||||
|
{% ifchanged date.hour date.date %}
|
||||||
|
{{ date.hour }}
|
||||||
|
{% endifchanged %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
ifequal
|
ifequal
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -558,7 +571,7 @@ The arguments can be hard-coded strings, so the following is valid::
|
|||||||
It is only possible to compare an argument to template variables or strings.
|
It is only possible to compare an argument to template variables or strings.
|
||||||
You cannot check for equality with Python objects such as ``True`` or
|
You cannot check for equality with Python objects such as ``True`` or
|
||||||
``False``. If you need to test if something is true or false, use the ``if``
|
``False``. If you need to test if something is true or false, use the ``if``
|
||||||
and ``ifnot`` tags instead.
|
tag instead.
|
||||||
|
|
||||||
ifnotequal
|
ifnotequal
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
@ -321,7 +321,7 @@ Note::
|
|||||||
|
|
||||||
def some_view(request):
|
def some_view(request):
|
||||||
# ...
|
# ...
|
||||||
return render_to_response('my_template'html',
|
return render_to_response('my_template.html',
|
||||||
my_data_dictionary,
|
my_data_dictionary,
|
||||||
context_instance=RequestContext(request))
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ used to validate that code behaves as expected. When refactoring or
|
|||||||
modifying code, tests serve as a guide to ensure that behavior hasn't
|
modifying code, tests serve as a guide to ensure that behavior hasn't
|
||||||
changed unexpectedly as a result of the refactor.
|
changed unexpectedly as a result of the refactor.
|
||||||
|
|
||||||
Testing an web application is a complex task, as there are many
|
Testing a web application is a complex task, as there are many
|
||||||
components of a web application that must be validated and tested. To
|
components of a web application that must be validated and tested. To
|
||||||
help you test your application, Django provides a test execution
|
help you test your application, Django provides a test execution
|
||||||
framework, and range of utilities that can be used to stimulate and
|
framework, and range of utilities that can be used to stimulate and
|
||||||
|
@ -64,4 +64,17 @@ True
|
|||||||
>>> paginator.last_on_page(1)
|
>>> paginator.last_on_page(1)
|
||||||
9
|
9
|
||||||
|
|
||||||
|
# Add a few more records to test out the orphans feature.
|
||||||
|
>>> for x in range(10, 13):
|
||||||
|
... Article(headline="Article %s" % x, pub_date=datetime(2006, 10, 6)).save()
|
||||||
|
|
||||||
|
# With orphans set to 3 and 10 items per page, we should get all 12 items on a single page:
|
||||||
|
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=3)
|
||||||
|
>>> paginator.pages
|
||||||
|
1
|
||||||
|
|
||||||
|
# With orphans only set to 1, we should get two pages:
|
||||||
|
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1)
|
||||||
|
>>> paginator.pages
|
||||||
|
2
|
||||||
"""}
|
"""}
|
||||||
|
@ -343,63 +343,63 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
|
|||||||
# CharField ###################################################################
|
# CharField ###################################################################
|
||||||
|
|
||||||
>>> f = CharField(required=False)
|
>>> f = CharField(required=False)
|
||||||
>>> f.to_python(1)
|
>>> f.clean(1)
|
||||||
u'1'
|
u'1'
|
||||||
>>> f.to_python('hello')
|
>>> f.clean('hello')
|
||||||
u'hello'
|
u'hello'
|
||||||
>>> f.to_python(None)
|
>>> f.clean(None)
|
||||||
u''
|
u''
|
||||||
>>> f.to_python([1, 2, 3])
|
>>> f.clean([1, 2, 3])
|
||||||
u'[1, 2, 3]'
|
u'[1, 2, 3]'
|
||||||
|
|
||||||
CharField accepts an optional max_length parameter:
|
CharField accepts an optional max_length parameter:
|
||||||
>>> f = CharField(max_length=10, required=False)
|
>>> f = CharField(max_length=10, required=False)
|
||||||
>>> f.to_python('')
|
>>> f.clean('')
|
||||||
u''
|
u''
|
||||||
>>> f.to_python('12345')
|
>>> f.clean('12345')
|
||||||
u'12345'
|
u'12345'
|
||||||
>>> f.to_python('1234567890')
|
>>> f.clean('1234567890')
|
||||||
u'1234567890'
|
u'1234567890'
|
||||||
>>> f.to_python('1234567890a')
|
>>> f.clean('1234567890a')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Ensure this value has at most 10 characters.']
|
ValidationError: [u'Ensure this value has at most 10 characters.']
|
||||||
|
|
||||||
CharField accepts an optional min_length parameter:
|
CharField accepts an optional min_length parameter:
|
||||||
>>> f = CharField(min_length=10, required=False)
|
>>> f = CharField(min_length=10, required=False)
|
||||||
>>> f.to_python('')
|
>>> f.clean('')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Ensure this value has at least 10 characters.']
|
ValidationError: [u'Ensure this value has at least 10 characters.']
|
||||||
>>> f.to_python('12345')
|
>>> f.clean('12345')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Ensure this value has at least 10 characters.']
|
ValidationError: [u'Ensure this value has at least 10 characters.']
|
||||||
>>> f.to_python('1234567890')
|
>>> f.clean('1234567890')
|
||||||
u'1234567890'
|
u'1234567890'
|
||||||
>>> f.to_python('1234567890a')
|
>>> f.clean('1234567890a')
|
||||||
u'1234567890a'
|
u'1234567890a'
|
||||||
|
|
||||||
# IntegerField ################################################################
|
# IntegerField ################################################################
|
||||||
|
|
||||||
>>> f = IntegerField()
|
>>> f = IntegerField()
|
||||||
>>> f.to_python('1')
|
>>> f.clean('1')
|
||||||
1
|
1
|
||||||
>>> isinstance(f.to_python('1'), int)
|
>>> isinstance(f.clean('1'), int)
|
||||||
True
|
True
|
||||||
>>> f.to_python('23')
|
>>> f.clean('23')
|
||||||
23
|
23
|
||||||
>>> f.to_python('a')
|
>>> f.clean('a')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a whole number.']
|
ValidationError: [u'Enter a whole number.']
|
||||||
>>> f.to_python('1 ')
|
>>> f.clean('1 ')
|
||||||
1
|
1
|
||||||
>>> f.to_python(' 1')
|
>>> f.clean(' 1')
|
||||||
1
|
1
|
||||||
>>> f.to_python(' 1 ')
|
>>> f.clean(' 1 ')
|
||||||
1
|
1
|
||||||
>>> f.to_python('1a')
|
>>> f.clean('1a')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a whole number.']
|
ValidationError: [u'Enter a whole number.']
|
||||||
@ -408,75 +408,75 @@ ValidationError: [u'Enter a whole number.']
|
|||||||
|
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
>>> f = DateField()
|
>>> f = DateField()
|
||||||
>>> f.to_python(datetime.date(2006, 10, 25))
|
>>> f.clean(datetime.date(2006, 10, 25))
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('2006-10-25')
|
>>> f.clean('2006-10-25')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('10/25/2006')
|
>>> f.clean('10/25/2006')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('10/25/06')
|
>>> f.clean('10/25/06')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('Oct 25 2006')
|
>>> f.clean('Oct 25 2006')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('October 25 2006')
|
>>> f.clean('October 25 2006')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('October 25, 2006')
|
>>> f.clean('October 25, 2006')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('25 October 2006')
|
>>> f.clean('25 October 2006')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('25 October, 2006')
|
>>> f.clean('25 October, 2006')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('2006-4-31')
|
>>> f.clean('2006-4-31')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date.']
|
ValidationError: [u'Enter a valid date.']
|
||||||
>>> f.to_python('200a-10-25')
|
>>> f.clean('200a-10-25')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date.']
|
ValidationError: [u'Enter a valid date.']
|
||||||
>>> f.to_python('25/10/06')
|
>>> f.clean('25/10/06')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date.']
|
ValidationError: [u'Enter a valid date.']
|
||||||
>>> f.to_python(None)
|
>>> f.clean(None)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This field is required.']
|
ValidationError: [u'This field is required.']
|
||||||
|
|
||||||
>>> f = DateField(required=False)
|
>>> f = DateField(required=False)
|
||||||
>>> f.to_python(None)
|
>>> f.clean(None)
|
||||||
>>> repr(f.to_python(None))
|
>>> repr(f.clean(None))
|
||||||
'None'
|
'None'
|
||||||
>>> f.to_python('')
|
>>> f.clean('')
|
||||||
>>> repr(f.to_python(''))
|
>>> repr(f.clean(''))
|
||||||
'None'
|
'None'
|
||||||
|
|
||||||
DateField accepts an optional input_formats parameter:
|
DateField accepts an optional input_formats parameter:
|
||||||
>>> f = DateField(input_formats=['%Y %m %d'])
|
>>> f = DateField(input_formats=['%Y %m %d'])
|
||||||
>>> f.to_python(datetime.date(2006, 10, 25))
|
>>> f.clean(datetime.date(2006, 10, 25))
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
>>> f.to_python('2006 10 25')
|
>>> f.clean('2006 10 25')
|
||||||
datetime.date(2006, 10, 25)
|
datetime.date(2006, 10, 25)
|
||||||
|
|
||||||
The input_formats parameter overrides all default input formats,
|
The input_formats parameter overrides all default input formats,
|
||||||
so the default formats won't work unless you specify them:
|
so the default formats won't work unless you specify them:
|
||||||
>>> f.to_python('2006-10-25')
|
>>> f.clean('2006-10-25')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date.']
|
ValidationError: [u'Enter a valid date.']
|
||||||
>>> f.to_python('10/25/2006')
|
>>> f.clean('10/25/2006')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date.']
|
ValidationError: [u'Enter a valid date.']
|
||||||
>>> f.to_python('10/25/06')
|
>>> f.clean('10/25/06')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date.']
|
ValidationError: [u'Enter a valid date.']
|
||||||
@ -485,63 +485,63 @@ ValidationError: [u'Enter a valid date.']
|
|||||||
|
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
>>> f = DateTimeField()
|
>>> f = DateTimeField()
|
||||||
>>> f.to_python(datetime.date(2006, 10, 25))
|
>>> f.clean(datetime.date(2006, 10, 25))
|
||||||
datetime.datetime(2006, 10, 25, 0, 0)
|
datetime.datetime(2006, 10, 25, 0, 0)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
||||||
datetime.datetime(2006, 10, 25, 14, 30, 59)
|
datetime.datetime(2006, 10, 25, 14, 30, 59)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
||||||
datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
|
datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
|
||||||
>>> f.to_python('2006-10-25 14:30:45')
|
>>> f.clean('2006-10-25 14:30:45')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
||||||
>>> f.to_python('2006-10-25 14:30:00')
|
>>> f.clean('2006-10-25 14:30:00')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python('2006-10-25 14:30')
|
>>> f.clean('2006-10-25 14:30')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python('2006-10-25')
|
>>> f.clean('2006-10-25')
|
||||||
datetime.datetime(2006, 10, 25, 0, 0)
|
datetime.datetime(2006, 10, 25, 0, 0)
|
||||||
>>> f.to_python('10/25/2006 14:30:45')
|
>>> f.clean('10/25/2006 14:30:45')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
||||||
>>> f.to_python('10/25/2006 14:30:00')
|
>>> f.clean('10/25/2006 14:30:00')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python('10/25/2006 14:30')
|
>>> f.clean('10/25/2006 14:30')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python('10/25/2006')
|
>>> f.clean('10/25/2006')
|
||||||
datetime.datetime(2006, 10, 25, 0, 0)
|
datetime.datetime(2006, 10, 25, 0, 0)
|
||||||
>>> f.to_python('10/25/06 14:30:45')
|
>>> f.clean('10/25/06 14:30:45')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
||||||
>>> f.to_python('10/25/06 14:30:00')
|
>>> f.clean('10/25/06 14:30:00')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python('10/25/06 14:30')
|
>>> f.clean('10/25/06 14:30')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python('10/25/06')
|
>>> f.clean('10/25/06')
|
||||||
datetime.datetime(2006, 10, 25, 0, 0)
|
datetime.datetime(2006, 10, 25, 0, 0)
|
||||||
>>> f.to_python('hello')
|
>>> f.clean('hello')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date/time.']
|
ValidationError: [u'Enter a valid date/time.']
|
||||||
>>> f.to_python('2006-10-25 4:30 p.m.')
|
>>> f.clean('2006-10-25 4:30 p.m.')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date/time.']
|
ValidationError: [u'Enter a valid date/time.']
|
||||||
|
|
||||||
DateField accepts an optional input_formats parameter:
|
DateField accepts an optional input_formats parameter:
|
||||||
>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p'])
|
>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p'])
|
||||||
>>> f.to_python(datetime.date(2006, 10, 25))
|
>>> f.clean(datetime.date(2006, 10, 25))
|
||||||
datetime.datetime(2006, 10, 25, 0, 0)
|
datetime.datetime(2006, 10, 25, 0, 0)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
||||||
datetime.datetime(2006, 10, 25, 14, 30, 59)
|
datetime.datetime(2006, 10, 25, 14, 30, 59)
|
||||||
>>> f.to_python(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
||||||
datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
|
datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
|
||||||
>>> f.to_python('2006 10 25 2:30 PM')
|
>>> f.clean('2006 10 25 2:30 PM')
|
||||||
datetime.datetime(2006, 10, 25, 14, 30)
|
datetime.datetime(2006, 10, 25, 14, 30)
|
||||||
|
|
||||||
The input_formats parameter overrides all default input formats,
|
The input_formats parameter overrides all default input formats,
|
||||||
so the default formats won't work unless you specify them:
|
so the default formats won't work unless you specify them:
|
||||||
>>> f.to_python('2006-10-25 14:30:45')
|
>>> f.clean('2006-10-25 14:30:45')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid date/time.']
|
ValidationError: [u'Enter a valid date/time.']
|
||||||
@ -549,51 +549,51 @@ ValidationError: [u'Enter a valid date/time.']
|
|||||||
# RegexField ##################################################################
|
# RegexField ##################################################################
|
||||||
|
|
||||||
>>> f = RegexField('^\d[A-F]\d$')
|
>>> f = RegexField('^\d[A-F]\d$')
|
||||||
>>> f.to_python('2A2')
|
>>> f.clean('2A2')
|
||||||
u'2A2'
|
u'2A2'
|
||||||
>>> f.to_python('3F3')
|
>>> f.clean('3F3')
|
||||||
u'3F3'
|
u'3F3'
|
||||||
>>> f.to_python('3G3')
|
>>> f.clean('3G3')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid value.']
|
ValidationError: [u'Enter a valid value.']
|
||||||
>>> f.to_python(' 2A2')
|
>>> f.clean(' 2A2')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid value.']
|
ValidationError: [u'Enter a valid value.']
|
||||||
>>> f.to_python('2A2 ')
|
>>> f.clean('2A2 ')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid value.']
|
ValidationError: [u'Enter a valid value.']
|
||||||
|
|
||||||
Alternatively, RegexField can take a compiled regular expression:
|
Alternatively, RegexField can take a compiled regular expression:
|
||||||
>>> f = RegexField(re.compile('^\d[A-F]\d$'))
|
>>> f = RegexField(re.compile('^\d[A-F]\d$'))
|
||||||
>>> f.to_python('2A2')
|
>>> f.clean('2A2')
|
||||||
u'2A2'
|
u'2A2'
|
||||||
>>> f.to_python('3F3')
|
>>> f.clean('3F3')
|
||||||
u'3F3'
|
u'3F3'
|
||||||
>>> f.to_python('3G3')
|
>>> f.clean('3G3')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid value.']
|
ValidationError: [u'Enter a valid value.']
|
||||||
>>> f.to_python(' 2A2')
|
>>> f.clean(' 2A2')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid value.']
|
ValidationError: [u'Enter a valid value.']
|
||||||
>>> f.to_python('2A2 ')
|
>>> f.clean('2A2 ')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid value.']
|
ValidationError: [u'Enter a valid value.']
|
||||||
|
|
||||||
RegexField takes an optional error_message argument:
|
RegexField takes an optional error_message argument:
|
||||||
>>> f = RegexField('^\d\d\d\d$', 'Enter a four-digit number.')
|
>>> f = RegexField('^\d\d\d\d$', 'Enter a four-digit number.')
|
||||||
>>> f.to_python('1234')
|
>>> f.clean('1234')
|
||||||
u'1234'
|
u'1234'
|
||||||
>>> f.to_python('123')
|
>>> f.clean('123')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a four-digit number.']
|
ValidationError: [u'Enter a four-digit number.']
|
||||||
>>> f.to_python('abcd')
|
>>> f.clean('abcd')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a four-digit number.']
|
ValidationError: [u'Enter a four-digit number.']
|
||||||
@ -601,17 +601,17 @@ ValidationError: [u'Enter a four-digit number.']
|
|||||||
# EmailField ##################################################################
|
# EmailField ##################################################################
|
||||||
|
|
||||||
>>> f = EmailField()
|
>>> f = EmailField()
|
||||||
>>> f.to_python('person@example.com')
|
>>> f.clean('person@example.com')
|
||||||
u'person@example.com'
|
u'person@example.com'
|
||||||
>>> f.to_python('foo')
|
>>> f.clean('foo')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid e-mail address.']
|
ValidationError: [u'Enter a valid e-mail address.']
|
||||||
>>> f.to_python('foo@')
|
>>> f.clean('foo@')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid e-mail address.']
|
ValidationError: [u'Enter a valid e-mail address.']
|
||||||
>>> f.to_python('foo@bar')
|
>>> f.clean('foo@bar')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid e-mail address.']
|
ValidationError: [u'Enter a valid e-mail address.']
|
||||||
@ -619,31 +619,31 @@ ValidationError: [u'Enter a valid e-mail address.']
|
|||||||
# URLField ##################################################################
|
# URLField ##################################################################
|
||||||
|
|
||||||
>>> f = URLField()
|
>>> f = URLField()
|
||||||
>>> f.to_python('http://example.com')
|
>>> f.clean('http://example.com')
|
||||||
u'http://example.com'
|
u'http://example.com'
|
||||||
>>> f.to_python('http://www.example.com')
|
>>> f.clean('http://www.example.com')
|
||||||
u'http://www.example.com'
|
u'http://www.example.com'
|
||||||
>>> f.to_python('foo')
|
>>> f.clean('foo')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid URL.']
|
ValidationError: [u'Enter a valid URL.']
|
||||||
>>> f.to_python('example.com')
|
>>> f.clean('example.com')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid URL.']
|
ValidationError: [u'Enter a valid URL.']
|
||||||
>>> f.to_python('http://')
|
>>> f.clean('http://')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid URL.']
|
ValidationError: [u'Enter a valid URL.']
|
||||||
>>> f.to_python('http://example')
|
>>> f.clean('http://example')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid URL.']
|
ValidationError: [u'Enter a valid URL.']
|
||||||
>>> f.to_python('http://example.')
|
>>> f.clean('http://example.')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid URL.']
|
ValidationError: [u'Enter a valid URL.']
|
||||||
>>> f.to_python('http://.com')
|
>>> f.clean('http://.com')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid URL.']
|
ValidationError: [u'Enter a valid URL.']
|
||||||
@ -651,17 +651,17 @@ ValidationError: [u'Enter a valid URL.']
|
|||||||
URLField takes an optional verify_exists parameter, which is False by default.
|
URLField takes an optional verify_exists parameter, which is False by default.
|
||||||
This verifies that the URL is live on the Internet and doesn't return a 404 or 500:
|
This verifies that the URL is live on the Internet and doesn't return a 404 or 500:
|
||||||
>>> f = URLField(verify_exists=True)
|
>>> f = URLField(verify_exists=True)
|
||||||
>>> f.to_python('http://www.google.com')
|
>>> f.clean('http://www.google.com') # This will fail if there's no Internet connection
|
||||||
u'http://www.google.com'
|
u'http://www.google.com'
|
||||||
>>> f.to_python('http://example')
|
>>> f.clean('http://example')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid URL.']
|
ValidationError: [u'Enter a valid URL.']
|
||||||
>>> f.to_python('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') # bad domain
|
>>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') # bad domain
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This URL appears to be a broken link.']
|
ValidationError: [u'This URL appears to be a broken link.']
|
||||||
>>> f.to_python('http://google.com/we-love-microsoft.html') # good domain, bad page
|
>>> f.clean('http://google.com/we-love-microsoft.html') # good domain, bad page
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This URL appears to be a broken link.']
|
ValidationError: [u'This URL appears to be a broken link.']
|
||||||
@ -669,41 +669,41 @@ ValidationError: [u'This URL appears to be a broken link.']
|
|||||||
# BooleanField ################################################################
|
# BooleanField ################################################################
|
||||||
|
|
||||||
>>> f = BooleanField()
|
>>> f = BooleanField()
|
||||||
>>> f.to_python(True)
|
>>> f.clean(True)
|
||||||
True
|
True
|
||||||
>>> f.to_python(False)
|
>>> f.clean(False)
|
||||||
False
|
False
|
||||||
>>> f.to_python(1)
|
>>> f.clean(1)
|
||||||
True
|
True
|
||||||
>>> f.to_python(0)
|
>>> f.clean(0)
|
||||||
False
|
False
|
||||||
>>> f.to_python('Django rocks')
|
>>> f.clean('Django rocks')
|
||||||
True
|
True
|
||||||
|
|
||||||
# ChoiceField #################################################################
|
# ChoiceField #################################################################
|
||||||
|
|
||||||
>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')])
|
>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')])
|
||||||
>>> f.to_python(1)
|
>>> f.clean(1)
|
||||||
u'1'
|
u'1'
|
||||||
>>> f.to_python('1')
|
>>> f.clean('1')
|
||||||
u'1'
|
u'1'
|
||||||
>>> f.to_python(None)
|
>>> f.clean(None)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This field is required.']
|
ValidationError: [u'This field is required.']
|
||||||
>>> f.to_python('')
|
>>> f.clean('')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This field is required.']
|
ValidationError: [u'This field is required.']
|
||||||
>>> f.to_python('3')
|
>>> f.clean('3')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Select a valid choice. 3 is not one of the available choices.']
|
ValidationError: [u'Select a valid choice. 3 is not one of the available choices.']
|
||||||
|
|
||||||
>>> f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')])
|
>>> f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')])
|
||||||
>>> f.to_python('J')
|
>>> f.clean('J')
|
||||||
u'J'
|
u'J'
|
||||||
>>> f.to_python('John')
|
>>> f.clean('John')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Select a valid choice. John is not one of the available choices.']
|
ValidationError: [u'Select a valid choice. John is not one of the available choices.']
|
||||||
@ -711,39 +711,98 @@ ValidationError: [u'Select a valid choice. John is not one of the available choi
|
|||||||
# MultipleChoiceField #########################################################
|
# MultipleChoiceField #########################################################
|
||||||
|
|
||||||
>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
|
>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
|
||||||
>>> f.to_python([1])
|
>>> f.clean([1])
|
||||||
[u'1']
|
[u'1']
|
||||||
>>> f.to_python(['1'])
|
>>> f.clean(['1'])
|
||||||
[u'1']
|
[u'1']
|
||||||
>>> f.to_python(['1', '2'])
|
>>> f.clean(['1', '2'])
|
||||||
[u'1', u'2']
|
[u'1', u'2']
|
||||||
>>> f.to_python([1, '2'])
|
>>> f.clean([1, '2'])
|
||||||
[u'1', u'2']
|
[u'1', u'2']
|
||||||
>>> f.to_python((1, '2'))
|
>>> f.clean((1, '2'))
|
||||||
[u'1', u'2']
|
[u'1', u'2']
|
||||||
>>> f.to_python('hello')
|
>>> f.clean('hello')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Enter a list of values.']
|
ValidationError: [u'Enter a list of values.']
|
||||||
>>> f.to_python([])
|
>>> f.clean([])
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This field is required.']
|
ValidationError: [u'This field is required.']
|
||||||
>>> f.to_python(())
|
>>> f.clean(())
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'This field is required.']
|
ValidationError: [u'This field is required.']
|
||||||
>>> f.to_python(['3'])
|
>>> f.clean(['3'])
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Select a valid choice. 3 is not one of the available choices.']
|
ValidationError: [u'Select a valid choice. 3 is not one of the available choices.']
|
||||||
|
|
||||||
|
# ComboField ##################################################################
|
||||||
|
|
||||||
|
ComboField takes a list of fields that should be used to validate a value,
|
||||||
|
in that order:
|
||||||
|
>>> f = ComboField(fields=[CharField(max_length=20), EmailField()])
|
||||||
|
>>> f.clean('test@example.com')
|
||||||
|
u'test@example.com'
|
||||||
|
>>> f.clean('longemailaddress@example.com')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Ensure this value has at most 20 characters.']
|
||||||
|
>>> f.clean('not an e-mail')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid e-mail address.']
|
||||||
|
>>> f.clean('')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
|
||||||
# Form ########################################################################
|
# Form ########################################################################
|
||||||
|
|
||||||
>>> class Person(Form):
|
>>> class Person(Form):
|
||||||
... first_name = CharField()
|
... first_name = CharField()
|
||||||
... last_name = CharField()
|
... last_name = CharField()
|
||||||
... birthday = DateField()
|
... birthday = DateField()
|
||||||
|
>>> p = Person()
|
||||||
|
>>> print p
|
||||||
|
<table>
|
||||||
|
<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
|
||||||
|
<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
|
||||||
|
<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
|
||||||
|
</table>
|
||||||
|
>>> print p.as_table()
|
||||||
|
<table>
|
||||||
|
<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
|
||||||
|
<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
|
||||||
|
<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
|
||||||
|
</table>
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<ul>
|
||||||
|
<li>First name: <input type="text" name="first_name" /></li>
|
||||||
|
<li>Last name: <input type="text" name="last_name" /></li>
|
||||||
|
<li>Birthday: <input type="text" name="birthday" /></li>
|
||||||
|
</ul>
|
||||||
|
>>> print p.as_table_with_errors()
|
||||||
|
<table>
|
||||||
|
<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
|
||||||
|
<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
|
||||||
|
<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
|
||||||
|
<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
|
||||||
|
<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
|
||||||
|
<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
|
||||||
|
</table>
|
||||||
|
>>> print p.as_ul_with_errors()
|
||||||
|
<ul>
|
||||||
|
<li><ul><li>This field is required.</li></ul>First name: <input type="text" name="first_name" /></li>
|
||||||
|
<li><ul><li>This field is required.</li></ul>Last name: <input type="text" name="last_name" /></li>
|
||||||
|
<li><ul><li>This field is required.</li></ul>Birthday: <input type="text" name="birthday" /></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
|
>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
|
||||||
>>> p.errors()
|
>>> p.errors()
|
||||||
{}
|
{}
|
||||||
@ -753,7 +812,7 @@ True
|
|||||||
u''
|
u''
|
||||||
>>> p.errors().as_text()
|
>>> p.errors().as_text()
|
||||||
u''
|
u''
|
||||||
>>> p.to_python()
|
>>> p.clean()
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
>>> print p['first_name']
|
>>> print p['first_name']
|
||||||
<input type="text" name="first_name" value="John" />
|
<input type="text" name="first_name" value="John" />
|
||||||
@ -766,6 +825,12 @@ u''
|
|||||||
<input type="text" name="first_name" value="John" />
|
<input type="text" name="first_name" value="John" />
|
||||||
<input type="text" name="last_name" value="Lennon" />
|
<input type="text" name="last_name" value="Lennon" />
|
||||||
<input type="text" name="birthday" value="1940-10-9" />
|
<input type="text" name="birthday" value="1940-10-9" />
|
||||||
|
>>> print p
|
||||||
|
<table>
|
||||||
|
<tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr>
|
||||||
|
<tr><td>Last name:</td><td><input type="text" name="last_name" value="Lennon" /></td></tr>
|
||||||
|
<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
>>> p = Person({'last_name': u'Lennon'})
|
>>> p = Person({'last_name': u'Lennon'})
|
||||||
>>> p.errors()
|
>>> p.errors()
|
||||||
@ -779,8 +844,8 @@ u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is re
|
|||||||
* This field is required.
|
* This field is required.
|
||||||
* birthday
|
* birthday
|
||||||
* This field is required.
|
* This field is required.
|
||||||
>>> p.to_python()
|
>>> p.clean()
|
||||||
>>> repr(p.to_python())
|
>>> repr(p.clean())
|
||||||
'None'
|
'None'
|
||||||
>>> p['first_name'].errors
|
>>> p['first_name'].errors
|
||||||
[u'This field is required.']
|
[u'This field is required.']
|
||||||
@ -887,6 +952,84 @@ MultipleChoiceField is a special case, as its data is required to be a list:
|
|||||||
<option value="J">John Lennon</option>
|
<option value="J">John Lennon</option>
|
||||||
<option value="P" selected="selected">Paul McCartney</option>
|
<option value="P" selected="selected">Paul McCartney</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
There are a couple of ways to do multiple-field validation. If you want the
|
||||||
|
validation message to be associated with a particular field, implement the
|
||||||
|
clean_XXX() method on the Form, where XXX is the field name. As in
|
||||||
|
Field.clean(), the clean_XXX() method should return the cleaned value:
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10)
|
||||||
|
... password1 = CharField(widget=PasswordInput)
|
||||||
|
... password2 = CharField(widget=PasswordInput)
|
||||||
|
... def clean_password2(self):
|
||||||
|
... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
|
||||||
|
... raise ValidationError(u'Please make sure your passwords match.')
|
||||||
|
... return self.clean_data['password2']
|
||||||
|
>>> f = UserRegistration()
|
||||||
|
>>> f.errors()
|
||||||
|
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
|
||||||
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'})
|
||||||
|
>>> f.errors()
|
||||||
|
{'password2': [u'Please make sure your passwords match.']}
|
||||||
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
|
||||||
|
>>> f.errors()
|
||||||
|
{}
|
||||||
|
>>> f.clean()
|
||||||
|
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
||||||
|
|
||||||
|
Another way of doing multiple-field validation is by implementing the
|
||||||
|
Form's clean() method. If you do this, any ValidationError raised by that
|
||||||
|
method will not be associated with a particular field; it will have a
|
||||||
|
special-case association with the field named '__all__'. Note that
|
||||||
|
Form.clean() still needs to return a dictionary of all clean data:
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10)
|
||||||
|
... password1 = CharField(widget=PasswordInput)
|
||||||
|
... password2 = CharField(widget=PasswordInput)
|
||||||
|
... def clean(self):
|
||||||
|
... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
|
||||||
|
... raise ValidationError(u'Please make sure your passwords match.')
|
||||||
|
... return self.clean_data
|
||||||
|
>>> f = UserRegistration()
|
||||||
|
>>> print f.as_table()
|
||||||
|
<table>
|
||||||
|
<tr><td>Username:</td><td><input type="text" name="username" /></td></tr>
|
||||||
|
<tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
|
||||||
|
<tr><td>Password2:</td><td><input type="password" name="password2" /></td></tr>
|
||||||
|
</table>
|
||||||
|
>>> f.errors()
|
||||||
|
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
|
||||||
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'})
|
||||||
|
>>> f.errors()
|
||||||
|
{'__all__': [u'Please make sure your passwords match.']}
|
||||||
|
>>> print f.as_table()
|
||||||
|
<table>
|
||||||
|
<tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
|
||||||
|
<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
|
||||||
|
<tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
|
||||||
|
</table>
|
||||||
|
>>> print f.as_table_with_errors()
|
||||||
|
<table>
|
||||||
|
<tr><td colspan="2"><ul><li>Please make sure your passwords match.</li></ul></td></tr>
|
||||||
|
<tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
|
||||||
|
<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
|
||||||
|
<tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
|
||||||
|
</table>
|
||||||
|
>>> print f.as_ul_with_errors()
|
||||||
|
<ul>
|
||||||
|
<li><ul><li>Please make sure your passwords match.</li></ul></li>
|
||||||
|
<li>Username: <input type="text" name="username" value="adrian" /></li>
|
||||||
|
<li>Password1: <input type="password" name="password1" value="foo" /></li>
|
||||||
|
<li>Password2: <input type="password" name="password2" value="bar" /></li>
|
||||||
|
</ul>
|
||||||
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
|
||||||
|
>>> f.errors()
|
||||||
|
{}
|
||||||
|
>>> f.clean()
|
||||||
|
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -329,6 +329,21 @@ class Templates(unittest.TestCase):
|
|||||||
'ifchanged06': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2)}, '1222'),
|
'ifchanged06': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2)}, '1222'),
|
||||||
'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'),
|
'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'),
|
||||||
|
|
||||||
|
# Test one parameter given to ifchanged.
|
||||||
|
'ifchanged-param01': ('{% for n in num %}{% ifchanged n %}..{% endifchanged %}{{ n }}{% endfor %}', { 'num': (1,2,3) }, '..1..2..3'),
|
||||||
|
'ifchanged-param02': ('{% for n in num %}{% for x in numx %}{% ifchanged n %}..{% endifchanged %}{{ x }}{% endfor %}{% endfor %}', { 'num': (1,2,3), 'numx': (5,6,7) }, '..567..567..567'),
|
||||||
|
|
||||||
|
# Test multiple parameters to ifchanged.
|
||||||
|
'ifchanged-param03': ('{% for n in num %}{{ n }}{% for x in numx %}{% ifchanged x n %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1,1,2), 'numx': (5,6,6) }, '156156256'),
|
||||||
|
|
||||||
|
# Test a date+hour like construct, where the hour of the last day
|
||||||
|
# is the same but the date had changed, so print the hour anyway.
|
||||||
|
'ifchanged-param04': ('{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),
|
||||||
|
|
||||||
|
# Logically the same as above, just written with explicit
|
||||||
|
# ifchanged for the day.
|
||||||
|
'ifchanged-param04': ('{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),
|
||||||
|
|
||||||
### IFEQUAL TAG ###########################################################
|
### IFEQUAL TAG ###########################################################
|
||||||
'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
|
'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
|
||||||
'ifequal02': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 1}, "yes"),
|
'ifequal02': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 1}, "yes"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user