mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +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:
parent
55d7f6519c
commit
694f44f600
@ -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 %}
|
||||||
|
@ -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:
|
||||||
|
@ -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()
|
||||||
|
0
django/contrib/formtools/__init__.py
Normal file
0
django/contrib/formtools/__init__.py
Normal file
160
django/contrib/formtools/preview.py
Normal file
160
django/contrib/formtools/preview.py
Normal 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__)
|
15
django/contrib/formtools/templates/formtools/form.html
Normal file
15
django/contrib/formtools/templates/formtools/form.html
Normal 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 %}
|
36
django/contrib/formtools/templates/formtools/preview.html
Normal file
36
django/contrib/formtools/templates/formtools/preview.html
Normal 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 %}
|
@ -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))
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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])
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
try:
|
||||||
return str(output)
|
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:
|
||||||
|
@ -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
79
docs/newforms.txt
Normal 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.
|
@ -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
|
||||||
---------
|
---------
|
||||||
|
@ -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" />
|
||||||
|
@ -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"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user