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:
parent
f4aa493322
commit
58ba520f32
3
AUTHORS
3
AUTHORS
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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 %}
|
||||
|
@ -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 %}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
@ -480,10 +493,13 @@ class DateField(Field):
|
||||
def get_manipulator_field_objs(self):
|
||||
return [oldforms.DateField]
|
||||
|
||||
def flatten_data(self, follow, obj = None):
|
||||
def flatten_data(self, follow, obj=None):
|
||||
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]
|
||||
|
@ -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
@ -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 *
|
||||
|
1
django/newforms/extras/__init__.py
Normal file
1
django/newforms/extras/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from widgets import *
|
59
django/newforms/extras/widgets.py
Normal file
59
django/newforms/extras/widgets.py
Normal 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
|
@ -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.
|
||||
|
@ -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):
|
||||
|
@ -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."
|
||||
|
@ -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])
|
||||
|
@ -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'"
|
||||
|
@ -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."
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
==========================
|
||||
|
||||
|
@ -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/
|
||||
|
@ -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>]
|
||||
|
@ -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()
|
||||
[]
|
||||
|
||||
"""}
|
||||
|
@ -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'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'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'
|
||||
"""}
|
||||
|
@ -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__":
|
||||
|
Loading…
x
Reference in New Issue
Block a user