1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

boulder-oracle-sprint: Merged to trunk [4253]

git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@4254 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Boulder Sprinters 2006-12-28 17:17:52 +00:00
parent f4aa493322
commit 58ba520f32
29 changed files with 950 additions and 1172 deletions

View File

@ -118,10 +118,12 @@ answer newbie questions, and generally made Django that much better:
Manuzhai
Petar Marić
mark@junklight.com
Yasushi Masuda <whosaysni@gmail.com>
mattycakes@gmail.com
Jason McBrayer <http://www.carcosa.net/jason/>
mccutchen@gmail.com
michael.mcewan@gmail.com
mitakummaa@gmail.com
mmarshall
Eric Moritz <http://eric.themoritzfamily.com/>
Robin Munn <http://www.geekforgod.com/>
@ -160,6 +162,7 @@ answer newbie questions, and generally made Django that much better:
Tom Insam
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
Karen Tracey <graybark@bellsouth.net>
Makoto Tsuyuki <mtsuyuki@gmail.com>
Amit Upadhyay
Geert Vanderkelen
Milton Waddams

View File

@ -38,7 +38,10 @@
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title|escape }}</h1>{% endif %}{% endblock %}
{% block content %}{{ content }}{% endblock %}
{% block content %}
{% block object-tools %}{% endblock %}
{{ content }}
{% endblock %}
{% block sidebar %}{% endblock %}
<br class="clear" />
</div>

View File

@ -16,11 +16,13 @@
</div>
{% endif %}{% endblock %}
{% block content %}<div id="content-main">
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
</ul>
{% endif %}{% endif %}
{% endblock %}
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
<div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}

View File

@ -7,9 +7,11 @@
{% block coltype %}flex{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
{% if has_add_permission %}
<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
{% endif %}
{% endblock %}
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
{% block search %}{% search_form cl %}{% endblock %}
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}

View File

@ -11,7 +11,7 @@ import md5
import re
import itertools
_ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>"
_ERROR_MSG = '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>'
_POST_FORM_RE = \
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)

View File

@ -60,7 +60,10 @@ class BaseHandler(object):
if response:
return response
resolver = urlresolvers.RegexURLResolver(r'^/', settings.ROOT_URLCONF)
# Get urlconf from request object, if available. Otherwise use default.
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
try:
callback, callback_args, callback_kwargs = resolver.resolve(request.path)

View File

@ -81,6 +81,8 @@ def destroy_test_db(settings, connection, backend, old_database_name, verbosity=
settings.DATABASE_NAME = old_database_name
#settings.DATABASE_USER = 'old_user'
#settings.DATABASE_PASSWORD = 'old_password'
settings.DATABASE_USER = 'mboersma'
settings.DATABASE_PASSWORD = 'password'
cursor = connection.cursor()
time.sleep(1) # To avoid "database is being accessed by other users" errors.

View File

@ -20,6 +20,38 @@ except ImportError:
# Import copy of _thread_local.py from Python 2.4
from django.utils._threading_local import local
def smart_basestring(s, charset):
if isinstance(s, unicode):
return s.encode(charset)
return s
class UnicodeCursorWrapper(object):
"""
A thin wrapper around psycopg cursors that allows them to accept Unicode
strings as params.
This is necessary because psycopg doesn't apply any DB quoting to
parameters that are Unicode strings. If a param is Unicode, this will
convert it to a bytestring using DEFAULT_CHARSET before passing it to
psycopg.
"""
def __init__(self, cursor, charset):
self.cursor = cursor
self.charset = charset
def execute(self, sql, params=()):
return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params])
def executemany(self, sql, param_list):
new_param_list = [[smart_basestring(p, self.charset) for p in params] for params in param_list]
return self.cursor.executemany(sql, new_param_list)
def __getattr__(self, attr):
if self.__dict__.has_key(attr):
return self.__dict__[attr]
else:
return getattr(self.cursor, attr)
class DatabaseWrapper(local):
def __init__(self, **kwargs):
self.connection = None
@ -45,6 +77,7 @@ class DatabaseWrapper(local):
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
cursor = self.connection.cursor()
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
cursor = UnicodeCursorWrapper(cursor, settings.DEFAULT_CHARSET)
if settings.DEBUG:
return util.CursorDebugWrapper(cursor, self)
return cursor
@ -131,7 +164,7 @@ def get_autoinc_sql(table):
try:
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
except AttributeError:
raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1"
raise Exception, "You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'."
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))

View File

@ -337,11 +337,15 @@ class Field(object):
return self._choices
choices = property(_get_choices)
def formfield(self):
def formfield(self, initial=None):
"Returns a django.newforms.Field instance for this database Field."
from django.newforms import CharField
# TODO: This is just a temporary default during development.
return CharField(label=capfirst(self.verbose_name))
return forms.CharField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
def value_from_object(self, obj):
"Returns the value of this field in the given model instance."
return getattr(obj, self.attname)
class AutoField(Field):
empty_strings_allowed = False
@ -379,6 +383,9 @@ class AutoField(Field):
super(AutoField, self).contribute_to_class(cls, name)
cls._meta.has_auto_field = True
def formfield(self, initial=None):
return None
class BooleanField(Field):
def __init__(self, *args, **kwargs):
kwargs['blank'] = True
@ -393,6 +400,9 @@ class BooleanField(Field):
def get_manipulator_field_objs(self):
return [oldforms.CheckboxField]
def formfield(self, initial=None):
return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class CharField(Field):
def get_manipulator_field_objs(self):
return [oldforms.TextField]
@ -407,6 +417,9 @@ class CharField(Field):
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
return str(value)
def formfield(self, initial=None):
return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
# TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField):
def get_manipulator_field_objs(self):
@ -484,6 +497,9 @@ class DateField(Field):
val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
def formfield(self, initial=None):
return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class DateTimeField(DateField):
def to_python(self, value):
if isinstance(value, datetime.datetime):
@ -553,6 +569,9 @@ class DateTimeField(DateField):
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
def formfield(self, initial=None):
return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class EmailField(CharField):
def __init__(self, *args, **kwargs):
kwargs['maxlength'] = 75
@ -567,6 +586,9 @@ class EmailField(CharField):
def validate(self, field_data, all_data):
validators.isValidEmail(field_data, all_data)
def formfield(self, initial=None):
return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class FileField(Field):
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to
@ -699,6 +721,9 @@ class IntegerField(Field):
def get_manipulator_field_objs(self):
return [oldforms.IntegerField]
def formfield(self, initial=None):
return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class IPAddressField(Field):
def __init__(self, *args, **kwargs):
kwargs['maxlength'] = 15
@ -799,15 +824,22 @@ class TimeField(Field):
val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
def formfield(self, initial=None):
return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class URLField(Field):
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
if verify_exists:
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
self.verify_exists = verify_exists
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [oldforms.URLField]
def formfield(self, initial=None):
return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name), initial=initial)
class USStateField(Field):
def get_manipulator_field_objs(self):
return [oldforms.USStateField]

View File

@ -2,10 +2,12 @@ from django.db import backend, transaction
from django.db.models import signals, get_model
from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class
from django.db.models.related import RelatedObject
from django.utils.text import capfirst
from django.utils.translation import gettext_lazy, string_concat, ngettext
from django.utils.functional import curry
from django.core import validators
from django import oldforms
from django import newforms as forms
from django.dispatch import dispatcher
# For Python 2.3
@ -256,8 +258,7 @@ class ForeignRelatedObjectsDescriptor(object):
# Otherwise, just move the named objects into the set.
if self.related.field.null:
manager.clear()
for obj in value:
manager.add(obj)
manager.add(*value)
def create_many_related_manager(superclass):
"""Creates a manager that subclasses 'superclass' (which is a Manager)
@ -318,6 +319,12 @@ def create_many_related_manager(superclass):
# *objs - objects to add
from django.db import connection
# If there aren't any objects, there is nothing to do.
if objs:
# Check that all the objects are of the right type
for obj in objs:
if not isinstance(obj, self.model):
raise ValueError, "objects to add() must be %s instances" % self.model._meta.object_name
# Add the newly created or already existing objects to the join table.
# First find out which items are already added, to avoid adding them twice
new_ids = set([obj._get_pk_val() for obj in objs])
@ -344,15 +351,19 @@ def create_many_related_manager(superclass):
# *objs - objects to remove
from django.db import connection
# If there aren't any objects, there is nothing to do.
if objs:
# Check that all the objects are of the right type
for obj in objs:
if not isinstance(obj, self.model):
raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
# Remove the specified objects from the join table
old_ids = set([obj._get_pk_val() for obj in objs])
cursor = connection.cursor()
for obj in objs:
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \
(self.join_table, source_col_name, target_col_name),
[self._pk_val, obj._get_pk_val()])
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
(self.join_table, source_col_name,
target_col_name, ",".join(['%s'] * len(old_ids))),
[self._pk_val] + list(old_ids))
transaction.commit_unless_managed()
def _clear_items(self, source_col_name):
@ -405,8 +416,7 @@ class ManyRelatedObjectsDescriptor(object):
manager = self.__get__(instance)
manager.clear()
for obj in value:
manager.add(obj)
manager.add(*value)
class ReverseManyRelatedObjectsDescriptor(object):
# This class provides the functionality that makes the related-object
@ -447,8 +457,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
manager = self.__get__(instance)
manager.clear()
for obj in value:
manager.add(obj)
manager.add(*value)
class ForeignKey(RelatedField, Field):
empty_strings_allowed = False
@ -539,6 +548,9 @@ class ForeignKey(RelatedField, Field):
def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
def formfield(self, initial=None):
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class OneToOneField(RelatedField, IntegerField):
def __init__(self, to, to_field=None, **kwargs):
try:
@ -600,6 +612,9 @@ class OneToOneField(RelatedField, IntegerField):
if not cls._meta.one_to_one_field:
cls._meta.one_to_one_field = self
def formfield(self, initial=None):
return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class ManyToManyField(RelatedField, Field):
def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
@ -709,6 +724,13 @@ class ManyToManyField(RelatedField, Field):
def set_attributes_from_rel(self):
pass
def value_from_object(self, obj):
"Returns the value of this field in the given model instance."
return getattr(obj, self.attname).all()
def formfield(self, initial=None):
return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial)
class ManyToOneRel(object):
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,

File diff suppressed because it is too large Load Diff

View File

@ -13,5 +13,5 @@ TODO:
from util import ValidationError
from widgets import *
from fields import *
from forms import Form
from forms import *
from models import *

View File

@ -0,0 +1 @@
from widgets import *

View File

@ -0,0 +1,59 @@
"""
Extra HTML Widget classes
"""
from django.newforms.widgets import Widget, Select
from django.utils.dates import MONTHS
import datetime
__all__ = ('SelectDateWidget',)
class SelectDateWidget(Widget):
"""
A Widget that splits date input into three <select> boxes.
This also serves as an example of a Widget that has more than one HTML
element and hence implements value_from_datadict.
"""
month_field = '%s_month'
day_field = '%s_day'
year_field = '%s_year'
def __init__(self, attrs=None, years=None):
# years is an optional list/tuple of years to use in the "year" select box.
self.attrs = attrs or {}
if years:
self.years = years
else:
this_year = datetime.date.today().year
self.years = range(this_year, this_year+10)
def render(self, name, value, attrs=None):
try:
value = datetime.date(*map(int, value.split('-')))
year_val, month_val, day_val = value.year, value.month, value.day
except (AttributeError, TypeError, ValueError):
year_val = month_val = day_val = None
output = []
month_choices = MONTHS.items()
month_choices.sort()
select_html = Select(choices=month_choices).render(self.month_field % name, month_val)
output.append(select_html)
day_choices = [(i, i) for i in range(1, 32)]
select_html = Select(choices=day_choices).render(self.day_field % name, day_val)
output.append(select_html)
year_choices = [(i, i) for i in self.years]
select_html = Select(choices=year_choices).render(self.year_field % name, year_val)
output.append(select_html)
return u'\n'.join(output)
def value_from_datadict(self, data, name):
y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name)
if y and m and d:
return '%s-%s-%s' % (y, m, d)
return None

View File

@ -33,8 +33,21 @@ class Field(object):
# Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0
def __init__(self, required=True, widget=None, label=None):
self.required, self.label = required, label
def __init__(self, required=True, widget=None, label=None, initial=None):
# required -- Boolean that specifies whether the field is required.
# True by default.
# widget -- A Widget class, or instance of a Widget class, that should be
# used for this Field when displaying it. Each Field has a default
# Widget that it'll use if you don't specify this. In most cases,
# the default widget is TextInput.
# label -- A verbose name for this field, for use in displaying this field in
# a form. By default, Django will use a "pretty" version of the form
# field name, if the Field is part of a Form.
# initial -- A value to use in this Field's initial display. This value is
# *not* used as a fallback if data isn't given.
if label is not None:
label = smart_unicode(label)
self.required, self.label, self.initial = required, label, initial
widget = widget or self.widget
if isinstance(widget, type):
widget = widget()
@ -70,14 +83,17 @@ class Field(object):
return {}
class CharField(Field):
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None):
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None):
self.max_length, self.min_length = max_length, min_length
Field.__init__(self, required, widget, label)
Field.__init__(self, required, widget, label, initial)
def clean(self, value):
"Validates max_length and min_length. Returns a Unicode object."
Field.clean(self, value)
if value in EMPTY_VALUES: value = u''
if value in EMPTY_VALUES:
value = u''
if not self.required:
return value
value = smart_unicode(value)
if self.max_length is not None and len(value) > self.max_length:
raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length)
@ -90,6 +106,10 @@ class CharField(Field):
return {'maxlength': str(self.max_length)}
class IntegerField(Field):
def __init__(self, max_value=None, min_value=None, required=True, widget=None, label=None, initial=None):
self.max_value, self.min_value = max_value, min_value
Field.__init__(self, required, widget, label, initial)
def clean(self, value):
"""
Validates that int() can be called on the input. Returns the result
@ -99,9 +119,14 @@ class IntegerField(Field):
if not self.required and value in EMPTY_VALUES:
return u''
try:
return int(value)
value = int(value)
except (ValueError, TypeError):
raise ValidationError(gettext(u'Enter a whole number.'))
if self.max_value is not None and value > self.max_value:
raise ValidationError(gettext(u'Ensure this value is less than or equal to %s.') % self.max_value)
if self.min_value is not None and value < self.min_value:
raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value)
return value
DEFAULT_DATE_INPUT_FORMATS = (
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
@ -112,8 +137,8 @@ DEFAULT_DATE_INPUT_FORMATS = (
)
class DateField(Field):
def __init__(self, input_formats=None, required=True, widget=None, label=None):
Field.__init__(self, required, widget, label)
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
Field.__init__(self, required, widget, label, initial)
self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
def clean(self, value):
@ -141,8 +166,8 @@ DEFAULT_TIME_INPUT_FORMATS = (
)
class TimeField(Field):
def __init__(self, input_formats=None, required=True, widget=None, label=None):
Field.__init__(self, required, widget, label)
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
Field.__init__(self, required, widget, label, initial)
self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
def clean(self, value):
@ -175,8 +200,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
)
class DateTimeField(Field):
def __init__(self, input_formats=None, required=True, widget=None, label=None):
Field.__init__(self, required, widget, label)
def __init__(self, input_formats=None, required=True, widget=None, label=None, initial=None):
Field.__init__(self, required, widget, label, initial)
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
def clean(self, value):
@ -199,16 +224,18 @@ class DateTimeField(Field):
raise ValidationError(gettext(u'Enter a valid date/time.'))
class RegexField(Field):
def __init__(self, regex, error_message=None, required=True, widget=None, label=None):
def __init__(self, regex, max_length=None, min_length=None, error_message=None,
required=True, widget=None, label=None, initial=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, label)
Field.__init__(self, required, widget, label, initial)
if isinstance(regex, basestring):
regex = re.compile(regex)
self.regex = regex
self.max_length, self.min_length = max_length, min_length
self.error_message = error_message or gettext(u'Enter a valid value.')
def clean(self, value):
@ -221,6 +248,10 @@ class RegexField(Field):
value = smart_unicode(value)
if not self.required and value == u'':
return value
if self.max_length is not None and len(value) > 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(gettext(u'Ensure this value has at least %d characters.') % self.min_length)
if not self.regex.search(value):
raise ValidationError(self.error_message)
return value
@ -231,8 +262,8 @@ email_re = re.compile(
r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
class EmailField(RegexField):
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)
def __init__(self, max_length=None, min_length=None, required=True, widget=None, label=None, initial=None):
RegexField.__init__(self, email_re, max_length, min_length, gettext(u'Enter a valid e-mail address.'), required, widget, label, initial)
url_re = re.compile(
r'^https?://' # http:// or https://
@ -248,9 +279,9 @@ except ImportError:
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
class URLField(RegexField):
def __init__(self, required=True, verify_exists=False, widget=None, label=None,
validator_user_agent=URL_VALIDATOR_USER_AGENT):
RegexField.__init__(self, url_re, gettext(u'Enter a valid URL.'), required, widget, label)
def __init__(self, max_length=None, min_length=None, required=True, verify_exists=False, widget=None, label=None,
initial=None, validator_user_agent=URL_VALIDATOR_USER_AGENT):
RegexField.__init__(self, url_re, max_length, min_length, gettext(u'Enter a valid URL.'), required, widget, label, initial)
self.verify_exists = verify_exists
self.user_agent = validator_user_agent
@ -284,10 +315,10 @@ class BooleanField(Field):
return bool(value)
class ChoiceField(Field):
def __init__(self, choices=(), required=True, widget=Select, label=None):
def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None):
if isinstance(widget, type):
widget = widget(choices=choices)
Field.__init__(self, required, widget, label)
Field.__init__(self, required, widget, label, initial)
self.choices = choices
def clean(self, value):
@ -305,8 +336,8 @@ class ChoiceField(Field):
return value
class MultipleChoiceField(ChoiceField):
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None):
ChoiceField.__init__(self, choices, required, widget, label)
def __init__(self, choices=(), required=True, widget=SelectMultiple, label=None, initial=None):
ChoiceField.__init__(self, choices, required, widget, label, initial)
def clean(self, value):
"""
@ -330,8 +361,8 @@ class MultipleChoiceField(ChoiceField):
return new_value
class ComboField(Field):
def __init__(self, fields=(), required=True, widget=None, label=None):
Field.__init__(self, required, widget, label)
def __init__(self, fields=(), required=True, widget=None, label=None, initial=None):
Field.__init__(self, required, widget, label, initial)
# Set 'required' to False on the individual fields, because the
# required validation will be handled by ComboField, not by those
# individual fields.

View File

@ -8,6 +8,8 @@ from fields import Field
from widgets import TextInput, Textarea, HiddenInput
from util import StrAndUnicode, ErrorDict, ErrorList, ValidationError
__all__ = ('BaseForm', 'Form')
NON_FIELD_ERRORS = '__all__'
def pretty_name(name):
@ -97,7 +99,8 @@ class BaseForm(StrAndUnicode):
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})
label = bf.label and bf.label_tag(escape(bf.label + ':')) or ''
output.append(normal_row % {'errors': bf_errors, 'label': label, 'field': bf})
if top_errors:
output.insert(0, error_row % top_errors)
if hidden_fields: # Insert any hidden fields in the last row.
@ -112,7 +115,7 @@ class BaseForm(StrAndUnicode):
def as_table(self):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
return self._html_output(u'<tr><th>%(label)s</th><td>%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', True)
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', False)
def as_ul(self):
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
@ -185,7 +188,10 @@ class BoundField(StrAndUnicode):
self.field = field
self.name = name
self.html_name = form.add_prefix(name)
self.label = self.field.label or pretty_name(name)
if self.field.label is None:
self.label = pretty_name(name)
else:
self.label = self.field.label
def __unicode__(self):
"Renders this field as an HTML widget."
@ -212,7 +218,11 @@ class BoundField(StrAndUnicode):
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.html_name, self.data, attrs=attrs)
if self.form.ignore_errors:
data = self.field.initial
else:
data = self.data
return widget.render(self.html_name, data, attrs=attrs)
def as_text(self, attrs=None):
"""
@ -231,10 +241,10 @@ class BoundField(StrAndUnicode):
return self.as_widget(HiddenInput(), attrs)
def _data(self):
"Returns the data for this BoundField, or None if it wasn't given."
if self.field.widget.requires_data_list and isinstance(self.form.data, MultiValueDict):
return self.form.data.getlist(self.html_name)
return self.form.data.get(self.html_name, None)
"""
Returns the data for this BoundField, or None if it wasn't given.
"""
return self.field.widget.value_from_datadict(self.form.data, self.html_name)
data = property(_data)
def label_tag(self, contents=None):

View File

@ -5,13 +5,69 @@ and database field objects.
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
__all__ = ('form_for_model', 'form_for_fields')
__all__ = ('form_for_model', 'form_for_instance', 'form_for_fields')
def form_for_model(model):
"Returns a Form class for the given Django model class."
def create(self, save=True):
"""
Creates and returns model instance according to self.clean_data.
This method is created for any form_for_model Form.
"""
if self.errors:
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
obj = self._model(**self.clean_data)
if save:
obj.save()
return obj
def make_apply_changes(opts, instance):
"Returns the apply_changes() method for a form_for_instance Form."
from django.db import models
def apply_changes(self, save=True):
if self.errors:
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
clean_data = self.clean_data
for f in opts.fields + opts.many_to_many:
if isinstance(f, models.AutoField):
continue
setattr(instance, f.attname, clean_data[f.name])
if save:
instance.save()
return instance
return apply_changes
def form_for_model(model, form=BaseForm):
"""
Returns a Form class for the given Django model class.
Provide 'form' if you want to use a custom BaseForm subclass.
"""
opts = model._meta
fields = SortedDictFromList([(f.name, f.formfield()) for f in opts.fields + opts.many_to_many])
return type(opts.object_name + 'Form', (BaseForm,), {'fields': fields, '_model_opts': opts})
field_list = []
for f in opts.fields + opts.many_to_many:
formfield = f.formfield()
if formfield:
field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list)
return type(opts.object_name + 'Form', (form,), {'fields': fields, '_model': model, 'create': create})
def form_for_instance(instance, form=BaseForm):
"""
Returns a Form class for the given Django model instance.
Provide 'form' if you want to use a custom BaseForm subclass.
"""
model = instance.__class__
opts = model._meta
field_list = []
for f in opts.fields + opts.many_to_many:
current_value = f.value_from_object(instance)
formfield = f.formfield(initial=current_value)
if formfield:
field_list.append((f.name, formfield))
fields = SortedDictFromList(field_list)
return type(opts.object_name + 'InstanceForm', (form,),
{'fields': fields, '_model': model, 'apply_changes': make_apply_changes(opts, instance)})
def form_for_fields(field_list):
"Returns a Form class for the given list of Django database field instances."

View File

@ -23,13 +23,18 @@ except NameError:
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
class Widget(object):
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
def __init__(self, attrs=None):
self.attrs = attrs or {}
def render(self, name, value):
def render(self, name, value, attrs=None):
"""
Returns this Widget rendered as HTML, as a Unicode string.
The 'value' given is not guaranteed to be valid input, so subclass
implementations should program defensively.
"""
raise NotImplementedError
def build_attrs(self, extra_attrs=None, **kwargs):
@ -130,7 +135,6 @@ class Select(Widget):
return u'\n'.join(output)
class SelectMultiple(Widget):
requires_data_list = True
def __init__(self, attrs=None, choices=()):
# choices can be any iterable
self.attrs = attrs or {}
@ -185,6 +189,10 @@ class RadioFieldRenderer(StrAndUnicode):
for i, choice in enumerate(self.choices):
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
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])

View File

@ -11,7 +11,7 @@ Usage:
>>>
"""
from django.utils.dates import MONTHS, MONTHS_AP, WEEKDAYS
from django.utils.dates import MONTHS, MONTHS_3, MONTHS_AP, WEEKDAYS
from django.utils.tzinfo import LocalTimezone
from calendar import isleap, monthrange
import re, time
@ -147,7 +147,7 @@ class DateFormat(TimeFormat):
def M(self):
"Month, textual, 3 letters; e.g. 'Jan'"
return MONTHS[self.data.month][0:3]
return MONTHS_3[self.data.month].title()
def n(self):
"Month without leading zeros; i.e. '1' to '12'"

View File

@ -8,17 +8,28 @@ capfirst = lambda x: x and x[0].upper() + x[1:]
def wrap(text, width):
"""
A word-wrap function that preserves existing line breaks and most spaces in
the text. Expects that existing line breaks are posix newlines (\n).
See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
the text. Expects that existing line breaks are posix newlines.
"""
return reduce(lambda line, word, width=width: '%s%s%s' %
(line,
' \n'[(len(line[line.rfind('\n')+1:])
+ len(word.split('\n',1)[0]
) >= width)],
word),
text.split(' ')
)
def _generator():
it = iter(text.split(' '))
word = it.next()
yield word
pos = len(word) - word.rfind('\n') - 1
for word in it:
if "\n" in word:
lines = word.splitlines()
else:
lines = (word,)
pos += len(lines[0]) + 1
if pos > width:
yield '\n'
pos = len(lines[-1])
else:
yield ' '
if len(lines) > 1:
pos = len(lines[-1])
yield word
return "".join(_generator())
def truncate_words(s, num):
"Truncates a string after a certain number of words."

View File

@ -1,5 +1,5 @@
=====================================
Cross Site Request Forgery Protection
Cross Site Request Forgery protection
=====================================
The CsrfMiddleware class provides easy-to-use protection against
@ -12,11 +12,11 @@ The first defense against CSRF attacks is to ensure that GET requests
are side-effect free. POST requests can then be protected by adding this
middleware into your list of installed middleware.
.. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF
How to use it
=============
Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to
your list of middleware classes, ``MIDDLEWARE_CLASSES``. It needs to process
the response after the SessionMiddleware, so must come before it in the
@ -25,6 +25,7 @@ happen to the response, so it must come after GZipMiddleware in the list.
How it works
============
CsrfMiddleware does two things:
1. It modifies outgoing requests by adding a hidden form field to all
@ -55,6 +56,7 @@ are modified.
Limitations
===========
CsrfMiddleware requires Django's session framework to work. If you have
a custom authentication system that manually sets cookies and the like,
it won't help you.
@ -65,4 +67,3 @@ you might bypass the filter that adds the hidden field to the form,
in which case form submission will always fail. It may still be possible
to use the middleware, provided you can find some way to get the
CSRF token and ensure that is included when your form is submitted.

View File

@ -143,9 +143,9 @@ or ``UPDATE`` SQL statements. Specifically, when you call ``save()``, Django
follows this algorithm:
* If the object's primary key attribute is set to a value that evaluates to
``False`` (such as ``None`` or the empty string), Django executes a
``SELECT`` query to determine whether a record with the given primary key
already exists.
``True`` (i.e., a value other than ``None`` or the empty string), Django
executes a ``SELECT`` query to determine whether a record with the given
primary key already exists.
* If the record with the given primary key does already exist, Django
executes an ``UPDATE`` query.
* If the object's primary key attribute is *not* set, or if it's set but a

View File

@ -22,7 +22,6 @@ what the name of the database is. Do that by editing these settings in your
* `DATABASE_ENGINE`_
* `DATABASE_USER`_
* `DATABASE_PASSWORD`_
* `DATABASE_NAME`_
* `DATABASE_HOST`_
* `DATABASE_PORT`_
@ -31,7 +30,6 @@ what the name of the database is. Do that by editing these settings in your
.. _DATABASE_ENGINE: http://www.djangoproject.com/documentation/settings/#database-engine
.. _DATABASE_USER: http://www.djangoproject.com/documentation/settings/#database-user
.. _DATABASE_PASSWORD: http://www.djangoproject.com/documentation/settings/#database-password
.. _DATABASE_NAME: http://www.djangoproject.com/documentation/settings/#database-name
.. _DATABASE_HOST: http://www.djangoproject.com/documentation/settings/#database-host
.. _DATABASE_PORT: http://www.djangoproject.com/documentation/settings/#database-port

View File

@ -282,6 +282,13 @@ example, in the ``ContactForm`` example, the fields are defined in the order
``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML
output, just change the order in which those fields are listed in the class.
Using forms to validate data
----------------------------
In addition to HTML form display, a ``Form`` class is responsible for
validating data.
More coming soon
================
@ -290,9 +297,6 @@ http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/t
-- the unit tests for ``django.newforms``. This can give you a good idea of
what's possible.
Using forms to validate data
----------------------------
Using forms with templates
==========================

View File

@ -63,9 +63,9 @@ Via the Python API
------------------
Redirects are represented by a standard `Django model`_, which lives in
`django/contrib/redirects/models/redirects.py`_. You can access redirect
`django/contrib/redirects/models.py`_. You can access redirect
objects via the `Django database API`_.
.. _Django model: http://www.djangoproject.com/documentation/model_api/
.. _django/contrib/redirects/models/redirects.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models/redirects.py
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
.. _Django database API: http://www.djangoproject.com/documentation/db_api/

View File

@ -12,6 +12,9 @@ class Article(models.Model):
class Meta:
ordering = ('pub_date',)
class Meta:
ordering = ('pub_date','headline')
def __str__(self):
return self.headline
@ -247,7 +250,7 @@ datetime.datetime(2005, 7, 28, 0, 0)
# Slices (without step) are lazy:
>>> Article.objects.all()[0:5].filter()
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>]
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>]
# Slicing again works:
>>> Article.objects.all()[0:5][0:2]
@ -255,17 +258,17 @@ datetime.datetime(2005, 7, 28, 0, 0)
>>> Article.objects.all()[0:5][:2]
[<Article: Area woman programs in Python>, <Article: Second article>]
>>> Article.objects.all()[0:5][4:]
[<Article: Article 6>]
[<Article: Default headline>]
>>> Article.objects.all()[0:5][5:]
[]
# Some more tests!
>>> Article.objects.all()[2:][0:2]
[<Article: Third article>, <Article: Fourth article>]
[<Article: Third article>, <Article: Article 6>]
>>> Article.objects.all()[2:][:2]
[<Article: Third article>, <Article: Fourth article>]
[<Article: Third article>, <Article: Article 6>]
>>> Article.objects.all()[2:][2:3]
[<Article: Article 6>]
[<Article: Default headline>]
# Note that you can't use 'offset' without 'limit' (on some dbs), so this doesn't work:
>>> Article.objects.all()[2:]
@ -314,7 +317,7 @@ AttributeError: Manager isn't accessible via Article instances
# Bulk delete test: How many objects before and after the delete?
>>> Article.objects.all()
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>, <Article: Fourth article>, <Article: Article 7>, <Article: Updated article 8>]
>>> Article.objects.filter(id__lte=4).delete()
>>> Article.objects.all()
[<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]

View File

@ -231,4 +231,16 @@ __test__ = {'API_TESTS':"""
>>> p1.article_set.all()
[<Article: NASA uses Python>]
# An alternate to calling clear() is to assign the empty set
>>> p1.article_set = []
>>> p1.article_set.all()
[]
>>> a2.publications = [p1, new_publication]
>>> a2.publications.all()
[<Publication: Highlights for Children>, <Publication: The Python Journal>]
>>> a2.publications = []
>>> a2.publications.all()
[]
"""}

View File

@ -2,13 +2,26 @@
34. Generating HTML forms from models
Django provides shortcuts for creating Form objects from a model class.
The function django.newforms.form_for_model() takes a model class and returns
a Form that is tied to the model. This Form works just like any other Form,
with one additional method: create(). The create() method creates an instance
of the model and returns that newly created instance. It saves the instance to
the database if create(save=True), which is default. If you pass
create(save=False), then you'll get the object without saving it.
"""
from django.db import models
class Category(models.Model):
name = models.CharField(maxlength=20)
url = models.CharField('The URL', maxlength=20)
url = models.CharField('The URL', maxlength=40)
def __str__(self):
return self.name
class Writer(models.Model):
name = models.CharField(maxlength=50)
def __str__(self):
return self.name
@ -16,29 +29,156 @@ class Category(models.Model):
class Article(models.Model):
headline = models.CharField(maxlength=50)
pub_date = models.DateTimeField()
categories = models.ManyToManyField(Category)
writer = models.ForeignKey(Writer)
categories = models.ManyToManyField(Category, blank=True)
def __str__(self):
return self.headline
__test__ = {'API_TESTS': """
>>> from django.newforms import form_for_model
>>> from django.newforms import form_for_model, form_for_instance, BaseForm
>>> import datetime
>>> Category.objects.all()
[]
>>> CategoryForm = form_for_model(Category)
>>> f = CategoryForm()
>>> print f
<tr><th><label for="id_id">ID:</label></th><td><input type="text" name="id" id="id_id" /></td></tr>
<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" /></td></tr>
<tr><th><label for="id_url">The URL:</label></th><td><input type="text" name="url" id="id_url" /></td></tr>
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
<tr><th><label for="id_url">The URL:</label></th><td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>
>>> print f.as_ul()
<li><label for="id_id">ID:</label> <input type="text" name="id" id="id_id" /></li>
<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li>
<li><label for="id_url">The URL:</label> <input type="text" name="url" id="id_url" /></li>
<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" /></li>
<li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></li>
>>> print f['name']
<input type="text" name="name" id="id_name" />
<input id="id_name" type="text" name="name" maxlength="20" />
>>> f = CategoryForm(auto_id=False)
>>> print f.as_ul()
<li>ID: <input type="text" name="id" /></li>
<li>Name: <input type="text" name="name" /></li>
<li>The URL: <input type="text" name="url" /></li>
<li>Name: <input type="text" name="name" maxlength="20" /></li>
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
>>> f.errors
{}
>>> f.clean_data
{'url': u'entertainment', 'name': u'Entertainment'}
>>> obj = f.create()
>>> obj
<Category: Entertainment>
>>> Category.objects.all()
[<Category: Entertainment>]
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
>>> f.errors
{}
>>> f.clean_data
{'url': u'test', 'name': u"It's a test"}
>>> obj = f.create()
>>> obj
<Category: It's a test>
>>> Category.objects.all()
[<Category: Entertainment>, <Category: It's a test>]
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
>>> f.errors
{}
>>> f.clean_data
{'url': u'third', 'name': u'Third test'}
>>> obj = f.create(save=False)
>>> obj
<Category: Third test>
>>> Category.objects.all()
[<Category: Entertainment>, <Category: It's a test>]
>>> obj.save()
>>> Category.objects.all()
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
>>> f = CategoryForm({'name': '', 'url': 'foo'})
>>> f.errors
{'name': [u'This field is required.']}
>>> f.clean_data
>>> f.create()
Traceback (most recent call last):
...
ValueError: The Category could not be created because the data didn't validate.
>>> f = CategoryForm({'name': '', 'url': 'foo'})
>>> f.create()
Traceback (most recent call last):
...
ValueError: The Category could not be created because the data didn't validate.
Create a couple of Writers.
>>> w = Writer(name='Mike Royko')
>>> w.save()
>>> w = Writer(name='Bob Woodward')
>>> w.save()
ManyToManyFields are represented by a MultipleChoiceField, and ForeignKeys are
represented by a ChoiceField.
>>> ArticleForm = form_for_model(Article)
>>> f = ArticleForm(auto_id=False)
>>> print f
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
<tr><th>Writer:</th><td><select name="writer">
<option value="" selected="selected">---------</option>
<option value="1">Mike Royko</option>
<option value="2">Bob Woodward</option>
</select></td></tr>
<tr><th>Categories:</th><td><select multiple="multiple" name="categories">
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
</select></td></tr>
You can pass a custom Form class to form_for_model. Make sure it's a
subclass of BaseForm, not Form.
>>> class CustomForm(BaseForm):
... def say_hello(self):
... print 'hello'
>>> CategoryForm = form_for_model(Category, form=CustomForm)
>>> f = CategoryForm()
>>> f.say_hello()
hello
Use form_for_instance to create a Form from a model instance. There are two
differences between this Form and one created via form_for_model. First, the
object's current values are inserted as 'initial' data in each Field. Second,
the Form gets an apply_changes() method instead of a create() method.
>>> w = Writer.objects.get(name='Mike Royko')
>>> RoykoForm = form_for_instance(w)
>>> f = RoykoForm(auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /></td></tr>
>>> art = Article(headline='Test article', pub_date=datetime.date(1988, 1, 4), writer=w)
>>> art.save()
>>> art.id
1
>>> TestArticleForm = form_for_instance(art)
>>> f = TestArticleForm(auto_id=False)
>>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
<li>Writer: <select name="writer">
<option value="">---------</option>
<option value="1" selected="selected">Mike Royko</option>
<option value="2">Bob Woodward</option>
</select></li>
<li>Categories: <select multiple="multiple" name="categories">
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
</select></li>
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1'})
>>> f.is_valid()
True
>>> new_art = f.apply_changes()
>>> new_art.id
1
>>> new_art = Article.objects.get(id=1)
>>> new_art.headline
'New headline'
"""}

View File

@ -514,6 +514,25 @@ beatle J P Paul False
beatle J G George False
beatle J R Ringo False
A RadioFieldRenderer object also allows index access to individual RadioInput
objects.
>>> w = RadioSelect()
>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> print r[1]
<label><input type="radio" name="beatle" value="P" /> Paul</label>
>>> print r[0]
<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
>>> r[0].is_checked()
True
>>> r[1].is_checked()
False
>>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label
('beatle', u'J', 'P', 'Paul')
>>> r[10]
Traceback (most recent call last):
...
IndexError: list index out of range
# CheckboxSelectMultiple Widget ###############################################
>>> w = CheckboxSelectMultiple()
@ -639,6 +658,8 @@ Each Field's __init__() takes at least these parameters:
label -- A verbose name for this field, for use in displaying this field in
a form. By default, Django will use a "pretty" version of the form
field name, if the Field is part of a Form.
initial -- A value to use in this Field's initial display. This value is
*not* used as a fallback if data isn't given.
Other than that, the Field subclasses have class-specific options for
__init__(). For example, CharField has a max_length option.
@ -687,9 +708,21 @@ ValidationError: [u'Ensure this value has at most 10 characters.']
CharField accepts an optional min_length parameter:
>>> f = CharField(min_length=10, required=False)
>>> f.clean('')
u''
>>> f.clean('12345')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at least 10 characters.']
>>> f.clean('1234567890')
u'1234567890'
>>> f.clean('1234567890a')
u'1234567890a'
>>> f = CharField(min_length=10, required=True)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean('12345')
Traceback (most recent call last):
...
@ -757,6 +790,71 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter a whole number.']
IntegerField accepts an optional max_value parameter:
>>> f = IntegerField(max_value=10)
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(1)
1
>>> f.clean(10)
10
>>> f.clean(11)
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value is less than or equal to 10.']
>>> f.clean('10')
10
>>> f.clean('11')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value is less than or equal to 10.']
IntegerField accepts an optional min_value parameter:
>>> f = IntegerField(min_value=10)
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(1)
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value is greater than or equal to 10.']
>>> f.clean(10)
10
>>> f.clean(11)
11
>>> f.clean('10')
10
>>> f.clean('11')
11
min_value and max_value can be used together:
>>> f = IntegerField(min_value=10, max_value=20)
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(1)
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value is greater than or equal to 10.']
>>> f.clean(10)
10
>>> f.clean(11)
11
>>> f.clean('10')
10
>>> f.clean('11')
11
>>> f.clean(20)
20
>>> f.clean(21)
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value is less than or equal to 20.']
# DateField ###################################################################
>>> import datetime
@ -1002,7 +1100,7 @@ Traceback (most recent call last):
ValidationError: [u'Enter a valid value.']
RegexField takes an optional error_message argument:
>>> f = RegexField('^\d\d\d\d$', 'Enter a four-digit number.')
>>> f = RegexField('^\d\d\d\d$', error_message='Enter a four-digit number.')
>>> f.clean('1234')
u'1234'
>>> f.clean('123')
@ -1014,6 +1112,29 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter a four-digit number.']
RegexField also access min_length and max_length parameters, for convenience.
>>> f = RegexField('^\d+$', min_length=5, max_length=10)
>>> f.clean('123')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at least 5 characters.']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at least 5 characters.']
>>> f.clean('12345')
u'12345'
>>> f.clean('1234567890')
u'1234567890'
>>> f.clean('12345678901')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at most 10 characters.']
>>> f.clean('12345a')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid value.']
# EmailField ##################################################################
>>> f = EmailField()
@ -1060,6 +1181,19 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter a valid e-mail address.']
EmailField also access min_length and max_length parameters, for convenience.
>>> f = EmailField(min_length=10, max_length=15)
>>> f.clean('a@foo.com')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at least 10 characters.']
>>> f.clean('alf@foo.com')
u'alf@foo.com'
>>> f.clean('alf123456788@foo.com')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at most 15 characters.']
# URLField ##################################################################
>>> f = URLField()
@ -1152,6 +1286,19 @@ Traceback (most recent call last):
...
ValidationError: [u'This URL appears to be a broken link.']
EmailField also access min_length and max_length parameters, for convenience.
>>> f = URLField(min_length=15, max_length=20)
>>> f.clean('http://f.com')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at least 15 characters.']
>>> f.clean('http://example.com')
u'http://example.com'
>>> f.clean('http://abcdefghijklmnopqrstuvwxyz.com')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at most 20 characters.']
# BooleanField ################################################################
>>> f = BooleanField()
@ -1398,19 +1545,13 @@ Empty dictionaries are valid, too.
>>> p.is_valid()
False
>>> print p
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr>
<tr><th><label for="id_birthday">Birthday:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="birthday" id="id_birthday" /></td></tr>
>>> print p.as_table()
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><th><label for="id_birthday">Birthday:</label></th><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
<tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr>
<tr><th><label for="id_birthday">Birthday:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="birthday" id="id_birthday" /></td></tr>
>>> print p.as_ul()
<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li>
@ -1799,12 +1940,9 @@ Form.clean() is required to return a dictionary of all clean data.
{}
>>> f = UserRegistration({}, auto_id=False)
>>> print f.as_table()
<tr><td colspan="2"><ul class="errorlist"><li>This field is required.</li></ul></td></tr>
<tr><th>Username:</th><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><th>Password1:</th><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><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr>
<tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr>
<tr><th>Password2:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" /></td></tr>
>>> f.errors
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
@ -1972,6 +2110,8 @@ in "attrs".
<li>Username: <input type="text" name="username" maxlength="10" /></li>
<li>Password: <input type="password" name="password" maxlength="10" /></li>
# Specifying labels ###########################################################
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.
@ -1985,6 +2125,81 @@ underscores converted to spaces, and the initial letter capitalized.
<li>Password1: <input type="password" name="password1" /></li>
<li>Password (again): <input type="password" name="password2" /></li>
A label can be a Unicode object or a bytestring with special characters.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label='ŠĐĆŽćžšđ')
... password = CharField(widget=PasswordInput, label=u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111')
>>> p = UserRegistration(auto_id=False)
>>> p.as_ul()
u'<li>\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: <input type="text" name="username" maxlength="10" /></li>\n<li>\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: <input type="password" name="password" /></li>'
If a label is set to the empty string for a field, that field won't get a label.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label='')
... password = CharField(widget=PasswordInput)
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
<li> <input type="text" name="username" maxlength="10" /></li>
<li>Password: <input type="password" name="password" /></li>
>>> p = UserRegistration(auto_id='id_%s')
>>> print p.as_ul()
<li> <input id="id_username" type="text" name="username" maxlength="10" /></li>
<li><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></li>
If label is None, Django will auto-create the label from the field name. This
is default behavior.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label=None)
... password = CharField(widget=PasswordInput)
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" maxlength="10" /></li>
<li>Password: <input type="password" name="password" /></li>
>>> p = UserRegistration(auto_id='id_%s')
>>> print p.as_ul()
<li><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></li>
<li><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></li>
# Initial data ################################################################
You can specify initial data for a field by using the 'initial' argument to a
Field class. This initial data is displayed when a Form is rendered with *no*
data. It is not displayed when a Form is rendered with any data (including an
empty dictionary). Also, the initial value is *not* used if data for a
particular required field isn't provided.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, initial='django')
... password = CharField(widget=PasswordInput)
Here, we're not submitting any data, so the initial value will be displayed.
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li>
<li>Password: <input type="password" name="password" /></li>
Here, we're submitting data, so the initial value will *not* be displayed.
>>> p = UserRegistration({}, auto_id=False)
>>> print p.as_ul()
<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
>>> p = UserRegistration({'username': u''}, auto_id=False)
>>> print p.as_ul()
<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
>>> p = UserRegistration({'username': u'foo'}, auto_id=False)
>>> print p.as_ul()
<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li>
<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
An 'initial' value is *not* used as a fallback if data is not provided. In this
example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'})
>>> p.errors
{'username': [u'This field is required.']}
>>> p.is_valid()
False
# Forms with prefixes #########################################################
Sometimes it's necessary to have multiple forms display on the same HTML page,
@ -2133,8 +2348,7 @@ Case 2: POST with erroneous data (a redisplayed form, with errors).
<form action="" method="post">
<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><th>Username:</th><td><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters.</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
</table>
@ -2257,6 +2471,141 @@ the list of errors is empty). You can also use it in {% if %} statements.
<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
<input type="submit" />
</form>
#################
# Extra widgets #
#################
The newforms library comes with some extra, higher-level Widget classes that
demonstrate some of the library's abilities.
# SelectDateWidget ############################################################
>>> from django.newforms.extras import SelectDateWidget
>>> w = SelectDateWidget()
>>> print w.render('mydate', '')
<select name="mydate_month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select name="mydate_day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
<option value="30">30</option>
<option value="31">31</option>
</select>
<select name="mydate_year">
<option value="2006">2006</option>
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
<option value="2010">2010</option>
<option value="2011">2011</option>
<option value="2012">2012</option>
<option value="2013">2013</option>
<option value="2014">2014</option>
<option value="2015">2015</option>
</select>
>>> w.render('mydate', None) == w.render('mydate', '')
True
>>> print w.render('mydate', '2010-04-15')
<select name="mydate_month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4" selected="selected">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select name="mydate_day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15" selected="selected">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
<option value="30">30</option>
<option value="31">31</option>
</select>
<select name="mydate_year">
<option value="2006">2006</option>
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
<option value="2010" selected="selected">2010</option>
<option value="2011">2011</option>
<option value="2012">2012</option>
<option value="2013">2013</option>
<option value="2014">2014</option>
<option value="2015">2015</option>
</select>
"""
if __name__ == "__main__":