Commits

Anonymous committed 9dc7c7d

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

  • Participants
  • Parent commits 9d34873
  • Branches multiple-db-support

Comments (0)

Files changed (19)

django/contrib/admin/templates/admin/search_form.html

 <input type="text" size="40" name="{{ search_var }}" value="{{ cl.query|escape }}" id="searchbar" />
 <input type="submit" value="{% trans 'Go' %}" />
 {% 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 %}
 {% for pair in cl.params.items %}
     {% ifnotequal pair.0 search_var %}<input type="hidden" name="{{ pair.0|escape }}" value="{{ pair.1|escape }}"/>{% endifnotequal %}

django/contrib/admin/views/main.py

 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)
     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
 
     if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
     model = models.get_model(app_label, model_name)
     object_id = unquote(object_id)
     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
 
     if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
 
     try:
         manipulator = model.ChangeManipulator(object_id)
-    except ObjectDoesNotExist:
-        raise Http404
+    except model.DoesNotExist:
+        raise Http404('%s object with primary key %r does not exist' % (model_name, escape(object_id)))
 
     if request.POST:
         new_data = request.POST.copy()
     model = models.get_model(app_label, model_name)
     object_id = unquote(object_id)
     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
     if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
         raise PermissionDenied
     model = models.get_model(app_label, model_name)
     object_id = unquote(object_id)
     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,
         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.
 def change_list(request, app_label, model_name):
     model = models.get_model(app_label, model_name)
     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()):
         raise PermissionDenied
     try:

django/contrib/contenttypes/management.py

 """
 
 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
     app_models = get_models(app)
     if not app_models:
             if verbosity >= 2:
                 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)
+
+if __name__ == "__main__":
+    create_all_contenttypes()

django/contrib/formtools/__init__.py

Empty file added.

django/contrib/formtools/preview.py

+"""
+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__)

django/contrib/formtools/templates/formtools/form.html

+{% 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 %}

django/contrib/formtools/templates/formtools/preview.html

+{% 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 %}

django/contrib/sitemaps/__init__.py

 
     from django.contrib.sites.models import Site
     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})
     urllib.urlopen("%s?%s" % (ping_url, params))
 

django/core/servers/fastcgi.py

     else:
         return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
 
+    wsgi_opts['debug'] = False # Turn off flup tracebacks
+
     # Prep up and go
     from django.core.handlers.wsgi import WSGIHandler
 

django/newforms/fields.py

 Field classes
 """
 
-from util import ValidationError, DEFAULT_ENCODING, smart_unicode
-from widgets import TextInput, CheckboxInput, Select, SelectMultiple
+from django.utils.translation import gettext
+from util import ValidationError, smart_unicode
+from widgets import TextInput, PasswordInput, CheckboxInput, Select, SelectMultiple
 import datetime
 import re
 import time
     # Tracks each time a Field instance is created. Used to retain order.
     creation_counter = 0
 
-    def __init__(self, required=True, widget=None):
-        self.required = required
+    def __init__(self, required=True, widget=None, label=None):
+        self.required, self.label = required, label
         widget = widget or self.widget
         if isinstance(widget, type):
             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
 
         # Increase the creation counter, and save our local copy.
         Raises ValidationError for any errors.
         """
         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
 
+    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):
-    def __init__(self, max_length=None, min_length=None, required=True, widget=None):
-        Field.__init__(self, required, widget)
+    def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None):
         self.max_length, self.min_length = max_length, min_length
+        Field.__init__(self, required, widget, label)
 
     def clean(self, value):
         "Validates max_length and min_length. Returns a Unicode object."
         if value in EMPTY_VALUES: value = u''
         value = smart_unicode(value)
         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:
-            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
 
+    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):
     def clean(self, value):
         """
         try:
             return int(value)
         except (ValueError, TypeError):
-            raise ValidationError(u'Enter a whole number.')
+            raise ValidationError(gettext(u'Enter a whole number.'))
 
 DEFAULT_DATE_INPUT_FORMATS = (
     '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
 )
 
 class DateField(Field):
-    def __init__(self, input_formats=None, required=True, widget=None):
-        Field.__init__(self, required, widget)
+    def __init__(self, input_formats=None, required=True, widget=None, label=None):
+        Field.__init__(self, required, widget, label)
         self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
 
     def clean(self, value):
                 return datetime.date(*time.strptime(value, format)[:3])
             except ValueError:
                 continue
-        raise ValidationError(u'Enter a valid date.')
+        raise ValidationError(gettext(u'Enter a valid date.'))
 
 DEFAULT_DATETIME_INPUT_FORMATS = (
     '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
 )
 
 class DateTimeField(Field):
-    def __init__(self, input_formats=None, required=True, widget=None):
-        Field.__init__(self, required, widget)
+    def __init__(self, input_formats=None, required=True, widget=None, label=None):
+        Field.__init__(self, required, widget, label)
         self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
 
     def clean(self, value):
                 return datetime.datetime(*time.strptime(value, format)[:6])
             except ValueError:
                 continue
-        raise ValidationError(u'Enter a valid date/time.')
+        raise ValidationError(gettext(u'Enter a valid date/time.'))
 
 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.
         error_message is an optional error message to use, if
         'Enter a valid value' is too generic for you.
         """
-        Field.__init__(self, required, widget)
+        Field.__init__(self, required, widget, label)
         if isinstance(regex, basestring):
             regex = re.compile(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):
         """
     r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain
 
 class EmailField(RegexField):
-    def __init__(self, required=True, widget=None):
-        RegexField.__init__(self, email_re, u'Enter a valid e-mail address.', required, widget)
+    def __init__(self, required=True, widget=None, label=None):
+        RegexField.__init__(self, email_re, gettext(u'Enter a valid e-mail address.'), required, widget, label)
 
 url_re = re.compile(
     r'^https?://' # http:// or https://
     URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
 
 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):
-        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.user_agent = validator_user_agent
 
                 req = urllib2.Request(value, None, headers)
                 u = urllib2.urlopen(req)
             except ValueError:
-                raise ValidationError(u'Enter a valid URL.')
+                raise ValidationError(gettext(u'Enter a valid URL.'))
             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
 
 class BooleanField(Field):
         return bool(value)
 
 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):
             widget = widget(choices=choices)
-        Field.__init__(self, required, widget)
+        Field.__init__(self, required, widget, label)
         self.choices = choices
 
     def clean(self, value):
             return value
         valid_values = set([str(k) for k, v in self.choices])
         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
 
 class MultipleChoiceField(ChoiceField):
-    def __init__(self, choices=(), required=True, widget=SelectMultiple):
-        ChoiceField.__init__(self, choices, required, widget)
+    def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None):
+        ChoiceField.__init__(self, choices, required, widget, label)
 
     def clean(self, value):
         """
         Validates that the input is a list or tuple.
         """
         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:
             return []
         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 = []
         for val in value:
             val = smart_unicode(val)
             new_value.append(val)
         # 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:
             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
 
 class ComboField(Field):
-    def __init__(self, fields=(), required=True, widget=None):
-        Field.__init__(self, required, widget)
+    def __init__(self, fields=(), required=True, widget=None, label=None):
+        Field.__init__(self, required, widget, label)
         # Set 'required' to False on the individual fields, because the
         # required validation will be handled by ComboField, not by those
         # individual fields.

django/newforms/forms.py

 from django.utils.html import escape
 from fields import Field
 from widgets import TextInput, Textarea, HiddenInput
-from util import ErrorDict, ErrorList, ValidationError
+from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
 
 NON_FIELD_ERRORS = '__all__'
 
         attrs['fields'] = SortedDictFromList(fields)
         return type.__new__(cls, name, bases, attrs)
 
-class Form(object):
+class Form(StrAndUnicode):
     "A collection of Fields, plus their associated data."
     __metaclass__ = DeclarativeFieldsMetaclass
 
         self.clean_data = None # Stores the data 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()
 
     def __iter__(self):
         """
         return not self.ignore_errors and not bool(self.errors)
 
+    def _html_output(self, normal_row, error_row, row_ender, errors_on_separate_row):
+        "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
+        top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
+        output, hidden_fields = [], []
+        for name, field in self.fields.items():
+            bf = BoundField(self, field, name)
+            bf_errors = bf.errors # Cache in local variable.
+            if bf.is_hidden:
+                if bf_errors:
+                    top_errors.extend(['(Hidden field %s) %s' % (name, e) for e in bf_errors])
+                hidden_fields.append(unicode(bf))
+            else:
+                if errors_on_separate_row and bf_errors:
+                    output.append(error_row % bf_errors)
+                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)
+
     def as_table(self):
         "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
-        output = []
-        if self.errors.get(NON_FIELD_ERRORS):
-            # 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():
-            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'<tr><td colspan="2">%s</td></tr>' % new_errors)
-                output.append(str(bf))
-            else:
-                if bf.errors:
-                    output.append(u'<tr><td colspan="2">%s</td></tr>' % bf.errors)
-                output.append(u'<tr><td>%s</td><td>%s</td></tr>' % (bf.label_tag(escape(bf.verbose_name+':')), bf))
-        return u'\n'.join(output)
+        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):
         "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
-        output = []
-        if self.errors.get(NON_FIELD_ERRORS):
-            # Errors not corresponding to a particular field are displayed at the top.
-            output.append(u'<li>%s</li>' % self.non_field_errors())
-        for name, field in self.fields.items():
-            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)
+        return self._html_output(u'<li>%(errors)s%(label)s %(field)s</li>', u'<li>%s</li>', '</li>', False)
+
+    def as_p(self):
+        "Returns this form rendered as HTML <p>s."
+        return self._html_output(u'<p>%(label)s %(field)s</p>', u'<p>%s</p>', '</p>', True)
 
     def non_field_errors(self):
         """
         """
         return self.clean_data
 
-class BoundField(object):
+class BoundField(StrAndUnicode):
     "A Field plus data"
     def __init__(self, form, field, name):
-        self._form = form
-        self._field = field
-        self._name = name
+        self.form = form
+        self.field = field
+        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."
         # Use the 'widget' attribute on the field to determine which type
         # of HTML widget to use.
-        value = self.as_widget(self._field.widget)
+        value = self.as_widget(self.field.widget)
         if not isinstance(value, basestring):
             # Some Widget render() methods -- notably RadioSelect -- return a
             # "special" object rather than a string. Call the __str__() on that
         Returns an ErrorList for this field. Returns an empty ErrorList
         if there are none.
         """
-        try:
-            return self._form.errors[self._name]
-        except KeyError:
-            return ErrorList()
+        return self.form.errors.get(self.name, ErrorList())
     errors = property(_errors)
 
     def as_widget(self, widget, attrs=None):
         auto_id = self.auto_id
         if auto_id and not attrs.has_key('id') and not widget.attrs.has_key('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 _data(self):
         "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)
 
-    def _verbose_name(self):
-        return pretty_name(self._name)
-    verbose_name = property(_verbose_name)
-
     def label_tag(self, contents=None):
         """
         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
-        field's HTML-escaped verbose_name.
+        field's HTML-escaped label.
         """
-        contents = contents or escape(self.verbose_name)
-        widget = self._field.widget
+        contents = contents or escape(self.label)
+        widget = self.field.widget
         id_ = widget.attrs.get('id') or self.auto_id
         if id_:
             contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
 
     def _is_hidden(self):
         "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)
 
     def _auto_id(self):
         Calculates and returns the ID attribute for this BoundField, if the
         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):
-            return str(auto_id) % self._name
+            return str(auto_id) % self.name
         elif auto_id:
-            return self._name
+            return self.name
         return ''
     auto_id = property(_auto_id)

django/newforms/util.py

-# Default encoding for input byte strings.
-DEFAULT_ENCODING = 'utf-8' # TODO: First look at django.conf.settings, then fall back to this.
+from django.conf import settings
 
 def smart_unicode(s):
     if not isinstance(s, basestring):
         s = unicode(str(s))
     elif not isinstance(s, unicode):
-        s = unicode(s, DEFAULT_ENCODING)
+        s = unicode(s, settings.DEFAULT_CHARSET)
     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):
     """
     A collection of errors that knows how to display itself in various formats.

django/newforms/widgets.py

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

django/template/__init__.py

     def encode_output(self, output):
         # Check type so that we don't run str() on a Unicode object
         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):
             return output.encode(settings.DEFAULT_CHARSET)
         else:
 
 .. _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
 ========
 

docs/newforms.txt

+====================
+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.

docs/settings.txt

 The string to use as the ``User-Agent`` header when checking to see if URLs
 exist (see the ``verify_exists`` option on URLField_).
 
-.. URLField: ../model_api/#urlfield
+.. _URLField: ../model_api/#urlfield
 
 USE_ETAGS
 ---------

tests/regressiontests/forms/tests.py

               used for this Field when displaying it. Each Field has a default
               Widget that it'll use if you don't specify this. In most cases,
               the default widget is TextInput.
+    label -- A verbose name for this field, for use in displaying this field in
+             a form. By default, Django will use a "pretty" version of the form
+             field name, if the Field is part of a Form.
 
 Other than that, the Field subclasses have class-specific options for
 __init__(). For example, CharField has a max_length option.
 <input type="text" name="last_name" value="Lennon" />
 <input type="text" name="birthday" value="1940-10-9" />
 >>> for boundfield in p:
-...     print boundfield.verbose_name, boundfield.data
+...     print boundfield.label, boundfield.data
 First name John
 Last name Lennon
 Birthday 1940-10-9
 <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>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,
 the Form won't do any validation. Form.errors will be an empty dictionary *but*
 <li>First name: <input type="text" name="first_name" /></li>
 <li>Last name: <input type="text" name="last_name" /></li>
 <li>Birthday: <input type="text" name="birthday" /></li>
+>>> 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.
 >>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'})
 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()
 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.errors
 into which the field's name will be inserted. It will also put a <label> around
 the human-readable labels for a field.
 >>> p = Person(auto_id='id_%s')
+>>> 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_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>
 >>> 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()
-<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_birthday">Birthday:</label></td><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
+>>> 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"
 attribute will be the name of the field.
 <li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li>
 <li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
 </ul></li>
+>>> print f.as_p()
+<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p>
+<p><label for="id_language_0">Language:</label> <ul>
+<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li>
+<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
+</ul></p>
 
 MultipleChoiceField is a special case, as its data is required to be a list:
 >>> class SongForm(Form):
 >>> f = UserRegistration({})
 >>> print f.as_table()
 <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>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>
 {'__all__': [u'Please make sure your passwords match.']}
 >>> print f.as_table()
 <tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
-<tr><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>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
 >>> print f.as_ul()
 <li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
-<li>Username: <input type="text" name="username" value="adrian" /></li>
+<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
 <li>Password1: <input type="password" name="password1" value="foo" /></li>
 <li>Password2: <input type="password" name="password2" value="bar" /></li>
 >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
 <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>
 
-HiddenInput widgets are displayed differently in the as_table() and as_ul()
-output of a Form -- their verbose names are not displayed, and a separate
-<tr>/<li> is not displayed.
+HiddenInput widgets are displayed differently in the as_table(), as_ul()
+and as_p() output of a Form -- their verbose names are not displayed, and a
+separate row is not displayed. They're displayed in the last row of the
+form, directly after that row's form element.
 >>> class Person(Form):
 ...     first_name = CharField()
 ...     last_name = CharField()
 >>> print p
 <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>
-<input type="hidden" name="hidden_text" />
-<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></td></tr>
 >>> print p.as_ul()
 <li>First name: <input type="text" name="first_name" /></li>
 <li>Last name: <input type="text" name="last_name" /></li>
-<input type="hidden" name="hidden_text" />
-<li>Birthday: <input type="text" name="birthday" /></li>
+<li>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></li>
+>>> print p.as_p()
+<p>First name: <input type="text" name="first_name" /></p>
+<p>Last name: <input type="text" name="last_name" /></p>
+<p>Birthday: <input type="text" name="birthday" /><input type="hidden" name="hidden_text" /></p>
 
 With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label.
 >>> p = Person(auto_id='id_%s')
 >>> print p
 <tr><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>
-<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" /></td></tr>
+<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>
 >>> 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>
-<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" /></li>
+<li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></li>
+>>> print p.as_p()
+<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p>
+<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p>
+<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /><input type="hidden" name="hidden_text" id="id_hidden_text" /></p>
 
 If a field with a HiddenInput has errors, the as_table() and as_ul() output
 will include the error message(s) with the text "(Hidden field [fieldname]) "
-prepended.
+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'})
 >>> 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>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>
-<input type="hidden" name="hidden_text" />
-<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>
+<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></td></tr>
 >>> print p.as_ul()
+<li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li>
 <li>First name: <input type="text" name="first_name" value="John" /></li>
 <li>Last name: <input type="text" name="last_name" value="Lennon" /></li>
-<li><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></li>
-<input type="hidden" name="hidden_text" />
-<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /></li>
+<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></li>
+>>> print p.as_p()
+<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.
 >>> class TestForm(Form):
 <tr><td>Field13:</td><td><input type="text" name="field13" /></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 #############################################
 
 >>> from django.template import Template, Context
 >>> print my_function('GET', {})
 <form action="" method="post">
 <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>Password2:</td><td><input type="password" name="password2" /></td></tr>
 </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>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>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
 </table>
 ... </form>''')
 >>> print t.render(Context({'form': UserRegistration()}))
 <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 (again): <input type="password" name="password2" /></label></p>
 <input type="submit" />
 </form>
 >>> print t.render(Context({'form': UserRegistration({'username': 'django'})}))
 <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 (again): <input type="password" name="password2" /></label></p>
 <input type="submit" />
 </form>
 
-Use form.[field].verbose_name to output a field's "verbose name" -- its field
-name with underscores converted to spaces, and the initial letter capitalized.
+Use form.[field].label to output a field's label. You can specify the label for
+a field by using the 'label' argument to a Field class. If you don't specify
+'label', Django will use the field name with underscores converted to spaces,
+and the initial letter capitalized.
 >>> t = Template('''<form action="">
-... <p><label>{{ form.username.verbose_name }}: {{ form.username }}</label></p>
-... <p><label>{{ form.password1.verbose_name }}: {{ form.password1 }}</label></p>
-... <p><label>{{ form.password2.verbose_name }}: {{ form.password2 }}</label></p>
+... <p><label>{{ form.username.label }}: {{ form.username }}</label></p>
+... <p><label>{{ form.password1.label }}: {{ form.password1 }}</label></p>
+... <p><label>{{ form.password2.label }}: {{ form.password2 }}</label></p>
 ... <input type="submit" />
 ... </form>''')
 >>> print t.render(Context({'form': UserRegistration()}))
 <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>Password2: <input type="password" name="password2" /></label></p>
 <input type="submit" />
 </form>
 
-User form.[field].label_tag to output a field's verbose_name with a <label>
-tag wrapped around it, but *only* if the given field has an "id" attribute.
+User form.[field].label_tag to output a field's label with a <label> tag
+wrapped around it, but *only* if the given field has an "id" attribute.
 Recall from above that passing the "auto_id" argument to a Form gives each
 field an "id" attribute.
 >>> t = Template('''<form action="">
 ... </form>''')
 >>> print t.render(Context({'form': UserRegistration()}))
 <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>Password2: <input type="password" name="password2" /></p>
 <input type="submit" />
 </form>
 >>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')}))
 <form action="">
-<p><label for="id_username">Username</label>: <input 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_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p>
 <input type="submit" />
 ... </form>''')
 >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))
 <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 (again): <input type="password" name="password2" value="bar" /></label></p>
 <input type="submit" />
 >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))
 <form action="">
 <ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
-<p><label>Your username: <input type="text" name="username" value="django" /></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 (again): <input type="password" name="password2" value="bar" /></label></p>
 <input type="submit" />

tests/regressiontests/templates/tests.py

+# -*- coding: utf-8 -*-
 from django.conf import settings
 
 if __name__ == '__main__':
     def method(self):
         return "OtherClass.method"
 
+class UnicodeInStrClass:
+    "Class whose __str__ returns a Unicode object."
+    def __str__(self):
+        return u'ŠĐĆŽćžšđ'
+
 class Templates(unittest.TestCase):
     def test_templates(self):
         # NOW and NOW_tz are used by timesince tag tests.
             # Empty strings can be passed as arguments to filters
             '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-syntax01': ("{# this is hidden #}hello", {}, "hello"),
             'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
             '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'),
             'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'),
-            
+
             # Test one parameter given to ifchanged.
             'ifchanged-param01': ('{% for n in num %}{% ifchanged n %}..{% endifchanged %}{{ n }}{% endfor %}', { 'num': (1,2,3) }, '..1..2..3'),
             'ifchanged-param02': ('{% for n in num %}{% for x in numx %}{% ifchanged n %}..{% endifchanged %}{{ x }}{% endfor %}{% endfor %}', { 'num': (1,2,3), 'numx': (5,6,7) }, '..567..567..567'),
-            
+
             # Test multiple parameters to ifchanged.
             'ifchanged-param03': ('{% for n in num %}{{ n }}{% for x in numx %}{% ifchanged x n %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1,1,2), 'numx': (5,6,6) }, '156156256'),
-            
+
             # Test a date+hour like construct, where the hour of the last day
             # is the same but the date had changed, so print the hour anyway.
             'ifchanged-param04': ('{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),
-            
+
             # Logically the same as above, just written with explicit
             # ifchanged for the day.
             'ifchanged-param04': ('{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),