1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

[multi-db] Merged trunk to [4188]. Some tests still failing.

git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@4189 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jason Pellerin 2006-12-10 15:43:51 +00:00
parent 55d7f6519c
commit 694f44f600
19 changed files with 600 additions and 156 deletions

View File

@ -7,7 +7,7 @@
<input type="text" size="40" name="{{ search_var }}" value="{{ cl.query|escape }}" id="searchbar" /> <input type="text" size="40" name="{{ search_var }}" value="{{ cl.query|escape }}" id="searchbar" />
<input type="submit" value="{% trans 'Go' %}" /> <input type="submit" value="{% trans 'Go' %}" />
{% if show_result_count %} {% if show_result_count %}
<span class="small quiet">{% blocktrans count cl.result_count as counter %}1 result{% plural %}{{ counter }} results{% endblocktrans %} (<a href="?">{% blocktrans with cl.full_result_count as full_result_count %}{{ full_result_count }} total{% endblocktrans %}</a>)</span> <span class="small quiet">{% blocktrans count cl.result_count as counter %}1 result{% plural %}{{ counter }} results{% endblocktrans %} (<a href="?{% if cl.is_popup %}pop=1{% endif %}">{% blocktrans with cl.full_result_count as full_result_count %}{{ full_result_count }} total{% endblocktrans %}</a>)</span>
{% endif %} {% endif %}
{% for pair in cl.params.items %} {% for pair in cl.params.items %}
{% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0|escape }}" value="{{ pair.1|escape }}"/>{% endifnotequal %} {% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0|escape }}" value="{{ pair.1|escape }}"/>{% endifnotequal %}

View File

@ -226,7 +226,7 @@ index = staff_member_required(never_cache(index))
def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None): def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
@ -302,7 +302,7 @@ def change_stage(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
object_id = unquote(object_id) object_id = unquote(object_id)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
@ -313,8 +313,8 @@ def change_stage(request, app_label, model_name, object_id):
try: try:
manipulator = model.ChangeManipulator(object_id) manipulator = model.ChangeManipulator(object_id)
except ObjectDoesNotExist: except model.DoesNotExist:
raise Http404 raise Http404('%s object with primary key %r does not exist' % (model_name, escape(object_id)))
if request.POST: if request.POST:
new_data = request.POST.copy() new_data = request.POST.copy()
@ -490,7 +490,7 @@ def delete_stage(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
object_id = unquote(object_id) object_id = unquote(object_id)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
opts = model._meta opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()): if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
raise PermissionDenied raise PermissionDenied
@ -527,7 +527,7 @@ def history(request, app_label, model_name, object_id):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
object_id = unquote(object_id) object_id = unquote(object_id)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
action_list = LogEntry.objects.filter(object_id=object_id, action_list = LogEntry.objects.filter(object_id=object_id,
content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time') content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time')
# If no history was found, see whether this object even exists. # If no history was found, see whether this object even exists.
@ -743,7 +743,7 @@ class ChangeList(object):
def change_list(request, app_label, model_name): def change_list(request, app_label, model_name):
model = models.get_model(app_label, model_name) model = models.get_model(app_label, model_name)
if model is None: if model is None:
raise Http404, "App %r, model %r, not found" % (app_label, model_name) raise Http404("App %r, model %r, not found" % (app_label, model_name))
if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()): if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
raise PermissionDenied raise PermissionDenied
try: try:

View File

@ -3,9 +3,9 @@ Creates content types for all installed models.
""" """
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.db.models import get_models, signals from django.db.models import get_apps, get_models, signals
def create_contenttypes(app, created_models, verbosity): def create_contenttypes(app, created_models, verbosity=2):
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
app_models = get_models(app) app_models = get_models(app)
if not app_models: if not app_models:
@ -22,4 +22,11 @@ def create_contenttypes(app, created_models, verbosity):
if verbosity >= 2: if verbosity >= 2:
print "Adding content type '%s | %s'" % (ct.app_label, ct.model) print "Adding content type '%s | %s'" % (ct.app_label, ct.model)
def create_all_contenttypes(verbosity=2):
for app in get_apps():
create_contenttypes(app, None, verbosity)
dispatcher.connect(create_contenttypes, signal=signals.post_syncdb) dispatcher.connect(create_contenttypes, signal=signals.post_syncdb)
if __name__ == "__main__":
create_all_contenttypes()

View File

View File

@ -0,0 +1,160 @@
"""
Formtools Preview application.
This is an abstraction of the following workflow:
"Display an HTML form, force a preview, then do something with the submission."
Given a django.newforms.Form object that you define, this takes care of the
following:
* Displays the form as HTML on a Web page.
* Validates the form data once it's submitted via POST.
* If it's valid, displays a preview page.
* If it's not valid, redisplays the form with error messages.
* At the preview page, if the preview confirmation button is pressed, calls
a hook that you define -- a done() method.
The framework enforces the required preview by passing a shared-secret hash to
the preview page. If somebody tweaks the form parameters on the preview page,
the form submission will fail the hash comparison test.
Usage
=====
Subclass FormPreview and define a done() method:
def done(self, request, clean_data):
# ...
This method takes an HttpRequest object and a dictionary of the form data after
it has been validated and cleaned. It should return an HttpResponseRedirect.
Then, just instantiate your FormPreview subclass by passing it a Form class,
and pass that to your URLconf, like so:
(r'^post/$', MyFormPreview(MyForm)),
The FormPreview class has a few other hooks. See the docstrings in the source
code below.
The framework also uses two templates: 'formtools/preview.html' and
'formtools/form.html'. You can override these by setting 'preview_template' and
'form_template' attributes on your FormPreview subclass. See
django/contrib/formtools/templates for the default templates.
"""
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.shortcuts import render_to_response
import cPickle as pickle
import md5
AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
class FormPreview(object):
preview_template = 'formtools/preview.html'
form_template = 'formtools/form.html'
# METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
def __init__(self, form):
# form should be a Form class, not an instance.
self.form, self.state = form, {}
def __call__(self, request, *args, **kwargs):
stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview')
self.parse_params(*args, **kwargs)
try:
method = getattr(self, stage + '_' + request.method.lower())
except AttributeError:
raise Http404
return method(request)
def unused_name(self, name):
"""
Given a first-choice name, adds an underscore to the name until it
reaches a name that isn't claimed by any field in the form.
This is calculated rather than being hard-coded so that no field names
are off-limits for use in the form.
"""
while 1:
try:
f = self.form.fields[name]
except KeyError:
break # This field name isn't being used by the form.
name += '_'
return name
def preview_get(self, request):
"Displays the form"
f = self.form(auto_id=AUTO_ID)
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
def preview_post(self, request):
"Validates the POST data. If valid, displays the preview page. Else, redisplays form."
f = self.form(request.POST, auto_id=AUTO_ID)
context = {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}
if f.is_valid():
context['hash_field'] = self.unused_name('hash')
context['hash_value'] = self.security_hash(request, f)
return render_to_response(self.preview_template, context)
else:
return render_to_response(self.form_template, context)
def post_post(self, request):
"Validates the POST data. If valid, calls done(). Else, redisplays form."
f = self.form(request.POST, auto_id=AUTO_ID)
if f.is_valid():
if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')):
return self.failed_hash(request) # Security hash failed.
return self.done(request, f.clean_data)
else:
return render_to_response(self.form_template, {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state})
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
def parse_params(self, *args, **kwargs):
"""
Given captured args and kwargs from the URLconf, saves something in
self.state and/or raises Http404 if necessary.
For example, this URLconf captures a user_id variable:
(r'^contact/(?P<user_id>\d{1,6})/$', MyFormPreview(MyForm)),
In this case, the kwargs variable in parse_params would be
{'user_id': 32} for a request to '/contact/32/'. You can use that
user_id to make sure it's a valid user and/or save it for later, for
use in done().
"""
pass
def security_hash(self, request, form):
"""
Calculates the security hash for the given Form instance.
This creates a list of the form field names/values in a deterministic
order, pickles the result with the SECRET_KEY setting and takes an md5
hash of that.
Subclasses may want to take into account request-specific information
such as the IP address.
"""
data = [(bf.name, bf.data) for bf in form] + [settings.SECRET_KEY]
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires
# Python 2.3, but Django requires 2.3 anyway, so that's OK.
pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
return md5.new(pickled).hexdigest()
def failed_hash(self, request):
"Returns an HttpResponse in the case of an invalid security hash."
return self.preview_post(request)
# METHODS SUBCLASSES MUST OVERRIDE ########################################
def done(self, request, clean_data):
"Does something with the clean_data and returns an HttpResponseRedirect."
raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)

View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block content %}
{% if form.errors %}<h1>Please correct the following errors</h1>{% else %}<h1>Submit</h1>{% endif %}
<form action="" method="post">
<table>
{{ form }}
</table>
<input type="hidden" name="{{ stage_field }}" value="1" />
<p><input type="submit" value="Submit" /></p>
</form>
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block content %}
<h1>Preview your submission</h1>
<table>
{% for field in form %}
<tr>
<th>{{ field.verbose_name }}:</th>
<td>{{ field.data|escape }}</td>
</tr>
{% endfor %}
</table>
<p>Security hash: {{ hash_value }}</p>
<form action="" method="post">
{% for field in form %}{{ field.as_hidden }}
{% endfor %}
<input type="hidden" name="{{ stage_field }}" value="2" />
<input type="hidden" name="{{ hash_field }}" value="{{ hash_value }}" />
<p><input type="submit" value="Submit" /></p>
</form>
<h1>Or edit it again</h1>
<form action="" method="post">
<table>
{{ form }}
</table>
<input type="hidden" name="{{ stage_field }}" value="1" />
<p><input type="submit" value="Submit changes" /></p>
</form>
{% endblock %}

View File

@ -29,7 +29,7 @@ def ping_google(sitemap_url=None, ping_url=PING_URL):
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
current_site = Site.objects.get_current() current_site = Site.objects.get_current()
url = "%s%s" % (current_site.domain, sitemap) url = "%s%s" % (current_site.domain, sitemap_url)
params = urllib.urlencode({'sitemap':url}) params = urllib.urlencode({'sitemap':url})
urllib.urlopen("%s?%s" % (ping_url, params)) urllib.urlopen("%s?%s" % (ping_url, params))

View File

@ -118,6 +118,8 @@ def runfastcgi(argset=[], **kwargs):
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.")
wsgi_opts['debug'] = False # Turn off flup tracebacks
# Prep up and go # Prep up and go
from django.core.handlers.wsgi import WSGIHandler from django.core.handlers.wsgi import WSGIHandler

View File

@ -2,8 +2,9 @@
Field classes Field classes
""" """
from util import ValidationError, DEFAULT_ENCODING, smart_unicode from django.utils.translation import gettext
from widgets import TextInput, CheckboxInput, Select, SelectMultiple from util import ValidationError, smart_unicode
from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
import datetime import datetime
import re import re
import time import time
@ -31,11 +32,17 @@ class Field(object):
# Tracks each time a Field instance is created. Used to retain order. # Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0 creation_counter = 0
def __init__(self, required=True, widget=None): def __init__(self, required=True, widget=None, label=None):
self.required = required self.required, self.label = required, label
widget = widget or self.widget widget = widget or self.widget
if isinstance(widget, type): if isinstance(widget, type):
widget = widget() widget = widget()
# Hook into self.widget_attrs() for any Field-specific HTML attributes.
extra_attrs = self.widget_attrs(widget)
if extra_attrs:
widget.attrs.update(extra_attrs)
self.widget = widget self.widget = widget
# Increase the creation counter, and save our local copy. # Increase the creation counter, and save our local copy.
@ -50,13 +57,21 @@ class Field(object):
Raises ValidationError for any errors. Raises ValidationError for any errors.
""" """
if self.required and value in EMPTY_VALUES: if self.required and value in EMPTY_VALUES:
raise ValidationError(u'This field is required.') raise ValidationError(gettext(u'This field is required.'))
return value return value
def widget_attrs(self, widget):
"""
Given a Widget instance (*not* a Widget class), returns a dictionary of
any HTML attributes that should be added to the Widget, based on this
Field.
"""
return {}
class CharField(Field): class CharField(Field):
def __init__(self, max_length=None, min_length=None, required=True, widget=None): def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None):
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
Field.__init__(self, required, widget, label)
def clean(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."
@ -64,11 +79,15 @@ class CharField(Field):
if value in EMPTY_VALUES: value = u'' if value in EMPTY_VALUES: value = u''
value = smart_unicode(value) value = smart_unicode(value)
if self.max_length is not None and len(value) > self.max_length: if self.max_length is not None and len(value) > self.max_length:
raise ValidationError(u'Ensure this value has at most %d characters.' % self.max_length) raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
if self.min_length is not None and len(value) < self.min_length: if self.min_length is not None and len(value) < self.min_length:
raise ValidationError(u'Ensure this value has at least %d characters.' % self.min_length) raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
return value return value
def widget_attrs(self, widget):
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
return {'maxlength': str(self.max_length)}
class IntegerField(Field): class IntegerField(Field):
def clean(self, value): def clean(self, value):
""" """
@ -81,7 +100,7 @@ class IntegerField(Field):
try: try:
return int(value) return int(value)
except (ValueError, TypeError): except (ValueError, TypeError):
raise ValidationError(u'Enter a whole number.') raise ValidationError(gettext(u'Enter a whole number.'))
DEFAULT_DATE_INPUT_FORMATS = ( DEFAULT_DATE_INPUT_FORMATS = (
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
@ -92,8 +111,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
) )
class DateField(Field): class DateField(Field):
def __init__(self, input_formats=None, required=True, widget=None): def __init__(self, input_formats=None, required=True, widget=None, label=None):
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
def clean(self, value): def clean(self, value):
@ -113,7 +132,7 @@ class DateField(Field):
return datetime.date(*time.strptime(value, format)[:3]) return datetime.date(*time.strptime(value, format)[:3])
except ValueError: except ValueError:
continue continue
raise ValidationError(u'Enter a valid date.') raise ValidationError(gettext(u'Enter a valid date.'))
DEFAULT_DATETIME_INPUT_FORMATS = ( DEFAULT_DATETIME_INPUT_FORMATS = (
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
@ -128,8 +147,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
) )
class DateTimeField(Field): class DateTimeField(Field):
def __init__(self, input_formats=None, required=True, widget=None): def __init__(self, input_formats=None, required=True, widget=None, label=None):
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
def clean(self, value): def clean(self, value):
@ -149,20 +168,20 @@ class DateTimeField(Field):
return datetime.datetime(*time.strptime(value, format)[:6]) return datetime.datetime(*time.strptime(value, format)[:6])
except ValueError: except ValueError:
continue continue
raise ValidationError(u'Enter a valid date/time.') raise ValidationError(gettext(u'Enter a valid date/time.'))
class RegexField(Field): class RegexField(Field):
def __init__(self, regex, error_message=None, required=True, widget=None): def __init__(self, regex, error_message=None, required=True, widget=None, label=None):
""" """
regex can be either a string or a compiled regular expression object. regex can be either a string or a compiled regular expression object.
error_message is an optional error message to use, if error_message is an optional error message to use, if
'Enter a valid value' is too generic for you. 'Enter a valid value' is too generic for you.
""" """
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
if isinstance(regex, basestring): if isinstance(regex, basestring):
regex = re.compile(regex) regex = re.compile(regex)
self.regex = regex self.regex = regex
self.error_message = error_message or u'Enter a valid value.' self.error_message = error_message or gettext(u'Enter a valid value.')
def clean(self, value): def clean(self, value):
""" """
@ -184,8 +203,8 @@ email_re = re.compile(
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
class EmailField(RegexField): class EmailField(RegexField):
def __init__(self, required=True, widget=None): def __init__(self, required=True, widget=None, label=None):
RegexField.__init__(self, email_re, u'Enter a valid e-mail address.', required, widget) RegexField.__init__(self, email_re, gettext(u'Enter a valid e-mail address.'), required, widget, label)
url_re = re.compile( url_re = re.compile(
r'^https?://' # http:// or https:// r'^https?://' # http:// or https://
@ -201,9 +220,9 @@ except ImportError:
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' 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, label=None,
validator_user_agent=URL_VALIDATOR_USER_AGENT): validator_user_agent=URL_VALIDATOR_USER_AGENT):
RegexField.__init__(self, url_re, u'Enter a valid URL.', required, widget) RegexField.__init__(self, url_re, gettext(u'Enter a valid URL.'), required, widget, label)
self.verify_exists = verify_exists self.verify_exists = verify_exists
self.user_agent = validator_user_agent self.user_agent = validator_user_agent
@ -223,9 +242,9 @@ class URLField(RegexField):
req = urllib2.Request(value, None, headers) req = urllib2.Request(value, None, headers)
u = urllib2.urlopen(req) u = urllib2.urlopen(req)
except ValueError: except ValueError:
raise ValidationError(u'Enter a valid URL.') raise ValidationError(gettext(u'Enter a valid URL.'))
except: # urllib2.URLError, httplib.InvalidURL, etc. except: # urllib2.URLError, httplib.InvalidURL, etc.
raise ValidationError(u'This URL appears to be a broken link.') raise ValidationError(gettext(u'This URL appears to be a broken link.'))
return value return value
class BooleanField(Field): class BooleanField(Field):
@ -237,10 +256,10 @@ class BooleanField(Field):
return bool(value) return bool(value)
class ChoiceField(Field): class ChoiceField(Field):
def __init__(self, choices=(), required=True, widget=Select): def __init__(self, choices=(), required=True, widget=Select, label=None):
if isinstance(widget, type): if isinstance(widget, type):
widget = widget(choices=choices) widget = widget(choices=choices)
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
self.choices = choices self.choices = choices
def clean(self, value): def clean(self, value):
@ -254,37 +273,37 @@ class ChoiceField(Field):
return value return value
valid_values = set([str(k) for k, v in self.choices]) valid_values = set([str(k) for k, v in self.choices])
if value not in valid_values: if value not in valid_values:
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value) raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % value)
return value return value
class MultipleChoiceField(ChoiceField): class MultipleChoiceField(ChoiceField):
def __init__(self, choices=(), required=True, widget=SelectMultiple): def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None):
ChoiceField.__init__(self, choices, required, widget) ChoiceField.__init__(self, choices, required, widget, label)
def clean(self, value): def clean(self, value):
""" """
Validates that the input is a list or tuple. Validates that the input is a list or tuple.
""" """
if self.required and not value: if self.required and not value:
raise ValidationError(u'This field is required.') raise ValidationError(gettext(u'This field is required.'))
elif not self.required and not value: elif not self.required and not value:
return [] return []
if not isinstance(value, (list, tuple)): if not isinstance(value, (list, tuple)):
raise ValidationError(u'Enter a list of values.') raise ValidationError(gettext(u'Enter a list of values.'))
new_value = [] new_value = []
for val in value: for val in value:
val = smart_unicode(val) val = smart_unicode(val)
new_value.append(val) new_value.append(val)
# Validate that each value in the value list is in self.choices. # Validate that each value in the value list is in self.choices.
valid_values = set([k for k, v in self.choices]) valid_values = set([smart_unicode(k) for k, v in self.choices])
for val in new_value: for val in new_value:
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(gettext(u'Select a valid choice. %s is not one of the available choices.') % val)
return new_value return new_value
class ComboField(Field): class ComboField(Field):
def __init__(self, fields=(), required=True, widget=None): def __init__(self, fields=(), required=True, widget=None, label=None):
Field.__init__(self, required, widget) Field.__init__(self, required, widget, label)
# Set 'required' to False on the individual fields, because the # Set 'required' to False on the individual fields, because the
# required validation will be handled by ComboField, not by those # required validation will be handled by ComboField, not by those
# individual fields. # individual fields.

View File

@ -6,7 +6,7 @@ from django.utils.datastructures import SortedDict
from django.utils.html import escape from django.utils.html import escape
from fields import Field from fields import Field
from widgets import TextInput, Textarea, HiddenInput from widgets import TextInput, Textarea, HiddenInput
from util import ErrorDict, ErrorList, ValidationError from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
NON_FIELD_ERRORS = '__all__' NON_FIELD_ERRORS = '__all__'
@ -32,7 +32,7 @@ class DeclarativeFieldsMetaclass(type):
attrs['fields'] = SortedDictFromList(fields) attrs['fields'] = SortedDictFromList(fields)
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
class Form(object): class Form(StrAndUnicode):
"A collection of Fields, plus their associated data." "A collection of Fields, plus their associated data."
__metaclass__ = DeclarativeFieldsMetaclass __metaclass__ = DeclarativeFieldsMetaclass
@ -43,7 +43,7 @@ class Form(object):
self.clean_data = None # Stores the data after clean() has been called. self.clean_data = None # Stores the data after clean() has been called.
self.__errors = None # Stores the errors after clean() has been called. self.__errors = None # Stores the errors after clean() has been called.
def __str__(self): def __unicode__(self):
return self.as_table() return self.as_table()
def __iter__(self): def __iter__(self):
@ -72,41 +72,44 @@ class Form(object):
""" """
return not self.ignore_errors and not bool(self.errors) return not self.ignore_errors and not bool(self.errors)
def as_table(self): def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>." "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
output = [] top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
if self.errors.get(NON_FIELD_ERRORS): output, hidden_fields = [], []
# Errors not corresponding to a particular field are displayed at the top.
output.append(u'<tr><td colspan="2">%s</td></tr>' % self.non_field_errors())
for name, field in self.fields.items(): for name, field in self.fields.items():
bf = BoundField(self, field, name) bf = BoundField(self, field, name)
bf_errors = bf.errors # Cache in local variable.
if bf.is_hidden: if bf.is_hidden:
if bf.errors: if bf_errors:
new_errors = ErrorList(['(Hidden field %s) %s' % (name, e) for e in bf.errors]) top_errors.extend(['(Hidden field %s) %s' % (name, e) for e in bf_errors])
output.append(u'<tr><td colspan="2">%s</td></tr>' % new_errors) hidden_fields.append(unicode(bf))
output.append(str(bf))
else: else:
if bf.errors: if errors_on_separate_row and bf_errors:
output.append(u'<tr><td colspan="2">%s</td></tr>' % bf.errors) output.append(error_row % bf_errors)
output.append(u'<tr><td>%s</td><td>%s</td></tr>' % (bf.label_tag(escape(bf.verbose_name+':')), bf)) output.append(normal_row % {'errors': bf_errors, 'label': bf.label_tag(escape(bf.label+':')), 'field': bf})
if top_errors:
output.insert(0, error_row % top_errors)
if hidden_fields: # Insert any hidden fields in the last row.
str_hidden = u''.join(hidden_fields)
if output:
last_row = output[-1]
# Chop off the trailing row_ender (e.g. '</td></tr>') and insert the hidden fields.
output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
else: # If there aren't any rows in the output, just append the hidden fields.
output.append(str_hidden)
return u'\n'.join(output) return u'\n'.join(output)
def as_table(self):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
return self._html_output(u'<tr><td>%(label)s</td><td>%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', True)
def as_ul(self): def as_ul(self):
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
output = [] return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False)
if self.errors.get(NON_FIELD_ERRORS):
# Errors not corresponding to a particular field are displayed at the top. def as_p(self):
output.append(u'<li>%s</li>' % self.non_field_errors()) "Returns this form rendered as HTML <p>s."
for name, field in self.fields.items(): return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True)
bf = BoundField(self, field, name)
if bf.is_hidden:
if bf.errors:
new_errors = ErrorList(['(Hidden field %s) %s' % (name, e) for e in bf.errors])
output.append(u'<li>%s</li>' % new_errors)
output.append(str(bf))
else:
output.append(u'<li>%s%s %s</li>' % (bf.errors, bf.label_tag(escape(bf.verbose_name+':')), bf))
return u'\n'.join(output)
def non_field_errors(self): def non_field_errors(self):
""" """
@ -155,18 +158,19 @@ class Form(object):
""" """
return self.clean_data return self.clean_data
class BoundField(object): class BoundField(StrAndUnicode):
"A Field plus data" "A Field plus data"
def __init__(self, form, field, name): def __init__(self, form, field, name):
self._form = form self.form = form
self._field = field self.field = field
self._name = name self.name = name
self.label = self.field.label or pretty_name(name)
def __str__(self): def __unicode__(self):
"Renders this field as an HTML widget." "Renders this field as an HTML widget."
# Use the 'widget' attribute on the field to determine which type # Use the 'widget' attribute on the field to determine which type
# of HTML widget to use. # of HTML widget to use.
value = self.as_widget(self._field.widget) value = self.as_widget(self.field.widget)
if not isinstance(value, basestring): if not isinstance(value, basestring):
# Some Widget render() methods -- notably RadioSelect -- return a # Some Widget render() methods -- notably RadioSelect -- return a
# "special" object rather than a string. Call the __str__() on that # "special" object rather than a string. Call the __str__() on that
@ -179,10 +183,7 @@ class BoundField(object):
Returns an ErrorList for this field. Returns an empty ErrorList Returns an ErrorList for this field. Returns an empty ErrorList
if there are none. if there are none.
""" """
try: return self.form.errors.get(self.name, ErrorList())
return self._form.errors[self._name]
except KeyError:
return ErrorList()
errors = property(_errors) errors = property(_errors)
def as_widget(self, widget, attrs=None): def as_widget(self, widget, attrs=None):
@ -190,7 +191,7 @@ class BoundField(object):
auto_id = self.auto_id auto_id = self.auto_id
if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'): if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('id'):
attrs['id'] = auto_id attrs['id'] = auto_id
return widget.render(self._name, self.data, attrs=attrs) return widget.render(self.name, self.data, attrs=attrs)
def as_text(self, attrs=None): def as_text(self, attrs=None):
""" """
@ -210,21 +211,17 @@ class BoundField(object):
def _data(self): def _data(self):
"Returns the data for this BoundField, or None if it wasn't given." "Returns the data for this BoundField, or None if it wasn't given."
return self._form.data.get(self._name, None) return self.form.data.get(self.name, None)
data = property(_data) data = property(_data)
def _verbose_name(self):
return pretty_name(self._name)
verbose_name = property(_verbose_name)
def label_tag(self, contents=None): def label_tag(self, contents=None):
""" """
Wraps the given contents in a <label>, if the field has an ID attribute. Wraps the given contents in a <label>, if the field has an ID attribute.
Does not HTML-escape the contents. If contents aren't given, uses the Does not HTML-escape the contents. If contents aren't given, uses the
field's HTML-escaped verbose_name. field's HTML-escaped label.
""" """
contents = contents or escape(self.verbose_name) contents = contents or escape(self.label)
widget = self._field.widget widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id id_ = widget.attrs.get('id') or self.auto_id
if id_: if id_:
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents) contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
@ -232,7 +229,7 @@ class BoundField(object):
def _is_hidden(self): def _is_hidden(self):
"Returns True if this BoundField's widget is hidden." "Returns True if this BoundField's widget is hidden."
return self._field.widget.is_hidden return self.field.widget.is_hidden
is_hidden = property(_is_hidden) is_hidden = property(_is_hidden)
def _auto_id(self): def _auto_id(self):
@ -240,10 +237,10 @@ class BoundField(object):
Calculates and returns the ID attribute for this BoundField, if the Calculates and returns the ID attribute for this BoundField, if the
associated Form has specified auto_id. Returns an empty string otherwise. associated Form has specified auto_id. Returns an empty string otherwise.
""" """
auto_id = self._form.auto_id auto_id = self.form.auto_id
if auto_id and '%s' in str(auto_id): if auto_id and '%s' in str(auto_id):
return str(auto_id) % self._name return str(auto_id) % self.name
elif auto_id: elif auto_id:
return self._name return self.name
return '' return ''
auto_id = property(_auto_id) auto_id = property(_auto_id)

View File

@ -1,13 +1,22 @@
# Default encoding for input byte strings. from django.conf import settings
DEFAULT_ENCODING = 'utf-8' # TODO: First look at django.conf.settings, then fall back to this.
def smart_unicode(s): def smart_unicode(s):
if not isinstance(s, basestring): if not isinstance(s, basestring):
s = unicode(str(s)) s = unicode(str(s))
elif not isinstance(s, unicode): elif not isinstance(s, unicode):
s = unicode(s, DEFAULT_ENCODING) s = unicode(s, settings.DEFAULT_CHARSET)
return s return s
class StrAndUnicode(object):
"""
A class whose __str__ returns its __unicode__ as a bytestring
according to settings.DEFAULT_CHARSET.
Useful as a mix-in.
"""
def __str__(self):
return self.__unicode__().encode(settings.DEFAULT_CHARSET)
class ErrorDict(dict): class ErrorDict(dict):
""" """
A collection of errors that knows how to display itself in various formats. A collection of errors that knows how to display itself in various formats.

View File

@ -8,7 +8,7 @@ __all__ = (
'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple', 'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
) )
from util import smart_unicode from util import StrAndUnicode, smart_unicode
from django.utils.html import escape from django.utils.html import escape
from itertools import chain from itertools import chain
@ -146,7 +146,7 @@ class SelectMultiple(Widget):
output.append(u'</select>') output.append(u'</select>')
return u'\n'.join(output) return u'\n'.join(output)
class RadioInput(object): class RadioInput(StrAndUnicode):
"An object used by RadioFieldRenderer that represents a single <input type='radio'>." "An object used by RadioFieldRenderer that represents a single <input type='radio'>."
def __init__(self, name, value, attrs, choice, index): def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value self.name, self.value = name, value
@ -154,7 +154,7 @@ class RadioInput(object):
self.choice_value, self.choice_label = choice self.choice_value, self.choice_label = choice
self.index = index self.index = index
def __str__(self): def __unicode__(self):
return u'<label>%s %s</label>' % (self.tag(), self.choice_label) return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
def is_checked(self): def is_checked(self):
@ -168,7 +168,7 @@ class RadioInput(object):
final_attrs['checked'] = 'checked' final_attrs['checked'] = 'checked'
return u'<input%s />' % flatatt(final_attrs) return u'<input%s />' % flatatt(final_attrs)
class RadioFieldRenderer(object): class RadioFieldRenderer(StrAndUnicode):
"An object used by RadioSelect to enable customization of radio widgets." "An object used by RadioSelect to enable customization of radio widgets."
def __init__(self, name, value, attrs, choices): def __init__(self, name, value, attrs, choices):
self.name, self.value, self.attrs = name, value, attrs self.name, self.value, self.attrs = name, value, attrs
@ -178,7 +178,7 @@ class RadioFieldRenderer(object):
for i, choice in enumerate(self.choices): for i, choice in enumerate(self.choices):
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i) yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
def __str__(self): def __unicode__(self):
"Outputs a <ul> for this set of radio fields." "Outputs a <ul> for this set of radio fields."
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self]) return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self])

View File

@ -742,7 +742,11 @@ class VariableNode(Node):
def encode_output(self, output): def encode_output(self, output):
# Check type so that we don't run str() on a Unicode object # Check type so that we don't run str() on a Unicode object
if not isinstance(output, basestring): if not isinstance(output, basestring):
return str(output) try:
return str(output)
except UnicodeEncodeError:
# If __str__() returns a Unicode object, convert it to bytestring.
return unicode(output).encode(settings.DEFAULT_CHARSET)
elif isinstance(output, unicode): elif isinstance(output, unicode):
return output.encode(settings.DEFAULT_CHARSET) return output.encode(settings.DEFAULT_CHARSET)
else: else:

View File

@ -48,6 +48,23 @@ See the `csrf documentation`_.
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/ .. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
formtools
=========
**New in Django development version**
A set of high-level abstractions for Django forms (django.newforms).
django.contrib.formtools.preview
--------------------------------
An abstraction of the following workflow:
"Display an HTML form, force a preview, then do something with the submission."
Full documentation for this feature does not yet exist, but you can read the
code and docstrings in ``django/contrib/formtools/preview.py`` for a start.
humanize humanize
======== ========

79
docs/newforms.txt Normal file
View File

@ -0,0 +1,79 @@
====================
The newforms library
====================
``django.newforms`` is a new replacement for ``django.forms``, the old Django
form/manipulator/validation framework. This document explains how to use this
new form library.
Migration plan
==============
``django.newforms`` currently is only available in the Django development version
-- i.e., it's not available in the Django 0.95 release. For the next Django
release, our plan is to do the following:
* Move the current ``django.forms`` to ``django.oldforms``. This will allow
for an eased migration of form code. You'll just have to change your
import statements::
from django import forms # old
from django import oldforms as forms # new
* Move the current ``django.newforms`` to ``django.forms``.
* We will remove ``django.oldforms`` in the release *after* the next Django
release -- the release that comes after the release in which we're
creating ``django.oldforms``.
With this in mind, we recommend you use the following import statement when
using ``django.newforms``::
from django import newforms as forms
This way, your code can refer to the ``forms`` module, and when
``django.newforms`` is renamed to ``django.forms``, you'll only have to change
your ``import`` statements.
If you prefer "``import *``" syntax, you can do the following::
from django.newforms import *
This will import all fields, widgets, form classes and other various utilities
into your local namespace. Some people find this convenient; others find it
too messy. The choice is yours.
Overview
========
As the ``django.forms`` system before it, ``django.newforms`` is intended to
handle HTML form display, validation and redisplay. It's what you use if you
want to perform server-side validation for an HTML form.
The library deals with these concepts:
* **Widget** -- A class that corresponds to an HTML form widget, e.g.
``<input type="text">`` or ``<textarea>``. This handles rendering of the
widget as HTML.
* **Field** -- A class that is responsible for doing validation, e.g.
an ``EmailField`` that makes sure its data is a valid e-mail address.
* **Form** -- A collection of fields that knows how to validate itself and
display itself as HTML.
Using forms with templates
==========================
Using forms in views
====================
More coming soon
================
That's all the documentation for now. For more, see the file
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py
-- the unit tests for ``django.newforms``. This can give you a good idea of
what's possible.

View File

@ -837,7 +837,7 @@ Default: ``Django/<version> (http://www.djangoproject.com/)``
The string to use as the ``User-Agent`` header when checking to see if URLs The string to use as the ``User-Agent`` header when checking to see if URLs
exist (see the ``verify_exists`` option on URLField_). exist (see the ``verify_exists`` option on URLField_).
.. URLField: ../model_api/#urlfield .. _URLField: ../model_api/#urlfield
USE_ETAGS USE_ETAGS
--------- ---------

View File

@ -636,6 +636,9 @@ Each Field's __init__() takes at least these parameters:
used for this Field when displaying it. Each Field has a default used for this Field when displaying it. Each Field has a default
Widget that it'll use if you don't specify this. In most cases, Widget that it'll use if you don't specify this. In most cases,
the default widget is TextInput. the default widget is TextInput.
label -- A verbose name for this field, for use in displaying this field in
a form. By default, Django will use a "pretty" version of the form
field name, if the Field is part of a Form.
Other than that, the Field subclasses have class-specific options for Other than that, the Field subclasses have class-specific options for
__init__(). For example, CharField has a max_length option. __init__(). For example, CharField has a max_length option.
@ -1335,7 +1338,7 @@ u''
<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" />
>>> for boundfield in p: >>> for boundfield in p:
... print boundfield.verbose_name, boundfield.data ... print boundfield.label, boundfield.data
First name John First name John
Last name Lennon Last name Lennon
Birthday 1940-10-9 Birthday 1940-10-9
@ -1368,6 +1371,13 @@ False
<li><ul class="errorlist"><li>This field is required.</li></ul>First name: <input type="text" name="first_name" /></li> <li><ul class="errorlist"><li>This field is required.</li></ul>First name: <input type="text" name="first_name" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Last name: <input type="text" name="last_name" /></li> <li><ul class="errorlist"><li>This field is required.</li></ul>Last name: <input type="text" name="last_name" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Birthday: <input type="text" name="birthday" /></li> <li><ul class="errorlist"><li>This field is required.</li></ul>Birthday: <input type="text" name="birthday" /></li>
>>> print p.as_p()
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>First name: <input type="text" name="first_name" /></p>
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Last name: <input type="text" name="last_name" /></p>
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Birthday: <input type="text" name="birthday" /></p>
If you don't pass any values to the Form's __init__(), or if you pass None, If you don't pass any values to the Form's __init__(), or if you pass None,
the Form won't do any validation. Form.errors will be an empty dictionary *but* the Form won't do any validation. Form.errors will be an empty dictionary *but*
@ -1389,6 +1399,10 @@ False
<li>First name: <input type="text" name="first_name" /></li> <li>First name: <input type="text" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li> <li>Last name: <input type="text" name="last_name" /></li>
<li>Birthday: <input type="text" name="birthday" /></li> <li>Birthday: <input type="text" name="birthday" /></li>
>>> print p.as_p()
<p>First name: <input type="text" name="first_name" /></p>
<p>Last name: <input type="text" name="last_name" /></p>
<p>Birthday: <input type="text" name="birthday" /></p>
Unicode values are handled properly. Unicode values are handled properly.
>>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'}) >>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'})
@ -1396,6 +1410,8 @@ Unicode values are handled properly.
u'<tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr>\n<tr><td>Last name:</td><td><input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></td></tr>\n<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>' u'<tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr>\n<tr><td>Last name:</td><td><input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></td></tr>\n<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>'
>>> p.as_ul() >>> p.as_ul()
u'<li>First name: <input type="text" name="first_name" value="John" /></li>\n<li>Last name: <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></li>\n<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /></li>' u'<li>First name: <input type="text" name="first_name" value="John" /></li>\n<li>Last name: <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></li>\n<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /></li>'
>>> p.as_p()
u'<p>First name: <input type="text" name="first_name" value="John" /></p>\n<p>Last name: <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></p>\n<p>Birthday: <input type="text" name="birthday" value="1940-10-9" /></p>'
>>> p = Person({'last_name': u'Lennon'}) >>> p = Person({'last_name': u'Lennon'})
>>> p.errors >>> p.errors
@ -1432,14 +1448,18 @@ If it's a string that contains '%s', Django will use that as a format string
into which the field's name will be inserted. It will also put a <label> around into which the field's name will be inserted. It will also put a <label> around
the human-readable labels for a field. the human-readable labels for a field.
>>> p = Person(auto_id='id_%s') >>> p = Person(auto_id='id_%s')
>>> print p.as_ul()
<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li>
<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li>
<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li>
>>> print p.as_table() >>> print p.as_table()
<tr><td><label for="id_first_name">First name:</label></td><td><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><td><label for="id_first_name">First name:</label></td><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><td><label for="id_last_name">Last name:</label></td><td><input type="text" name="last_name" id="id_last_name" /></td></tr> <tr><td><label for="id_last_name">Last name:</label></td><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
<tr><td><label for="id_birthday">Birthday:</label></td><td><input type="text" name="birthday" id="id_birthday" /></td></tr> <tr><td><label for="id_birthday">Birthday:</label></td><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
>>> print p.as_ul()
<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li>
<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li>
<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li>
>>> print p.as_p()
<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p>
<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p>
<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
If auto_id is any True value whose str() does not contain '%s', the "id" If auto_id is any True value whose str() does not contain '%s', the "id"
attribute will be the name of the field. attribute will be the name of the field.
@ -1596,6 +1616,12 @@ ID of the *first* radio button.
<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li> <li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li>
<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> <li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
</ul></li> </ul></li>
>>> print f.as_p()
<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p>
<p><label for="id_language_0">Language:</label> <ul>
<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li>
<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
</ul></p>
MultipleChoiceField is a special case, as its data is required to be a list: MultipleChoiceField is a special case, as its data is required to be a list:
>>> class SongForm(Form): >>> class SongForm(Form):
@ -1713,7 +1739,7 @@ Form.clean() is required to return a dictionary of all clean data.
>>> f = UserRegistration({}) >>> f = UserRegistration({})
>>> print f.as_table() >>> print f.as_table()
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr> <tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><td>Username:</td><td><input type="text" name="username" /></td></tr> <tr><td>Username:</td><td><input type="text" name="username" maxlength="10" /></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr> <tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr> <tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr> <tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
@ -1725,12 +1751,12 @@ Form.clean() is required to return a dictionary of all clean data.
{'__all__': [u'Please make sure your passwords match.']} {'__all__': [u'Please make sure your passwords match.']}
>>> print f.as_table() >>> print f.as_table()
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> <tr><td colspan="2"><ul class="errorlist"><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>Username:</td><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>
<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></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> <tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
>>> print f.as_ul() >>> print f.as_ul()
<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li> <li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
<li>Username: <input type="text" name="username" value="adrian" /></li> <li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
<li>Password1: <input type="password" name="password1" value="foo" /></li> <li>Password1: <input type="password" name="password1" value="foo" /></li>
<li>Password2: <input type="password" name="password2" value="bar" /></li> <li>Password2: <input type="password" name="password2" value="bar" /></li>
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}) >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
@ -1754,9 +1780,10 @@ subclass' __init__().
<tr><td>Last name:</td><td><input type="text" name="last_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> <tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
HiddenInput widgets are displayed differently in the as_table() and as_ul() HiddenInput widgets are displayed differently in the as_table(), as_ul()
output of a Form -- their verbose names are not displayed, and a separate and as_p() output of a Form -- their verbose names are not displayed, and a
<tr>/<li> is not displayed. separate row is not displayed. They're displayed in the last row of the
form, directly after that row's form element.
>>> class Person(Form): >>> class Person(Form):
... first_name = CharField() ... first_name = CharField()
... last_name = CharField() ... last_name = CharField()
@ -1766,43 +1793,63 @@ output of a Form -- their verbose names are not displayed, and a separate
>>> print p >>> print p
<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr> <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>Last name:</td><td><input type="text" name="last_name" /></td></tr>
<input type="hidden" name="hidden_text" /> <tr><td>Birthday:</td><td><input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></td></tr>
<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
>>> print p.as_ul() >>> print p.as_ul()
<li>First name: <input type="text" name="first_name" /></li> <li>First name: <input type="text" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li> <li>Last name: <input type="text" name="last_name" /></li>
<input type="hidden" name="hidden_text" /> <li>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></li>
<li>Birthday: <input type="text" name="birthday" /></li> >>> print p.as_p()
<p>First name: <input type="text" name="first_name" /></p>
<p>Last name: <input type="text" name="last_name" /></p>
<p>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></p>
With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label. With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label.
>>> p = Person(auto_id='id_%s') >>> p = Person(auto_id='id_%s')
>>> print p >>> print p
<tr><td><label for="id_first_name">First name:</label></td><td><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><td><label for="id_first_name">First name:</label></td><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><td><label for="id_last_name">Last name:</label></td><td><input type="text" name="last_name" id="id_last_name" /></td></tr> <tr><td><label for="id_last_name">Last name:</label></td><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
<input type="hidden" name="hidden_text" id="id_hidden_text" /> <tr><td><label for="id_birthday">Birthday:</label></td><td><input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></td></tr>
<tr><td><label for="id_birthday">Birthday:</label></td><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
>>> print p.as_ul() >>> print p.as_ul()
<li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li> <li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li>
<li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li> <li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li>
<input type="hidden" name="hidden_text" id="id_hidden_text" /> <li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></li>
<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li> >>> print p.as_p()
<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p>
<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p>
<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></p>
If a field with a HiddenInput has errors, the as_table() and as_ul() output If a field with a HiddenInput has errors, the as_table() and as_ul() output
will include the error message(s) with the text "(Hidden field [fieldname]) " will include the error message(s) with the text "(Hidden field [fieldname]) "
prepended. prepended. This message is displayed at the top of the output, regardless of
its field's order in the form.
>>> p = Person({'first_name': 'John', 'last_name': 'Lennon', 'birthday': '1940-10-9'}) >>> p = Person({'first_name': 'John', 'last_name': 'Lennon', 'birthday': '1940-10-9'})
>>> print p >>> print p
<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></td></tr>
<tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr> <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>Last name:</td><td><input type="text" name="last_name" value="Lennon" /></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></td></tr> <tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></td></tr>
<input type="hidden" name="hidden_text" />
<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>
>>> print p.as_ul() >>> print p.as_ul()
<li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li>
<li>First name: <input type="text" name="first_name" value="John" /></li> <li>First name: <input type="text" name="first_name" value="John" /></li>
<li>Last name: <input type="text" name="last_name" value="Lennon" /></li> <li>Last name: <input type="text" name="last_name" value="Lennon" /></li>
<li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li> <li>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></li>
<input type="hidden" name="hidden_text" /> >>> print p.as_p()
<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /></li> <p><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></p>
<p>First name: <input type="text" name="first_name" value="John" /></p>
<p>Last name: <input type="text" name="last_name" value="Lennon" /></p>
<p>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></p>
A corner case: It's possible for a form to have only HiddenInputs.
>>> class TestForm(Form):
... foo = CharField(widget=HiddenInput)
... bar = CharField(widget=HiddenInput)
>>> p = TestForm()
>>> print p.as_table()
<input type="hidden" name="foo" /><input type="hidden" name="bar" />
>>> print p.as_ul()
<input type="hidden" name="foo" /><input type="hidden" name="bar" />
>>> print p.as_p()
<input type="hidden" name="foo" /><input type="hidden" name="bar" />
A Form's fields are displayed in the same order in which they were defined. A Form's fields are displayed in the same order in which they were defined.
>>> class TestForm(Form): >>> class TestForm(Form):
@ -1837,6 +1884,46 @@ A Form's fields are displayed in the same order in which they were defined.
<tr><td>Field13:</td><td><input type="text" name="field13" /></td></tr> <tr><td>Field13:</td><td><input type="text" name="field13" /></td></tr>
<tr><td>Field14:</td><td><input type="text" name="field14" /></td></tr> <tr><td>Field14:</td><td><input type="text" name="field14" /></td></tr>
Some Field classes have an effect on the HTML attributes of their associated
Widget. If you set max_length in a CharField and its associated widget is
either a TextInput or PasswordInput, then the widget's rendered HTML will
include the "maxlength" attribute.
>>> class UserRegistration(Form):
... username = CharField(max_length=10) # uses TextInput by default
... password = CharField(max_length=10, widget=PasswordInput)
... realname = CharField(max_length=10, widget=TextInput) # redundantly define widget, just to test
... address = CharField() # no max_length defined here
>>> p = UserRegistration()
>>> print p.as_ul()
<li>Username: <input type="text" name="username" maxlength="10" /></li>
<li>Password: <input type="password" name="password" maxlength="10" /></li>
<li>Realname: <input type="text" name="realname" maxlength="10" /></li>
<li>Address: <input type="text" name="address" /></li>
If you specify a custom "attrs" that includes the "maxlength" attribute,
the Field's max_length attribute will override whatever "maxlength" you specify
in "attrs".
>>> class UserRegistration(Form):
... username = CharField(max_length=10, widget=TextInput(attrs={'maxlength': 20}))
... password = CharField(max_length=10, widget=PasswordInput)
>>> p = UserRegistration()
>>> print p.as_ul()
<li>Username: <input type="text" name="username" maxlength="10" /></li>
<li>Password: <input type="password" name="password" maxlength="10" /></li>
You can specify the label for a field by using the 'label' argument to a Field
class. If you don't specify 'label', Django will use the field name with
underscores converted to spaces, and the initial letter capitalized.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label='Your username')
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput, label='Password (again)')
>>> p = UserRegistration()
>>> print p.as_ul()
<li>Your username: <input type="text" name="username" maxlength="10" /></li>
<li>Password1: <input type="password" name="password1" /></li>
<li>Password (again): <input type="password" name="password2" /></li>
# Basic form processing in a view ############################################# # Basic form processing in a view #############################################
>>> from django.template import Template, Context >>> from django.template import Template, Context
@ -1862,7 +1949,7 @@ Case 1: GET (an empty form, with no errors).
>>> print my_function('GET', {}) >>> print my_function('GET', {})
<form action="" method="post"> <form action="" method="post">
<table> <table>
<tr><td>Username:</td><td><input type="text" name="username" /></td></tr> <tr><td>Username:</td><td><input type="text" name="username" maxlength="10" /></td></tr>
<tr><td>Password1:</td><td><input type="password" name="password1" /></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> <tr><td>Password2:</td><td><input type="password" name="password2" /></td></tr>
</table> </table>
@ -1875,7 +1962,7 @@ Case 2: POST with erroneous data (a redisplayed form, with errors).
<table> <table>
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> <tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>Ensure this value has at most 10 characters.</li></ul></td></tr> <tr><td colspan="2"><ul class="errorlist"><li>Ensure this value has at most 10 characters.</li></ul></td></tr>
<tr><td>Username:</td><td><input type="text" name="username" value="this-is-a-long-username" /></td></tr> <tr><td>Username:</td><td><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></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> <tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
</table> </table>
@ -1910,37 +1997,39 @@ particular field.
... </form>''') ... </form>''')
>>> print t.render(Context({'form': UserRegistration()})) >>> print t.render(Context({'form': UserRegistration()}))
<form action=""> <form action="">
<p><label>Your username: <input type="text" name="username" /></label></p> <p><label>Your username: <input type="text" name="username" maxlength="10" /></label></p>
<p><label>Password: <input type="password" name="password1" /></label></p> <p><label>Password: <input type="password" name="password1" /></label></p>
<p><label>Password (again): <input type="password" name="password2" /></label></p> <p><label>Password (again): <input type="password" name="password2" /></label></p>
<input type="submit" /> <input type="submit" />
</form> </form>
>>> print t.render(Context({'form': UserRegistration({'username': 'django'})})) >>> print t.render(Context({'form': UserRegistration({'username': 'django'})}))
<form action=""> <form action="">
<p><label>Your username: <input type="text" name="username" value="django" /></label></p> <p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
<ul class="errorlist"><li>This field is required.</li></ul><p><label>Password: <input type="password" name="password1" /></label></p> <ul class="errorlist"><li>This field is required.</li></ul><p><label>Password: <input type="password" name="password1" /></label></p>
<ul class="errorlist"><li>This field is required.</li></ul><p><label>Password (again): <input type="password" name="password2" /></label></p> <ul class="errorlist"><li>This field is required.</li></ul><p><label>Password (again): <input type="password" name="password2" /></label></p>
<input type="submit" /> <input type="submit" />
</form> </form>
Use form.[field].verbose_name to output a field's "verbose name" -- its field Use form.[field].label to output a field's label. You can specify the label for
name with underscores converted to spaces, and the initial letter capitalized. a field by using the 'label' argument to a Field class. If you don't specify
'label', Django will use the field name with underscores converted to spaces,
and the initial letter capitalized.
>>> t = Template('''<form action=""> >>> t = Template('''<form action="">
... <p><label>{{ form.username.verbose_name }}: {{ form.username }}</label></p> ... <p><label>{{ form.username.label }}: {{ form.username }}</label></p>
... <p><label>{{ form.password1.verbose_name }}: {{ form.password1 }}</label></p> ... <p><label>{{ form.password1.label }}: {{ form.password1 }}</label></p>
... <p><label>{{ form.password2.verbose_name }}: {{ form.password2 }}</label></p> ... <p><label>{{ form.password2.label }}: {{ form.password2 }}</label></p>
... <input type="submit" /> ... <input type="submit" />
... </form>''') ... </form>''')
>>> print t.render(Context({'form': UserRegistration()})) >>> print t.render(Context({'form': UserRegistration()}))
<form action=""> <form action="">
<p><label>Username: <input type="text" name="username" /></label></p> <p><label>Username: <input type="text" name="username" maxlength="10" /></label></p>
<p><label>Password1: <input type="password" name="password1" /></label></p> <p><label>Password1: <input type="password" name="password1" /></label></p>
<p><label>Password2: <input type="password" name="password2" /></label></p> <p><label>Password2: <input type="password" name="password2" /></label></p>
<input type="submit" /> <input type="submit" />
</form> </form>
User form.[field].label_tag to output a field's verbose_name with a <label> User form.[field].label_tag to output a field's label with a <label> tag
tag wrapped around it, but *only* if the given field has an "id" attribute. wrapped around it, but *only* if the given field has an "id" attribute.
Recall from above that passing the "auto_id" argument to a Form gives each Recall from above that passing the "auto_id" argument to a Form gives each
field an "id" attribute. field an "id" attribute.
>>> t = Template('''<form action=""> >>> t = Template('''<form action="">
@ -1951,14 +2040,14 @@ field an "id" attribute.
... </form>''') ... </form>''')
>>> print t.render(Context({'form': UserRegistration()})) >>> print t.render(Context({'form': UserRegistration()}))
<form action=""> <form action="">
<p>Username: <input type="text" name="username" /></p> <p>Username: <input type="text" name="username" maxlength="10" /></p>
<p>Password1: <input type="password" name="password1" /></p> <p>Password1: <input type="password" name="password1" /></p>
<p>Password2: <input type="password" name="password2" /></p> <p>Password2: <input type="password" name="password2" /></p>
<input type="submit" /> <input type="submit" />
</form> </form>
>>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')})) >>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')}))
<form action=""> <form action="">
<p><label for="id_username">Username</label>: <input type="text" name="username" id="id_username" /></p> <p><label for="id_username">Username</label>: <input id="id_username" type="text" name="username" maxlength="10" /></p>
<p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p> <p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p>
<p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p> <p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p>
<input type="submit" /> <input type="submit" />
@ -1976,7 +2065,7 @@ the list of errors is empty). You can also use it in {% if %} statements.
... </form>''') ... </form>''')
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})})) >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))
<form action=""> <form action="">
<p><label>Your username: <input type="text" name="username" value="django" /></label></p> <p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> <p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> <p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
<input type="submit" /> <input type="submit" />
@ -1991,7 +2080,7 @@ the list of errors is empty). You can also use it in {% if %} statements.
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})})) >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))
<form action=""> <form action="">
<ul class="errorlist"><li>Please make sure your passwords match.</li></ul> <ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
<p><label>Your username: <input type="text" name="username" value="django" /></label></p> <p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> <p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> <p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
<input type="submit" /> <input type="submit" />

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from django.conf import settings from django.conf import settings
if __name__ == '__main__': if __name__ == '__main__':
@ -62,6 +63,11 @@ class OtherClass:
def method(self): def method(self):
return "OtherClass.method" return "OtherClass.method"
class UnicodeInStrClass:
"Class whose __str__ returns a Unicode object."
def __str__(self):
return u'ŠĐĆŽćžšđ'
class Templates(unittest.TestCase): class Templates(unittest.TestCase):
def test_templates(self): def test_templates(self):
# NOW and NOW_tz are used by timesince tag tests. # NOW and NOW_tz are used by timesince tag tests.
@ -173,6 +179,10 @@ class Templates(unittest.TestCase):
# Empty strings can be passed as arguments to filters # Empty strings can be passed as arguments to filters
'basic-syntax36': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'), 'basic-syntax36': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'),
# If a variable has a __str__() that returns a Unicode object, the value
# will be converted to a bytestring.
'basic-syntax37': (r'{{ var }}', {'var': UnicodeInStrClass()}, '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91'),
### COMMENT SYNTAX ######################################################## ### COMMENT SYNTAX ########################################################
'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"), 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"), 'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
@ -328,18 +338,18 @@ class Templates(unittest.TestCase):
'ifchanged05': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (1, 2, 3)}, '1123123123'), 'ifchanged05': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (1, 2, 3)}, '1123123123'),
'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. # 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-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'), '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. # 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'), '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 # 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. # 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'), '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 # Logically the same as above, just written with explicit
# ifchanged for the day. # 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'), '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'),