mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
newforms-admin: Merged to [5243]. There are 3 failing tests in regressiontests.serializers_regress.tests.SerializerTests, but they fail in trunk also.
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5244 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
415e84ad53
commit
4336591395
2
AUTHORS
2
AUTHORS
@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
flavio.curella@gmail.com
|
flavio.curella@gmail.com
|
||||||
Jure Cuhalev <gandalf@owca.info>
|
Jure Cuhalev <gandalf@owca.info>
|
||||||
dackze+django@gmail.com
|
dackze+django@gmail.com
|
||||||
|
David Danier <goliath.mailinglist@gmx.de>
|
||||||
Dirk Datzert <dummy@habmalnefrage.de>
|
Dirk Datzert <dummy@habmalnefrage.de>
|
||||||
Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/>
|
Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/>
|
||||||
dave@thebarproject.com
|
dave@thebarproject.com
|
||||||
@ -85,6 +86,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Alex Dedul
|
Alex Dedul
|
||||||
deric@monowerks.com
|
deric@monowerks.com
|
||||||
Max Derkachev <mderk@yandex.ru>
|
Max Derkachev <mderk@yandex.ru>
|
||||||
|
Jordan Dimov <s3x3y1@gmail.com>
|
||||||
dne@mayonnaise.net
|
dne@mayonnaise.net
|
||||||
Maximillian Dornseif <md@hudora.de>
|
Maximillian Dornseif <md@hudora.de>
|
||||||
Jeremy Dunck <http://dunck.us/>
|
Jeremy Dunck <http://dunck.us/>
|
||||||
|
@ -38,6 +38,7 @@ LANGUAGE_CODE = 'en-us'
|
|||||||
LANGUAGES = (
|
LANGUAGES = (
|
||||||
('ar', gettext_noop('Arabic')),
|
('ar', gettext_noop('Arabic')),
|
||||||
('bn', gettext_noop('Bengali')),
|
('bn', gettext_noop('Bengali')),
|
||||||
|
('bg', gettext_noop('Bulgarian')),
|
||||||
('ca', gettext_noop('Catalan')),
|
('ca', gettext_noop('Catalan')),
|
||||||
('cs', gettext_noop('Czech')),
|
('cs', gettext_noop('Czech')),
|
||||||
('cy', gettext_noop('Welsh')),
|
('cy', gettext_noop('Welsh')),
|
||||||
|
BIN
django/conf/locale/bg/LC_MESSAGES/django.mo
Normal file
BIN
django/conf/locale/bg/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2670
django/conf/locale/bg/LC_MESSAGES/django.po
Normal file
2670
django/conf/locale/bg/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
django/conf/locale/bg/LC_MESSAGES/djangojs.mo
Normal file
BIN
django/conf/locale/bg/LC_MESSAGES/djangojs.mo
Normal file
Binary file not shown.
106
django/conf/locale/bg/LC_MESSAGES/djangojs.po
Normal file
106
django/conf/locale/bg/LC_MESSAGES/djangojs.po
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# translation of djangojs.po to Bulgarian
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: djangojs\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2005-12-09 11:51+0100\n"
|
||||||
|
"PO-Revision-Date: 2007-05-12 17:51+0300\n"
|
||||||
|
"Last-Translator: Jordan Dimov <s3x3y1@gmail.com>\n"
|
||||||
|
"Language-Team: Bulgarian\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/SelectFilter2.js:33
|
||||||
|
#, perl-format
|
||||||
|
msgid "Available %s"
|
||||||
|
msgstr "Налични %s"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/SelectFilter2.js:41
|
||||||
|
msgid "Choose all"
|
||||||
|
msgstr "Избери всички"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/SelectFilter2.js:46
|
||||||
|
msgid "Add"
|
||||||
|
msgstr "Добави"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/SelectFilter2.js:48
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "Премахни"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/SelectFilter2.js:53
|
||||||
|
#, perl-format
|
||||||
|
msgid "Chosen %s"
|
||||||
|
msgstr "Избрахме %s"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/SelectFilter2.js:54
|
||||||
|
msgid "Select your choice(s) and click "
|
||||||
|
msgstr "Направете своя избор и щракнете "
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/SelectFilter2.js:59
|
||||||
|
msgid "Clear all"
|
||||||
|
msgstr "Изчисти всички"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/dateparse.js:26
|
||||||
|
#: contrib/admin/media/js/calendar.js:24
|
||||||
|
msgid ""
|
||||||
|
"January February March April May June July August September October November "
|
||||||
|
"December"
|
||||||
|
msgstr "Януари Февруари Март Април Май Юни Юли Август Септември Октомври Ноември Декември"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/dateparse.js:27
|
||||||
|
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
||||||
|
msgstr "Неделя Понеделник Вторник Сряда Четвъртък Петък Събота"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/calendar.js:25
|
||||||
|
msgid "S M T W T F S"
|
||||||
|
msgstr "Н П В С Ч П С"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
|
||||||
|
msgid "Now"
|
||||||
|
msgstr "Сега"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
|
||||||
|
msgid "Clock"
|
||||||
|
msgstr "Часовник"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
|
||||||
|
msgid "Choose a time"
|
||||||
|
msgstr "Избери време"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
|
||||||
|
msgid "Midnight"
|
||||||
|
msgstr "Полунощ"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
|
||||||
|
msgid "6 a.m."
|
||||||
|
msgstr "6 a.m."
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
|
||||||
|
msgid "Noon"
|
||||||
|
msgstr "По обяд"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Отказ"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
|
||||||
|
msgid "Today"
|
||||||
|
msgstr "Днес"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
|
||||||
|
msgid "Calendar"
|
||||||
|
msgstr "Календар"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
|
||||||
|
msgid "Yesterday"
|
||||||
|
msgstr "Вчера"
|
||||||
|
|
||||||
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
|
||||||
|
msgid "Tomorrow"
|
||||||
|
msgstr "Утре"
|
||||||
|
|
@ -24,7 +24,7 @@ Usage
|
|||||||
|
|
||||||
Subclass FormPreview and define a done() method:
|
Subclass FormPreview and define a done() method:
|
||||||
|
|
||||||
def done(self, request, clean_data):
|
def done(self, request, cleaned_data):
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
This method takes an HttpRequest object and a dictionary of the form data after
|
This method takes an HttpRequest object and a dictionary of the form data after
|
||||||
@ -113,7 +113,7 @@ class FormPreview(object):
|
|||||||
if f.is_valid():
|
if f.is_valid():
|
||||||
if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')):
|
if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')):
|
||||||
return self.failed_hash(request) # Security hash failed.
|
return self.failed_hash(request) # Security hash failed.
|
||||||
return self.done(request, f.clean_data)
|
return self.done(request, f.cleaned_data)
|
||||||
else:
|
else:
|
||||||
return render_to_response(self.form_template,
|
return render_to_response(self.form_template,
|
||||||
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
|
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
|
||||||
@ -160,6 +160,9 @@ class FormPreview(object):
|
|||||||
|
|
||||||
# METHODS SUBCLASSES MUST OVERRIDE ########################################
|
# METHODS SUBCLASSES MUST OVERRIDE ########################################
|
||||||
|
|
||||||
def done(self, request, clean_data):
|
def done(self, request, cleaned_data):
|
||||||
"Does something with the clean_data and returns an HttpResponseRedirect."
|
"""
|
||||||
|
Does something with the cleaned_data and returns an
|
||||||
|
HttpResponseRedirect.
|
||||||
|
"""
|
||||||
raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)
|
raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)
|
||||||
|
@ -41,7 +41,7 @@ class ISIdNumberField(RegexField):
|
|||||||
method is modulo 11.
|
method is modulo 11.
|
||||||
"""
|
"""
|
||||||
check = [3, 2, 7, 6, 5, 4, 3, 2, 1, 0]
|
check = [3, 2, 7, 6, 5, 4, 3, 2, 1, 0]
|
||||||
return sum(int(value[i]) * check[i] for i in range(10)) % 11 == 0
|
return sum([int(value[i]) * check[i] for i in range(10)]) % 11 == 0
|
||||||
|
|
||||||
def _format(self, value):
|
def _format(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -1407,21 +1407,25 @@ def load_data(fixture_labels, verbosity=1):
|
|||||||
if verbosity > 1:
|
if verbosity > 1:
|
||||||
print "No %s fixture '%s' in %s." % \
|
print "No %s fixture '%s' in %s." % \
|
||||||
(format, fixture_name, humanize(fixture_dir))
|
(format, fixture_name, humanize(fixture_dir))
|
||||||
if count[0] == 0:
|
|
||||||
if verbosity > 0:
|
if count[0] > 0:
|
||||||
print "No fixtures found."
|
|
||||||
else:
|
|
||||||
if verbosity > 0:
|
|
||||||
print "Installed %d object(s) from %d fixture(s)" % tuple(count)
|
|
||||||
sequence_sql = backend.get_sql_sequence_reset(style, models)
|
sequence_sql = backend.get_sql_sequence_reset(style, models)
|
||||||
if sequence_sql:
|
if sequence_sql:
|
||||||
if verbosity > 1:
|
if verbosity > 1:
|
||||||
print "Resetting sequences"
|
print "Resetting sequences"
|
||||||
for line in sequence_sql:
|
for line in sequence_sql:
|
||||||
cursor.execute(line)
|
cursor.execute(line)
|
||||||
|
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
transaction.leave_transaction_management()
|
transaction.leave_transaction_management()
|
||||||
|
|
||||||
|
if count[0] == 0:
|
||||||
|
if verbosity > 0:
|
||||||
|
print "No fixtures found."
|
||||||
|
else:
|
||||||
|
if verbosity > 0:
|
||||||
|
print "Installed %d object(s) from %d fixture(s)" % tuple(count)
|
||||||
|
|
||||||
load_data.help_doc = 'Installs the named fixture(s) in the database'
|
load_data.help_doc = 'Installs the named fixture(s) in the database'
|
||||||
load_data.args = "[--verbosity] fixture, fixture, ..."
|
load_data.args = "[--verbosity] fixture, fixture, ..."
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class Serializer(base.Serializer):
|
|||||||
def handle_fk_field(self, obj, field):
|
def handle_fk_field(self, obj, field):
|
||||||
related = getattr(obj, field.name)
|
related = getattr(obj, field.name)
|
||||||
if related is not None:
|
if related is not None:
|
||||||
related = related._get_pk_val()
|
related = getattr(related, field.rel.field_name)
|
||||||
self._current[field.name] = related
|
self._current[field.name] = related
|
||||||
|
|
||||||
def handle_m2m_field(self, obj, field):
|
def handle_m2m_field(self, obj, field):
|
||||||
@ -80,7 +80,10 @@ def Deserializer(object_list, **options):
|
|||||||
|
|
||||||
# Handle FK fields
|
# Handle FK fields
|
||||||
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
|
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
|
||||||
data[field.attname] = field.rel.to._meta.pk.to_python(field_value)
|
if field_value:
|
||||||
|
data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
|
||||||
|
else:
|
||||||
|
data[field.attname] = None
|
||||||
|
|
||||||
# Handle all other fields
|
# Handle all other fields
|
||||||
else:
|
else:
|
||||||
|
@ -82,7 +82,7 @@ class Serializer(base.Serializer):
|
|||||||
self._start_relational_field(field)
|
self._start_relational_field(field)
|
||||||
related = getattr(obj, field.name)
|
related = getattr(obj, field.name)
|
||||||
if related is not None:
|
if related is not None:
|
||||||
self.xml.characters(str(related._get_pk_val()))
|
self.xml.characters(str(getattr(related, field.rel.field_name)))
|
||||||
else:
|
else:
|
||||||
self.xml.addQuickElement("None")
|
self.xml.addQuickElement("None")
|
||||||
self.xml.endElement("field")
|
self.xml.endElement("field")
|
||||||
@ -181,7 +181,7 @@ class Deserializer(base.Deserializer):
|
|||||||
if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
|
if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return field.rel.to._meta.pk.to_python(
|
return field.rel.to._meta.get_field(field.rel.field_name).to_python(
|
||||||
getInnerText(node).strip().encode(self.encoding))
|
getInnerText(node).strip().encode(self.encoding))
|
||||||
|
|
||||||
def _handle_m2m_field_node(self, node, field):
|
def _handle_m2m_field_node(self, node, field):
|
||||||
|
@ -223,7 +223,7 @@ def get_sql_sequence_reset(style, model_list):
|
|||||||
if isinstance(f, models.AutoField):
|
if isinstance(f, models.AutoField):
|
||||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
(style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)),
|
style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))),
|
||||||
style.SQL_KEYWORD('SELECT'),
|
style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD(quote_name(f.column)),
|
style.SQL_FIELD(quote_name(f.column)),
|
||||||
style.SQL_KEYWORD('FROM'),
|
style.SQL_KEYWORD('FROM'),
|
||||||
@ -232,7 +232,7 @@ def get_sql_sequence_reset(style, model_list):
|
|||||||
for f in model._meta.many_to_many:
|
for f in model._meta.many_to_many:
|
||||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
(style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
|
style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())),
|
||||||
style.SQL_KEYWORD('SELECT'),
|
style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD(quote_name('id')),
|
style.SQL_FIELD(quote_name('id')),
|
||||||
style.SQL_KEYWORD('FROM'),
|
style.SQL_KEYWORD('FROM'),
|
||||||
|
@ -180,7 +180,7 @@ def get_sql_sequence_reset(style, model_list):
|
|||||||
if isinstance(f, models.AutoField):
|
if isinstance(f, models.AutoField):
|
||||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
(style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)),
|
style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))),
|
||||||
style.SQL_KEYWORD('SELECT'),
|
style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD(quote_name(f.column)),
|
style.SQL_FIELD(quote_name(f.column)),
|
||||||
style.SQL_KEYWORD('FROM'),
|
style.SQL_KEYWORD('FROM'),
|
||||||
@ -189,7 +189,7 @@ def get_sql_sequence_reset(style, model_list):
|
|||||||
for f in model._meta.many_to_many:
|
for f in model._meta.many_to_many:
|
||||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
(style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
|
style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())),
|
||||||
style.SQL_KEYWORD('SELECT'),
|
style.SQL_KEYWORD('SELECT'),
|
||||||
style.SQL_FIELD(quote_name('id')),
|
style.SQL_FIELD(quote_name('id')),
|
||||||
style.SQL_KEYWORD('FROM'),
|
style.SQL_KEYWORD('FROM'),
|
||||||
|
@ -187,13 +187,13 @@ class BaseForm(StrAndUnicode):
|
|||||||
|
|
||||||
def full_clean(self):
|
def full_clean(self):
|
||||||
"""
|
"""
|
||||||
Cleans all of self.data and populates self.__errors and self.clean_data.
|
Cleans all of self.data and populates self.__errors and self.cleaned_data.
|
||||||
"""
|
"""
|
||||||
errors = ErrorDict()
|
errors = ErrorDict()
|
||||||
if not self.is_bound: # Stop further processing.
|
if not self.is_bound: # Stop further processing.
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
return
|
return
|
||||||
self.clean_data = {}
|
self.cleaned_data = {}
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
# value_from_datadict() gets the data from the dictionary.
|
# value_from_datadict() gets the data from the dictionary.
|
||||||
# Each widget type knows how to retrieve its own data, because some
|
# Each widget type knows how to retrieve its own data, because some
|
||||||
@ -201,18 +201,18 @@ class BaseForm(StrAndUnicode):
|
|||||||
value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
|
value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
|
||||||
try:
|
try:
|
||||||
value = field.clean(value)
|
value = field.clean(value)
|
||||||
self.clean_data[name] = value
|
self.cleaned_data[name] = value
|
||||||
if hasattr(self, 'clean_%s' % name):
|
if hasattr(self, 'clean_%s' % name):
|
||||||
value = getattr(self, 'clean_%s' % name)()
|
value = getattr(self, 'clean_%s' % name)()
|
||||||
self.clean_data[name] = value
|
self.cleaned_data[name] = value
|
||||||
except ValidationError, e:
|
except ValidationError, e:
|
||||||
errors[name] = e.messages
|
errors[name] = e.messages
|
||||||
try:
|
try:
|
||||||
self.clean_data = self.clean()
|
self.cleaned_data = self.clean()
|
||||||
except ValidationError, e:
|
except ValidationError, e:
|
||||||
errors[NON_FIELD_ERRORS] = e.messages
|
errors[NON_FIELD_ERRORS] = e.messages
|
||||||
if errors:
|
if errors:
|
||||||
delattr(self, 'clean_data')
|
delattr(self, 'cleaned_data')
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
@ -222,7 +222,7 @@ class BaseForm(StrAndUnicode):
|
|||||||
not be associated with a particular field; it will have a special-case
|
not be associated with a particular field; it will have a special-case
|
||||||
association with the field named '__all__'.
|
association with the field named '__all__'.
|
||||||
"""
|
"""
|
||||||
return self.clean_data
|
return self.cleaned_data
|
||||||
|
|
||||||
class Form(BaseForm):
|
class Form(BaseForm):
|
||||||
"A collection of Fields, plus their associated data."
|
"A collection of Fields, plus their associated data."
|
||||||
@ -273,6 +273,8 @@ class BoundField(StrAndUnicode):
|
|||||||
attrs['id'] = auto_id
|
attrs['id'] = auto_id
|
||||||
if not self.form.is_bound:
|
if not self.form.is_bound:
|
||||||
data = self.form.initial.get(self.name, self.field.initial)
|
data = self.form.initial.get(self.name, self.field.initial)
|
||||||
|
if callable(data):
|
||||||
|
data = data()
|
||||||
else:
|
else:
|
||||||
data = self.data
|
data = self.data
|
||||||
return widget.render(self.html_name, data, attrs=attrs)
|
return widget.render(self.html_name, data, attrs=attrs)
|
||||||
|
@ -33,7 +33,7 @@ class BaseFormSet(object):
|
|||||||
if data:
|
if data:
|
||||||
self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
|
self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
|
||||||
if self.management_form.is_valid():
|
if self.management_form.is_valid():
|
||||||
self.total_forms = self.management_form.clean_data[FORM_COUNT_FIELD_NAME]
|
self.total_forms = self.management_form.cleaned_data[FORM_COUNT_FIELD_NAME]
|
||||||
self.required_forms = self.total_forms - self.num_extra
|
self.required_forms = self.total_forms - self.num_extra
|
||||||
else:
|
else:
|
||||||
# not sure that ValidationError is the best thing to raise here
|
# not sure that ValidationError is the best thing to raise here
|
||||||
@ -67,7 +67,7 @@ class BaseFormSet(object):
|
|||||||
|
|
||||||
def full_clean(self):
|
def full_clean(self):
|
||||||
"""
|
"""
|
||||||
Cleans all of self.data and populates self.__errors and self.clean_data.
|
Cleans all of self.data and populates self.__errors and self.cleaned_data.
|
||||||
"""
|
"""
|
||||||
is_valid = True
|
is_valid = True
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ class BaseFormSet(object):
|
|||||||
if not self.is_bound: # Stop further processing.
|
if not self.is_bound: # Stop further processing.
|
||||||
self.__errors = errors
|
self.__errors = errors
|
||||||
return
|
return
|
||||||
clean_data = []
|
cleaned_data = []
|
||||||
deleted_data = []
|
deleted_data = []
|
||||||
|
|
||||||
self._form_list = []
|
self._form_list = []
|
||||||
@ -103,12 +103,12 @@ class BaseFormSet(object):
|
|||||||
self.add_fields(form, i)
|
self.add_fields(form, i)
|
||||||
else:
|
else:
|
||||||
# if the formset is still vaild overall and this form instance
|
# if the formset is still vaild overall and this form instance
|
||||||
# is valid, keep appending to clean_data
|
# is valid, keep appending to cleaned_data
|
||||||
if is_valid and form.is_valid():
|
if is_valid and form.is_valid():
|
||||||
if self.deletable and form.clean_data[DELETION_FIELD_NAME]:
|
if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
|
||||||
deleted_data.append(form.clean_data)
|
deleted_data.append(form.cleaned_data)
|
||||||
else:
|
else:
|
||||||
clean_data.append(form.clean_data)
|
cleaned_data.append(form.cleaned_data)
|
||||||
else:
|
else:
|
||||||
is_valid = False
|
is_valid = False
|
||||||
# append to errors regardless
|
# append to errors regardless
|
||||||
@ -117,14 +117,14 @@ class BaseFormSet(object):
|
|||||||
|
|
||||||
deleted_data.reverse()
|
deleted_data.reverse()
|
||||||
if self.orderable:
|
if self.orderable:
|
||||||
clean_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME])
|
cleaned_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME])
|
||||||
else:
|
else:
|
||||||
clean_data.reverse()
|
cleaned_data.reverse()
|
||||||
errors.reverse()
|
errors.reverse()
|
||||||
self._form_list.reverse()
|
self._form_list.reverse()
|
||||||
|
|
||||||
if is_valid:
|
if is_valid:
|
||||||
self.clean_data = clean_data
|
self.cleaned_data = cleaned_data
|
||||||
self.deleted_data = deleted_data
|
self.deleted_data = deleted_data
|
||||||
self.errors = errors
|
self.errors = errors
|
||||||
self._is_valid = is_valid
|
self._is_valid = is_valid
|
||||||
|
@ -12,19 +12,9 @@ from widgets import Select, SelectMultiple, MultipleHiddenInput
|
|||||||
__all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
__all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
||||||
'ModelChoiceField', 'ModelMultipleChoiceField')
|
'ModelChoiceField', 'ModelMultipleChoiceField')
|
||||||
|
|
||||||
def model_save(self, commit=True):
|
def save_instance(form, instance, fields=None, fail_message='saved', commit=True):
|
||||||
"""
|
"""
|
||||||
Creates and returns model instance according to self.clean_data.
|
Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
|
||||||
|
|
||||||
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)
|
|
||||||
return save_instance(self, self._model(), commit)
|
|
||||||
|
|
||||||
def save_instance(form, instance, commit=True):
|
|
||||||
"""
|
|
||||||
Saves bound Form ``form``'s clean_data into model instance ``instance``.
|
|
||||||
|
|
||||||
Assumes ``form`` has a field for every non-AutoField database field in
|
Assumes ``form`` has a field for every non-AutoField database field in
|
||||||
``instance``. If commit=True, then the changes to ``instance`` will be
|
``instance``. If commit=True, then the changes to ``instance`` will be
|
||||||
@ -33,30 +23,40 @@ def save_instance(form, instance, commit=True):
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
opts = instance.__class__._meta
|
opts = instance.__class__._meta
|
||||||
if form.errors:
|
if form.errors:
|
||||||
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
raise ValueError("The %s could not be %s because the data didn't validate." % (opts.object_name, fail_message))
|
||||||
clean_data = form.clean_data
|
cleaned_data = form.cleaned_data
|
||||||
for f in opts.fields:
|
for f in opts.fields:
|
||||||
if not f.editable or isinstance(f, models.AutoField) or not f.name in clean_data:
|
if not f.editable or isinstance(f, models.AutoField) or not f.name in cleaned_data:
|
||||||
continue
|
continue
|
||||||
setattr(instance, f.name, clean_data[f.name])
|
if fields and f.name not in fields:
|
||||||
|
continue
|
||||||
|
setattr(instance, f.name, cleaned_data[f.name])
|
||||||
if commit:
|
if commit:
|
||||||
instance.save()
|
instance.save()
|
||||||
for f in opts.many_to_many:
|
for f in opts.many_to_many:
|
||||||
if f.name in clean_data:
|
if fields and f.name not in fields:
|
||||||
setattr(instance, f.attname, clean_data[f.name])
|
continue
|
||||||
|
if f.name in cleaned_data:
|
||||||
|
setattr(instance, f.attname, cleaned_data[f.name])
|
||||||
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
|
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
|
||||||
# data will be lost. This happens because a many-to-many options cannot be
|
# data will be lost. This happens because a many-to-many options cannot be
|
||||||
# set on an object until after it's saved. Maybe we should raise an
|
# set on an object until after it's saved. Maybe we should raise an
|
||||||
# exception in that case.
|
# exception in that case.
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def make_instance_save(instance):
|
def make_model_save(model, fields, fail_message):
|
||||||
"Returns the save() method for a form_for_instance Form."
|
"Returns the save() method for a Form."
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
return save_instance(self, instance, commit)
|
return save_instance(self, model(), fields, fail_message, commit)
|
||||||
return save
|
return save
|
||||||
|
|
||||||
def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()):
|
def make_instance_save(instance, fields, fail_message):
|
||||||
|
"Returns the save() method for a Form."
|
||||||
|
def save(self, commit=True):
|
||||||
|
return save_instance(self, instance, fields, fail_message, commit)
|
||||||
|
return save
|
||||||
|
|
||||||
|
def form_for_model(model, form=BaseForm, fields=None, formfield_callback=lambda f: f.formfield()):
|
||||||
"""
|
"""
|
||||||
Returns a Form class for the given Django model class.
|
Returns a Form class for the given Django model class.
|
||||||
|
|
||||||
@ -71,13 +71,16 @@ def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfiel
|
|||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
if not f.editable:
|
if not f.editable:
|
||||||
continue
|
continue
|
||||||
|
if fields and not f.name in fields:
|
||||||
|
continue
|
||||||
formfield = formfield_callback(f)
|
formfield = formfield_callback(f)
|
||||||
if formfield:
|
if formfield:
|
||||||
field_list.append((f.name, formfield))
|
field_list.append((f.name, formfield))
|
||||||
fields = SortedDictFromList(field_list)
|
base_fields = SortedDictFromList(field_list)
|
||||||
return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
|
return type(opts.object_name + 'Form', (form,),
|
||||||
|
{'base_fields': base_fields, '_model': model, 'save': make_model_save(model, fields, 'created')})
|
||||||
|
|
||||||
def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
|
def form_for_instance(instance, form=BaseForm, fields=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
|
||||||
"""
|
"""
|
||||||
Returns a Form class for the given Django model instance.
|
Returns a Form class for the given Django model instance.
|
||||||
|
|
||||||
@ -94,13 +97,15 @@ def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kw
|
|||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
if not f.editable:
|
if not f.editable:
|
||||||
continue
|
continue
|
||||||
|
if fields and not f.name in fields:
|
||||||
|
continue
|
||||||
current_value = f.value_from_object(instance)
|
current_value = f.value_from_object(instance)
|
||||||
formfield = formfield_callback(f, initial=current_value)
|
formfield = formfield_callback(f, initial=current_value)
|
||||||
if formfield:
|
if formfield:
|
||||||
field_list.append((f.name, formfield))
|
field_list.append((f.name, formfield))
|
||||||
fields = SortedDictFromList(field_list)
|
base_fields = SortedDictFromList(field_list)
|
||||||
return type(opts.object_name + 'InstanceForm', (form,),
|
return type(opts.object_name + 'InstanceForm', (form,),
|
||||||
{'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})
|
{'base_fields': base_fields, '_model': model, 'save': make_instance_save(instance, fields, 'changed')})
|
||||||
|
|
||||||
def form_for_fields(field_list):
|
def form_for_fields(field_list):
|
||||||
"Returns a Form class for the given list of Django database field instances."
|
"Returns a Form class for the given list of Django database field instances."
|
||||||
|
@ -46,15 +46,15 @@ class TestCase(unittest.TestCase):
|
|||||||
management.load_data(self.fixtures, verbosity=0)
|
management.load_data(self.fixtures, verbosity=0)
|
||||||
mail.outbox = []
|
mail.outbox = []
|
||||||
|
|
||||||
def run(self, result=None):
|
def __call__(self, result=None):
|
||||||
"""Wrapper around default run method to perform common Django test set up.
|
"""
|
||||||
This means that user-defined Test Cases aren't required to include a call
|
Wrapper around default __call__ method to perform common Django test
|
||||||
to super().setUp().
|
set up. This means that user-defined Test Cases aren't required to
|
||||||
|
include a call to super().setUp().
|
||||||
"""
|
"""
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
self._pre_setup()
|
self._pre_setup()
|
||||||
super(TestCase, self).run(result)
|
super(TestCase, self).__call__(result)
|
||||||
|
|
||||||
def assertRedirects(self, response, expected_path, status_code=302, target_status_code=200):
|
def assertRedirects(self, response, expected_path, status_code=302, target_status_code=200):
|
||||||
"""Assert that a response redirected to a specific URL, and that the
|
"""Assert that a response redirected to a specific URL, and that the
|
||||||
@ -62,7 +62,7 @@ class TestCase(unittest.TestCase):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self.assertEqual(response.status_code, status_code,
|
self.assertEqual(response.status_code, status_code,
|
||||||
"Response didn't redirect: Reponse code was %d (expected %d)" %
|
"Response didn't redirect as expected: Reponse code was %d (expected %d)" %
|
||||||
(response.status_code, status_code))
|
(response.status_code, status_code))
|
||||||
scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
|
scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
|
||||||
self.assertEqual(path, expected_path,
|
self.assertEqual(path, expected_path,
|
||||||
@ -70,7 +70,7 @@ class TestCase(unittest.TestCase):
|
|||||||
redirect_response = self.client.get(path)
|
redirect_response = self.client.get(path)
|
||||||
self.assertEqual(redirect_response.status_code, target_status_code,
|
self.assertEqual(redirect_response.status_code, target_status_code,
|
||||||
"Couldn't retrieve redirection page '%s': response code was %d (expected %d)" %
|
"Couldn't retrieve redirection page '%s': response code was %d (expected %d)" %
|
||||||
(path, response.status_code, status_code))
|
(path, redirect_response.status_code, target_status_code))
|
||||||
|
|
||||||
def assertContains(self, response, text, count=1, status_code=200):
|
def assertContains(self, response, text, count=1, status_code=200):
|
||||||
"""Assert that a response indicates that a page was retreived successfully,
|
"""Assert that a response indicates that a page was retreived successfully,
|
||||||
@ -108,7 +108,7 @@ class TestCase(unittest.TestCase):
|
|||||||
for err in errors:
|
for err in errors:
|
||||||
if field:
|
if field:
|
||||||
if field in context[form].errors:
|
if field in context[form].errors:
|
||||||
self.assertTrue(err in context[form].errors[field],
|
self.failUnless(err in context[form].errors[field],
|
||||||
"The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %
|
"The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %
|
||||||
(field, form, i, err, list(context[form].errors[field])))
|
(field, form, i, err, list(context[form].errors[field])))
|
||||||
elif field in context[form].fields:
|
elif field in context[form].fields:
|
||||||
@ -117,7 +117,7 @@ class TestCase(unittest.TestCase):
|
|||||||
else:
|
else:
|
||||||
self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
|
self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
|
||||||
else:
|
else:
|
||||||
self.assertTrue(err in context[form].non_field_errors(),
|
self.failUnless(err in context[form].non_field_errors(),
|
||||||
"The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %
|
"The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %
|
||||||
(form, i, err, list(context[form].non_field_errors())))
|
(form, i, err, list(context[form].non_field_errors())))
|
||||||
if not found_form:
|
if not found_form:
|
||||||
@ -127,7 +127,7 @@ class TestCase(unittest.TestCase):
|
|||||||
"Assert that the template with the provided name was used in rendering the response"
|
"Assert that the template with the provided name was used in rendering the response"
|
||||||
if isinstance(response.template, list):
|
if isinstance(response.template, list):
|
||||||
template_names = [t.name for t in response.template]
|
template_names = [t.name for t in response.template]
|
||||||
self.assertTrue(template_name in template_names,
|
self.failUnless(template_name in template_names,
|
||||||
"Template '%s' was not one of the templates used to render the response. Templates used: %s" %
|
"Template '%s' was not one of the templates used to render the response. Templates used: %s" %
|
||||||
(template_name, template_names))
|
(template_name, template_names))
|
||||||
elif response.template:
|
elif response.template:
|
||||||
@ -140,7 +140,7 @@ class TestCase(unittest.TestCase):
|
|||||||
def assertTemplateNotUsed(self, response, template_name):
|
def assertTemplateNotUsed(self, response, template_name):
|
||||||
"Assert that the template with the provided name was NOT used in rendering the response"
|
"Assert that the template with the provided name was NOT used in rendering the response"
|
||||||
if isinstance(response.template, list):
|
if isinstance(response.template, list):
|
||||||
self.assertFalse(template_name in [t.name for t in response.template],
|
self.failIf(template_name in [t.name for t in response.template],
|
||||||
"Template '%s' was used unexpectedly in rendering the response" % template_name)
|
"Template '%s' was used unexpectedly in rendering the response" % template_name)
|
||||||
elif response.template:
|
elif response.template:
|
||||||
self.assertNotEqual(template_name, response.template.name,
|
self.assertNotEqual(template_name, response.template.name,
|
||||||
|
@ -44,7 +44,11 @@ How to use Databrowse
|
|||||||
It doesn't matter where you put this, as long as it gets executed at
|
It doesn't matter where you put this, as long as it gets executed at
|
||||||
some point. A good place for it is in your URLconf file (``urls.py``).
|
some point. A good place for it is in your URLconf file (``urls.py``).
|
||||||
|
|
||||||
3. Add the following line to your URLconf::
|
3. Change your URLconf to import the ``databrowse`` module::
|
||||||
|
|
||||||
|
from django.contrib import databrowse
|
||||||
|
|
||||||
|
...and add the following line to your URLconf::
|
||||||
|
|
||||||
(r'^databrowse/(.*)', databrowse.site.root),
|
(r'^databrowse/(.*)', databrowse.site.root),
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ To pluralize, specify both the singular and plural forms with the
|
|||||||
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and
|
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and
|
||||||
``{% endblocktrans %}``. Example::
|
``{% endblocktrans %}``. Example::
|
||||||
|
|
||||||
{% blocktrans count list|count as counter %}
|
{% blocktrans count list|length as counter %}
|
||||||
There is only one {{ name }} object.
|
There is only one {{ name }} object.
|
||||||
{% plural %}
|
{% plural %}
|
||||||
There are {{ counter }} {{ name }} objects.
|
There are {{ counter }} {{ name }} objects.
|
||||||
|
@ -9,28 +9,30 @@ framework. This document explains how to use this new library.
|
|||||||
Migration plan
|
Migration plan
|
||||||
==============
|
==============
|
||||||
|
|
||||||
``django.newforms`` currently is only available in Django beginning
|
``django.newforms`` is new in Django's 0.96 release, but, as it won't be new
|
||||||
with the 0.96 release. the Django development version -- i.e., it's
|
forever, we plan to rename it to ``django.forms`` in the future. The current
|
||||||
not available in the Django 0.95 release. For the next Django release,
|
``django.forms`` package will be available as ``django.oldforms`` until Django
|
||||||
our plan is to do the following:
|
1.0, when we plan to remove it for good.
|
||||||
|
|
||||||
* As of revision [4208], we've copied the current ``django.forms`` to
|
That has direct repercussions on the forward compatibility of your code. Please
|
||||||
``django.oldforms``. This allows you to upgrade your code *now* rather
|
read the following migration plan and code accordingly:
|
||||||
than waiting for the backwards-incompatible change and rushing to fix
|
|
||||||
your code after the fact. Just change your import statements like this::
|
* The old forms framework (the current ``django.forms``) has been copied to
|
||||||
|
``django.oldforms``. Thus, you can start upgrading your code *now*,
|
||||||
|
rather than waiting for the future backwards-incompatible change, by
|
||||||
|
changing your import statements like this::
|
||||||
|
|
||||||
from django import forms # old
|
from django import forms # old
|
||||||
from django import oldforms as forms # new
|
from django import oldforms as forms # new
|
||||||
|
|
||||||
* At an undecided future date, we will move the current ``django.newforms``
|
* In the next Django release (0.97), we will move the current
|
||||||
to ``django.forms``. This will be a backwards-incompatible change, and
|
``django.newforms`` to ``django.forms``. This will be a
|
||||||
anybody who is still using the old version of ``django.forms`` at that
|
backwards-incompatible change, and anybody who is still using the old
|
||||||
time will need to change their import statements, as described in the
|
version of ``django.forms`` at that time will need to change their import
|
||||||
previous bullet.
|
statements, as described in the previous bullet.
|
||||||
|
|
||||||
* We will remove ``django.oldforms`` in the release *after* the next Django
|
* We will remove ``django.oldforms`` in the release *after* the next Django
|
||||||
release -- the release that comes after the release in which we're
|
release -- either 0.98 or 1.0, whichever comes first.
|
||||||
creating the new ``django.forms``.
|
|
||||||
|
|
||||||
With this in mind, we recommend you use the following import statement when
|
With this in mind, we recommend you use the following import statement when
|
||||||
using ``django.newforms``::
|
using ``django.newforms``::
|
||||||
@ -184,7 +186,7 @@ e-mail address::
|
|||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
False
|
False
|
||||||
|
|
||||||
Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
|
Access the ``errors`` attribute to get a dictionary of error messages::
|
||||||
|
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
|
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
|
||||||
@ -197,6 +199,10 @@ You can access ``errors`` without having to call ``is_valid()`` first. The
|
|||||||
form's data will be validated the first time either you call ``is_valid()`` or
|
form's data will be validated the first time either you call ``is_valid()`` or
|
||||||
access ``errors``.
|
access ``errors``.
|
||||||
|
|
||||||
|
The validation routines will only get called once, regardless of how many times
|
||||||
|
you access ``errors`` or call ``is_valid()``. This means that if validation has
|
||||||
|
side effects, those side effects will only be triggered once.
|
||||||
|
|
||||||
Behavior of unbound forms
|
Behavior of unbound forms
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -224,7 +230,7 @@ object. Regardless of whether you pass it a string in the format
|
|||||||
it's valid.
|
it's valid.
|
||||||
|
|
||||||
Once you've created a ``Form`` instance with a set of data and validated it,
|
Once you've created a ``Form`` instance with a set of data and validated it,
|
||||||
you can access the clean data via the ``clean_data`` attribute of the ``Form``
|
you can access the clean data via the ``cleaned_data`` attribute of the ``Form``
|
||||||
object::
|
object::
|
||||||
|
|
||||||
>>> data = {'subject': 'hello',
|
>>> data = {'subject': 'hello',
|
||||||
@ -234,7 +240,7 @@ object::
|
|||||||
>>> f = ContactForm(data)
|
>>> f = ContactForm(data)
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||||
|
|
||||||
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
|
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
|
||||||
@ -242,7 +248,7 @@ always cleans the input into a Unicode string. We'll cover the encoding
|
|||||||
implications later in this document.
|
implications later in this document.
|
||||||
|
|
||||||
If your data does *not* validate, your ``Form`` instance will not have a
|
If your data does *not* validate, your ``Form`` instance will not have a
|
||||||
``clean_data`` attribute::
|
``cleaned_data`` attribute::
|
||||||
|
|
||||||
>>> data = {'subject': '',
|
>>> data = {'subject': '',
|
||||||
... 'message': 'Hi there',
|
... 'message': 'Hi there',
|
||||||
@ -251,15 +257,15 @@ If your data does *not* validate, your ``Form`` instance will not have a
|
|||||||
>>> f = ContactForm(data)
|
>>> f = ContactForm(data)
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
False
|
False
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
AttributeError: 'ContactForm' object has no attribute 'cleaned_data'
|
||||||
|
|
||||||
``clean_data`` will always *only* contain a key for fields defined in the
|
``cleaned_data`` will always *only* contain a key for fields defined in the
|
||||||
``Form``, even if you pass extra data when you define the ``Form``. In this
|
``Form``, even if you pass extra data when you define the ``Form``. In this
|
||||||
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
|
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
|
||||||
but ``clean_data`` contains only the form's fields::
|
but ``cleaned_data`` contains only the form's fields::
|
||||||
|
|
||||||
>>> data = {'subject': 'hello',
|
>>> data = {'subject': 'hello',
|
||||||
... 'message': 'Hi there',
|
... 'message': 'Hi there',
|
||||||
@ -271,9 +277,30 @@ but ``clean_data`` contains only the form's fields::
|
|||||||
>>> f = ContactForm(data)
|
>>> f = ContactForm(data)
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.clean_data # Doesn't contain extra_field_1, etc.
|
>>> f.cleaned_data # Doesn't contain extra_field_1, etc.
|
||||||
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||||
|
|
||||||
|
``cleaned_data`` will include a key and value for *all* fields defined in the
|
||||||
|
``Form``, even if the data didn't include a value for fields that are not
|
||||||
|
required. In this example, the data dictionary doesn't include a value for the
|
||||||
|
``nick_name`` field, but ``cleaned_data`` includes it, with an empty value::
|
||||||
|
|
||||||
|
>>> class OptionalPersonForm(Form):
|
||||||
|
... first_name = CharField()
|
||||||
|
... last_name = CharField()
|
||||||
|
... nick_name = CharField(required=False)
|
||||||
|
>>> data = {'first_name': u'John', 'last_name': u'Lennon'}
|
||||||
|
>>> f = OptionalPersonForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.cleaned_data
|
||||||
|
{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
|
||||||
|
|
||||||
|
In this above example, the ``cleaned_data`` value for ``nick_name`` is set to an
|
||||||
|
empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat
|
||||||
|
empty values as an empty string. Each field type knows what its "blank" value
|
||||||
|
is -- e.g., for ``DateField``, it's ``None`` instead of the empty string.
|
||||||
|
|
||||||
Behavior of unbound forms
|
Behavior of unbound forms
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -281,10 +308,10 @@ It's meaningless to request "clean" data in a form with no data, but, for the
|
|||||||
record, here's what happens with unbound forms::
|
record, here's what happens with unbound forms::
|
||||||
|
|
||||||
>>> f = ContactForm()
|
>>> f = ContactForm()
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'ContactForm' object has no attribute 'clean_data'
|
AttributeError: 'ContactForm' object has no attribute 'cleaned_data'
|
||||||
|
|
||||||
Outputting forms as HTML
|
Outputting forms as HTML
|
||||||
------------------------
|
------------------------
|
||||||
@ -454,7 +481,7 @@ field::
|
|||||||
If ``auto_id`` is set to a string containing the format character ``'%s'``,
|
If ``auto_id`` is set to a string containing the format character ``'%s'``,
|
||||||
then the form output will include ``<label>`` tags, and will generate ``id``
|
then the form output will include ``<label>`` tags, and will generate ``id``
|
||||||
attributes based on the format string. For example, for a format string
|
attributes based on the format string. For example, for a format string
|
||||||
``'field_%s'``, a field named ``subject`` will get the ``id``
|
``'field_%s'``, a field named ``subject`` will get the ``id`` value
|
||||||
``'field_subject'``. Continuing our example::
|
``'field_subject'``. Continuing our example::
|
||||||
|
|
||||||
>>> f = ContactForm(auto_id='id_for_%s')
|
>>> f = ContactForm(auto_id='id_for_%s')
|
||||||
@ -493,8 +520,9 @@ How errors are displayed
|
|||||||
|
|
||||||
If you render a bound ``Form`` object, the act of rendering will automatically
|
If you render a bound ``Form`` object, the act of rendering will automatically
|
||||||
run the form's validation if it hasn't already happened, and the HTML output
|
run the form's validation if it hasn't already happened, and the HTML output
|
||||||
will include the validation errors as a ``<ul>`` near the field. The particular
|
will include the validation errors as a ``<ul class="errorlist">`` near the
|
||||||
positioning of the error messages depends on the output method you're using::
|
field. The particular positioning of the error messages depends on the output
|
||||||
|
method you're using::
|
||||||
|
|
||||||
>>> data = {'subject': '',
|
>>> data = {'subject': '',
|
||||||
... 'message': 'Hi there',
|
... 'message': 'Hi there',
|
||||||
@ -556,7 +584,8 @@ The field-specific output honors the form object's ``auto_id`` setting::
|
|||||||
<input type="text" name="message" id="id_message" />
|
<input type="text" name="message" id="id_message" />
|
||||||
|
|
||||||
For a field's list of errors, access the field's ``errors`` attribute. This
|
For a field's list of errors, access the field's ``errors`` attribute. This
|
||||||
is a list-like object that is displayed as an HTML ``<ul>`` when printed::
|
is a list-like object that is displayed as an HTML ``<ul class="errorlist">``
|
||||||
|
when printed::
|
||||||
|
|
||||||
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
|
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
|
||||||
>>> f = ContactForm(data, auto_id=False)
|
>>> f = ContactForm(data, auto_id=False)
|
||||||
@ -646,7 +675,7 @@ Core field arguments
|
|||||||
|
|
||||||
Each ``Field`` class constructor takes at least these arguments. Some
|
Each ``Field`` class constructor takes at least these arguments. Some
|
||||||
``Field`` classes take additional, field-specific arguments, but the following
|
``Field`` classes take additional, field-specific arguments, but the following
|
||||||
should *always* be available:
|
should *always* be accepted:
|
||||||
|
|
||||||
``required``
|
``required``
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
@ -704,7 +733,7 @@ field.)
|
|||||||
The ``label`` argument lets you specify the "human-friendly" label for this
|
The ``label`` argument lets you specify the "human-friendly" label for this
|
||||||
field. This is used when the ``Field`` is displayed in a ``Form``.
|
field. This is used when the ``Field`` is displayed in a ``Form``.
|
||||||
|
|
||||||
As explained in _`Outputting forms as HTML` above, the default label for a
|
As explained in "Outputting forms as HTML" above, the default label for a
|
||||||
``Field`` is generated from the field name by converting all underscores to
|
``Field`` is generated from the field name by converting all underscores to
|
||||||
spaces and upper-casing the first letter. Specify ``label`` if that default
|
spaces and upper-casing the first letter. Specify ``label`` if that default
|
||||||
behavior doesn't result in an adequate label.
|
behavior doesn't result in an adequate label.
|
||||||
@ -779,14 +808,15 @@ validation if a particular field's value is not given. ``initial`` values are
|
|||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
The ``widget`` argument lets you specify a ``Widget`` class to use when
|
The ``widget`` argument lets you specify a ``Widget`` class to use when
|
||||||
rendering this ``Field``. See _`Widgets` below for more information.
|
rendering this ``Field``. See "Widgets" below for more information.
|
||||||
|
|
||||||
``help_text``
|
``help_text``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
The ``help_text`` argument lets you specify descriptive text for this
|
The ``help_text`` argument lets you specify descriptive text for this
|
||||||
``Field``. If you provide ``help_text``, it will be displayed next to the
|
``Field``. If you provide ``help_text``, it will be displayed next to the
|
||||||
``Field`` when the ``Field`` is rendered in a ``Form``.
|
``Field`` when the ``Field`` is rendered by one of the convenience ``Form``
|
||||||
|
methods (e.g., ``as_ul()``).
|
||||||
|
|
||||||
Here's a full example ``Form`` that implements ``help_text`` for two of its
|
Here's a full example ``Form`` that implements ``help_text`` for two of its
|
||||||
fields. We've specified ``auto_id=False`` to simplify the output::
|
fields. We've specified ``auto_id=False`` to simplify the output::
|
||||||
@ -860,6 +890,212 @@ level and at the form instance level, and the latter gets precedence::
|
|||||||
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
|
||||||
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
|
||||||
|
|
||||||
|
Built-in ``Field`` classes
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Naturally, the ``newforms`` library comes with a set of ``Field`` classes that
|
||||||
|
represent common validation needs. This section documents each built-in field.
|
||||||
|
|
||||||
|
For each field, we describe the default widget used if you don't specify
|
||||||
|
``widget``. We also specify the value returned when you provide an empty value
|
||||||
|
(see the section on ``required`` above to understand what that means).
|
||||||
|
|
||||||
|
``BooleanField``
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``CheckboxInput``
|
||||||
|
* Empty value: ``None``
|
||||||
|
* Normalizes to: A Python ``True`` or ``False`` value.
|
||||||
|
* Validates nothing (i.e., it never raises a ``ValidationError``).
|
||||||
|
|
||||||
|
``CharField``
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``''`` (an empty string)
|
||||||
|
* Normalizes to: A Unicode object.
|
||||||
|
* Validates nothing, unless ``max_length`` or ``min_length`` is provided.
|
||||||
|
|
||||||
|
Has two optional arguments for validation, ``max_length`` and ``min_length``.
|
||||||
|
If provided, these arguments ensure that the string is at most or at least the
|
||||||
|
given length.
|
||||||
|
|
||||||
|
``ChoiceField``
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``Select``
|
||||||
|
* Empty value: ``''`` (an empty string)
|
||||||
|
* Normalizes to: A Unicode object.
|
||||||
|
* Validates that the given value exists in the list of choices.
|
||||||
|
|
||||||
|
Takes one extra argument, ``choices``, which is an iterable (e.g., a list or
|
||||||
|
tuple) of 2-tuples to use as choices for this field.
|
||||||
|
|
||||||
|
``DateField``
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``None``
|
||||||
|
* Normalizes to: A Python ``datetime.date`` object.
|
||||||
|
* Validates that the given value is either a ``datetime.date``,
|
||||||
|
``datetime.datetime`` or string formatted in a particular date format.
|
||||||
|
|
||||||
|
Takes one optional argument, ``input_formats``, which is a list of formats used
|
||||||
|
to attempt to convert a string to a valid ``datetime.date`` object.
|
||||||
|
|
||||||
|
If no ``input_formats`` argument is provided, the default input formats are::
|
||||||
|
|
||||||
|
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
|
||||||
|
'%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
|
||||||
|
'%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
|
||||||
|
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
|
||||||
|
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
|
||||||
|
|
||||||
|
``DateTimeField``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``None``
|
||||||
|
* Normalizes to: A Python ``datetime.datetime`` object.
|
||||||
|
* Validates that the given value is either a ``datetime.datetime``,
|
||||||
|
``datetime.date`` or string formatted in a particular datetime format.
|
||||||
|
|
||||||
|
Takes one optional argument, ``input_formats``, which is a list of formats used
|
||||||
|
to attempt to convert a string to a valid ``datetime.datetime`` object.
|
||||||
|
|
||||||
|
If no ``input_formats`` argument is provided, the default input formats are::
|
||||||
|
|
||||||
|
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
|
||||||
|
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
|
||||||
|
'%Y-%m-%d', # '2006-10-25'
|
||||||
|
'%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
|
||||||
|
'%m/%d/%Y %H:%M', # '10/25/2006 14:30'
|
||||||
|
'%m/%d/%Y', # '10/25/2006'
|
||||||
|
'%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
|
||||||
|
'%m/%d/%y %H:%M', # '10/25/06 14:30'
|
||||||
|
'%m/%d/%y', # '10/25/06'
|
||||||
|
|
||||||
|
``EmailField``
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``''`` (an empty string)
|
||||||
|
* Normalizes to: A Unicode object.
|
||||||
|
* Validates that the given value is a valid e-mail address, using a
|
||||||
|
moderately complex regular expression.
|
||||||
|
|
||||||
|
Has two optional arguments for validation, ``max_length`` and ``min_length``.
|
||||||
|
If provided, these arguments ensure that the string is at most or at least the
|
||||||
|
given length.
|
||||||
|
|
||||||
|
``IntegerField``
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``None``
|
||||||
|
* Normalizes to: A Python integer or long integer.
|
||||||
|
* Validates that the given value is an integer. Leading and trailing
|
||||||
|
whitespace is allowed, as in Python's ``int()`` function.
|
||||||
|
|
||||||
|
``MultipleChoiceField``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``SelectMultiple``
|
||||||
|
* Empty value: ``[]`` (an empty list)
|
||||||
|
* Normalizes to: A list of Unicode objects.
|
||||||
|
* Validates that every value in the given list of values exists in the list
|
||||||
|
of choices.
|
||||||
|
|
||||||
|
Takes one extra argument, ``choices``, which is an iterable (e.g., a list or
|
||||||
|
tuple) of 2-tuples to use as choices for this field.
|
||||||
|
|
||||||
|
``NullBooleanField``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``NullBooleanSelect``
|
||||||
|
* Empty value: ``None``
|
||||||
|
* Normalizes to: A Python ``True``, ``False`` or ``None`` value.
|
||||||
|
* Validates nothing (i.e., it never raises a ``ValidationError``).
|
||||||
|
|
||||||
|
``RegexField``
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``''`` (an empty string)
|
||||||
|
* Normalizes to: A Unicode object.
|
||||||
|
* Validates that the given value matches against a certain regular
|
||||||
|
expression.
|
||||||
|
|
||||||
|
Takes one required argument, ``regex``, which is a regular expression specified
|
||||||
|
either as a string or a compiled regular expression object.
|
||||||
|
|
||||||
|
Also takes the following optional arguments:
|
||||||
|
|
||||||
|
====================== =====================================================
|
||||||
|
Argument Description
|
||||||
|
====================== =====================================================
|
||||||
|
``max_length`` Ensures the string has at most this many characters.
|
||||||
|
``min_length`` Ensures the string has at least this many characters.
|
||||||
|
``error_message`` Error message to return for failed validation. If no
|
||||||
|
message is provided, a generic error message will be
|
||||||
|
used.
|
||||||
|
====================== =====================================================
|
||||||
|
|
||||||
|
``TimeField``
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``None``
|
||||||
|
* Normalizes to: A Python ``datetime.time`` object.
|
||||||
|
* Validates that the given value is either a ``datetime.time`` or string
|
||||||
|
formatted in a particular time format.
|
||||||
|
|
||||||
|
Takes one optional argument, ``input_formats``, which is a list of formats used
|
||||||
|
to attempt to convert a string to a valid ``datetime.time`` object.
|
||||||
|
|
||||||
|
If no ``input_formats`` argument is provided, the default input formats are::
|
||||||
|
|
||||||
|
'%H:%M:%S', # '14:30:59'
|
||||||
|
'%H:%M', # '14:30'
|
||||||
|
|
||||||
|
``URLField``
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Default widget: ``TextInput``
|
||||||
|
* Empty value: ``''`` (an empty string)
|
||||||
|
* Normalizes to: A Unicode object.
|
||||||
|
* Validates that the given value is a valid URL.
|
||||||
|
|
||||||
|
Takes the following optional arguments:
|
||||||
|
|
||||||
|
======================== =====================================================
|
||||||
|
Argument Description
|
||||||
|
======================== =====================================================
|
||||||
|
``max_length`` Ensures the string has at most this many characters.
|
||||||
|
``min_length`` Ensures the string has at least this many characters.
|
||||||
|
``verify_exists`` If ``True``, the validator will attempt to load the
|
||||||
|
given URL, raising ``ValidationError`` if the page
|
||||||
|
gives a 404. Defaults to ``False``.
|
||||||
|
``validator_user_agent`` String used as the user-agent used when checking for
|
||||||
|
a URL's existence. Defaults to the value of the
|
||||||
|
``URL_VALIDATOR_USER_AGENT`` setting.
|
||||||
|
======================== =====================================================
|
||||||
|
|
||||||
|
Slightly complex built-in ``Field`` classes
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
The following are not yet documented here. See the unit tests, linked-to from
|
||||||
|
the bottom of this document, for examples of their use.
|
||||||
|
|
||||||
|
``ComboField``
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``MultiValueField``
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``SplitDateTimeField``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Creating custom fields
|
Creating custom fields
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -870,6 +1106,308 @@ custom ``Field`` classes. To do this, just create a subclass of
|
|||||||
mentioned above (``required``, ``label``, ``initial``, ``widget``,
|
mentioned above (``required``, ``label``, ``initial``, ``widget``,
|
||||||
``help_text``).
|
``help_text``).
|
||||||
|
|
||||||
|
Generating forms for models
|
||||||
|
===========================
|
||||||
|
|
||||||
|
If you're building a database-driven app, chances are you'll have forms that
|
||||||
|
map closely to Django models. For instance, you might have a ``BlogComment``
|
||||||
|
model, and you want to create a form that lets people submit comments. In this
|
||||||
|
case, it would be redundant to define the field types in your form, because
|
||||||
|
you've already defined the fields in your model.
|
||||||
|
|
||||||
|
For this reason, Django provides a few helper functions that let you create a
|
||||||
|
``Form`` class from a Django model.
|
||||||
|
|
||||||
|
``form_for_model()``
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The method ``django.newforms.form_for_model()`` creates a form based on the
|
||||||
|
definition of a specific model. Pass it the model class, and it will return a
|
||||||
|
``Form`` class that contains a form field for each model field.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
>>> from django.newforms import form_for_model
|
||||||
|
|
||||||
|
# Create the form class.
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
|
||||||
|
# Create an empty form instance.
|
||||||
|
>>> f = ArticleForm()
|
||||||
|
|
||||||
|
It bears repeating that ``form_for_model()`` takes the model *class*, not a
|
||||||
|
model instance, and it returns a ``Form`` *class*, not a ``Form`` instance.
|
||||||
|
|
||||||
|
Field types
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The generated ``Form`` class will have a form field for every model field. Each
|
||||||
|
model field has a corresponding default form field. For example, a
|
||||||
|
``CharField`` on a model is represented as a ``CharField`` on a form. A
|
||||||
|
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
|
||||||
|
the full list of conversions:
|
||||||
|
|
||||||
|
=============================== ========================================
|
||||||
|
Model field Form field
|
||||||
|
=============================== ========================================
|
||||||
|
``AutoField`` Not represented in the form
|
||||||
|
``BooleanField`` ``BooleanField``
|
||||||
|
``CharField`` ``CharField`` with ``max_length`` set to
|
||||||
|
the model field's ``maxlength``
|
||||||
|
``CommaSeparatedIntegerField`` ``CharField``
|
||||||
|
``DateField`` ``DateField``
|
||||||
|
``DateTimeField`` ``DateTimeField``
|
||||||
|
``EmailField`` ``EmailField``
|
||||||
|
``FileField`` ``CharField``
|
||||||
|
``FilePathField`` ``CharField``
|
||||||
|
``FloatField`` ``CharField``
|
||||||
|
``ForeignKey`` ``ModelChoiceField`` (see below)
|
||||||
|
``ImageField`` ``CharField``
|
||||||
|
``IntegerField`` ``IntegerField``
|
||||||
|
``IPAddressField`` ``CharField``
|
||||||
|
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
||||||
|
below)
|
||||||
|
``NullBooleanField`` ``CharField``
|
||||||
|
``PhoneNumberField`` ``USPhoneNumberField``
|
||||||
|
(from ``django.contrib.localflavor.us``)
|
||||||
|
``PositiveIntegerField`` ``IntegerField``
|
||||||
|
``PositiveSmallIntegerField`` ``IntegerField``
|
||||||
|
``SlugField`` ``CharField``
|
||||||
|
``SmallIntegerField`` ``IntegerField``
|
||||||
|
``TextField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
``TimeField`` ``TimeField``
|
||||||
|
``URLField`` ``URLField`` with ``verify_exists`` set
|
||||||
|
to the model field's ``verify_exists``
|
||||||
|
``USStateField`` ``CharField`` with
|
||||||
|
``widget=USStateSelect``
|
||||||
|
(``USStateSelect`` is from
|
||||||
|
``django.contrib.localflavor.us``)
|
||||||
|
``XMLField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
=============================== ========================================
|
||||||
|
|
||||||
|
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
||||||
|
types are special cases:
|
||||||
|
|
||||||
|
* ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``,
|
||||||
|
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
* ``ManyToManyField`` is represented by
|
||||||
|
``django.newforms.ModelMultipleChoiceField``, which is a
|
||||||
|
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
In addition, each generated form field has attributes set as follows:
|
||||||
|
|
||||||
|
* If the model field has ``blank=True``, then ``required`` is set to
|
||||||
|
``False`` on the form field. Otherwise, ``required=True``.
|
||||||
|
|
||||||
|
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
||||||
|
field, with the first character capitalized.
|
||||||
|
|
||||||
|
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
||||||
|
field.
|
||||||
|
|
||||||
|
* If the model field has ``choices`` set, then the form field's ``widget``
|
||||||
|
will be set to ``Select``, with choices coming from the model field's
|
||||||
|
``choices``.
|
||||||
|
|
||||||
|
Finally, note that you can override the form field used for a given model
|
||||||
|
field. See "Overriding the default field types" below.
|
||||||
|
|
||||||
|
A full example
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Consider this set of models::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
TITLE_CHOICES = (
|
||||||
|
('MR', 'Mr.'),
|
||||||
|
('MRS', 'Mrs.'),
|
||||||
|
('MS', 'Ms.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Author(models.Model):
|
||||||
|
name = models.CharField(maxlength=100)
|
||||||
|
title = models.CharField(maxlength=3, choices=TITLE_CHOICES)
|
||||||
|
birth_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Book(models.Model):
|
||||||
|
name = models.CharField(maxlength=100)
|
||||||
|
authors = models.ManyToManyField(Author)
|
||||||
|
|
||||||
|
With these models, a call to ``form_for_model(Author)`` would return a ``Form``
|
||||||
|
class equivalent to this::
|
||||||
|
|
||||||
|
class AuthorForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
title = forms.CharField(max_length=3,
|
||||||
|
widget=forms.Select(choices=TITLE_CHOICES))
|
||||||
|
birth_date = forms.DateField(required=False)
|
||||||
|
|
||||||
|
A call to ``form_for_model(Book)`` would return a ``Form`` class equivalent to
|
||||||
|
this::
|
||||||
|
|
||||||
|
class BookForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
||||||
|
|
||||||
|
The ``save()`` method
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Every form produced by ``form_for_model()`` also has a ``save()`` method. This
|
||||||
|
method creates and saves a database object from the data bound to the form. For
|
||||||
|
example::
|
||||||
|
|
||||||
|
# Create a form instance from POST data.
|
||||||
|
>>> f = ArticleForm(request.POST)
|
||||||
|
|
||||||
|
# Save a new Article object from the form's data.
|
||||||
|
>>> new_article = f.save()
|
||||||
|
|
||||||
|
Note that ``save()`` will raise a ``ValueError`` if the data in the form
|
||||||
|
doesn't validate -- i.e., ``if form.errors``.
|
||||||
|
|
||||||
|
Using an alternate base class
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you want to add custom methods to the form generated by
|
||||||
|
``form_for_model()``, write a class that extends ``django.newforms.BaseForm``
|
||||||
|
and contains your custom methods. Then, use the ``form`` argument to
|
||||||
|
``form_for_model()`` to tell it to use your custom form as its base class.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Create the new base class.
|
||||||
|
>>> class MyBase(BaseForm):
|
||||||
|
... def my_method(self):
|
||||||
|
... # Do whatever the method does
|
||||||
|
|
||||||
|
# Create the form class with a different base class.
|
||||||
|
>>> ArticleForm = form_for_model(Article, form=MyBase)
|
||||||
|
|
||||||
|
# Instantiate the form.
|
||||||
|
>>> f = ArticleForm()
|
||||||
|
|
||||||
|
# Use the base class method.
|
||||||
|
>>> f.my_method()
|
||||||
|
|
||||||
|
Using a subset of fields on the form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
In some cases, you may not want all the model fields to appear on the generated
|
||||||
|
form. There are two ways of telling ``form_for_model()`` to use only a subset
|
||||||
|
of the model fields:
|
||||||
|
|
||||||
|
1. Set ``editable=False`` on the model field. As a result, *any* form
|
||||||
|
created from the model via ``form_for_model()`` will not include that
|
||||||
|
field.
|
||||||
|
|
||||||
|
2. Use the ``fields`` argument to ``form_for_model()``. This argument, if
|
||||||
|
given, should be a list of field names to include in the form.
|
||||||
|
|
||||||
|
For example, if you want a form for the ``Author`` model (defined above)
|
||||||
|
that includes only the ``name`` and ``title`` fields, you would specify
|
||||||
|
``fields`` like this::
|
||||||
|
|
||||||
|
PartialArticleForm = form_for_model(Author, fields=('name', 'title'))
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you specify ``fields`` when creating a form with ``form_for_model()``,
|
||||||
|
make sure that the fields that are *not* specified can provide default
|
||||||
|
values, or are allowed to have a value of ``None``. If a field isn't
|
||||||
|
specified on a form, the object created from the form can't provide
|
||||||
|
a value for that attribute, which will prevent the new instance from
|
||||||
|
being saved.
|
||||||
|
|
||||||
|
Overriding the default field types
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The default field types, as described in the "Field types" table above, are
|
||||||
|
sensible defaults; if you have a ``DateField`` in your model, chances are you'd
|
||||||
|
want that to be represented as a ``DateField`` in your form. But
|
||||||
|
``form_for_model()`` gives you the flexibility of changing the form field type
|
||||||
|
for a given model field. You do this by specifying a **formfield callback**.
|
||||||
|
|
||||||
|
A formfield callback is a function that, when provided with a model field,
|
||||||
|
returns a form field instance. When constructing a form, ``form_for_model()``
|
||||||
|
asks the formfield callback to provide form field types.
|
||||||
|
|
||||||
|
By default, ``form_for_model()`` calls the ``formfield()`` method on the model
|
||||||
|
field::
|
||||||
|
|
||||||
|
def default_callback(field, **kwargs):
|
||||||
|
return field.formfield(**kwargs)
|
||||||
|
|
||||||
|
The ``kwargs`` are any keyword arguments that might be passed to the form
|
||||||
|
field, such as ``required=True`` or ``label='Foo'``.
|
||||||
|
|
||||||
|
For example, if you wanted to use ``MyDateFormField`` for any ``DateField``
|
||||||
|
field on the model, you could define the callback::
|
||||||
|
|
||||||
|
>>> def my_callback(field, **kwargs):
|
||||||
|
... if isinstance(field, models.DateField):
|
||||||
|
... return MyDateFormField(**kwargs)
|
||||||
|
... else:
|
||||||
|
... return field.formfield(**kwargs)
|
||||||
|
|
||||||
|
>>> ArticleForm = form_for_model(formfield_callback=my_callback)
|
||||||
|
|
||||||
|
Note that your callback needs to handle *all* possible model field types, not
|
||||||
|
just the ones that you want to behave differently to the default. That's why
|
||||||
|
this example has an ``else`` clause that implements the default behavior.
|
||||||
|
|
||||||
|
Finding the model associated with a form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The model class that was used to construct the form is available
|
||||||
|
using the ``_model`` property of the generated form::
|
||||||
|
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
>>> ArticleForm._model
|
||||||
|
<class 'myapp.models.Article'>
|
||||||
|
|
||||||
|
``form_for_instance()``
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
``form_for_instance()`` is like ``form_for_model()``, but it takes a model
|
||||||
|
instance instead of a model class::
|
||||||
|
|
||||||
|
# Create an Author.
|
||||||
|
>>> a = Author(name='Joe Smith', title='MR', birth_date=None)
|
||||||
|
>>> a.save()
|
||||||
|
|
||||||
|
# Create a form for this particular Author.
|
||||||
|
>>> AuthorForm = form_for_instance(a)
|
||||||
|
|
||||||
|
# Instantiate the form.
|
||||||
|
>>> f = AuthorForm()
|
||||||
|
|
||||||
|
When a form created by ``form_for_instance()`` is created, the initial
|
||||||
|
data values for the form fields are drawn from the instance. However,
|
||||||
|
this data is not bound to the form. You will need to bind data to the
|
||||||
|
form before the form can be saved.
|
||||||
|
|
||||||
|
When you call ``save()`` on a form created by ``form_for_instance()``,
|
||||||
|
the database instance will be updated. As in ``form_for_model()``, ``save()``
|
||||||
|
will raise ``ValueError`` if the data doesn't validate.
|
||||||
|
|
||||||
|
``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback``
|
||||||
|
arguments that behave the same way as they do for ``form_for_model()``.
|
||||||
|
|
||||||
|
When should you use ``form_for_model()`` and ``form_for_instance()``?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``form_for_model()`` and ``form_for_instance()`` functions are meant to be
|
||||||
|
shortcuts for the common case. If you want to create a form whose fields map to
|
||||||
|
more than one model, or a form that contains fields that *aren't* on a model,
|
||||||
|
you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way
|
||||||
|
isn't that difficult, after all.
|
||||||
|
|
||||||
More coming soon
|
More coming soon
|
||||||
================
|
================
|
||||||
|
|
||||||
@ -880,6 +1418,3 @@ what's possible.
|
|||||||
|
|
||||||
If you're really itching to learn and use this library, please be patient.
|
If you're really itching to learn and use this library, please be patient.
|
||||||
We're working hard on finishing both the code and documentation.
|
We're working hard on finishing both the code and documentation.
|
||||||
|
|
||||||
Widgets
|
|
||||||
=======
|
|
||||||
|
@ -177,7 +177,7 @@ tools that can be used to establish tests and test conditions.
|
|||||||
|
|
||||||
* `Test Client`_
|
* `Test Client`_
|
||||||
* `TestCase`_
|
* `TestCase`_
|
||||||
* `Email services`_
|
* `E-mail services`_
|
||||||
|
|
||||||
Test Client
|
Test Client
|
||||||
-----------
|
-----------
|
||||||
@ -459,9 +459,9 @@ Emptying the test outbox
|
|||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
At the start of each test case, in addition to installing fixtures,
|
At the start of each test case, in addition to installing fixtures,
|
||||||
Django clears the contents of the test email outbox.
|
Django clears the contents of the test e-mail outbox.
|
||||||
|
|
||||||
For more detail on email services during tests, see `Email services`_.
|
For more detail on e-mail services during tests, see `E-mail services`_.
|
||||||
|
|
||||||
Assertions
|
Assertions
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
@ -502,16 +502,17 @@ that can be useful in testing the behavior of web sites.
|
|||||||
Assert that the template with the given name was used in rendering the
|
Assert that the template with the given name was used in rendering the
|
||||||
response.
|
response.
|
||||||
|
|
||||||
Email services
|
E-mail services
|
||||||
--------------
|
---------------
|
||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
If your view makes use of the `Django email services`_, you don't really
|
If your view makes use of the `Django e-mail services`_, you don't really
|
||||||
want email to be sent every time you run a test using that view.
|
want e-mail to be sent every time you run a test using that view.
|
||||||
|
|
||||||
When the Django test framework is initialized, it transparently replaces the
|
When the Django test framework is initialized, it transparently replaces the
|
||||||
normal `SMTPConnection`_ class with a dummy implementation that redirects all
|
normal `SMTPConnection`_ class with a dummy implementation that redirects all
|
||||||
email to a dummy outbox. This outbox, stored as ``django.core.mail.outbox``,
|
e-mail to a dummy outbox. This outbox, stored as ``django.core.mail.outbox``,
|
||||||
is a simple list of all `EmailMessage`_ instances that have been sent.
|
is a simple list of all `EmailMessage`_ instances that have been sent.
|
||||||
For example, during test conditions, it would be possible to run the following
|
For example, during test conditions, it would be possible to run the following
|
||||||
code::
|
code::
|
||||||
@ -541,7 +542,7 @@ to mail.outbox::
|
|||||||
# Empty the test outbox
|
# Empty the test outbox
|
||||||
mail.outbox = []
|
mail.outbox = []
|
||||||
|
|
||||||
.. _`Django email services`: ../email/
|
.. _`Django e-mail services`: ../email/
|
||||||
.. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes
|
.. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes
|
||||||
.. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes
|
.. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes
|
||||||
.. _`previously`: #emptying-the-test-outbox
|
.. _`previously`: #emptying-the-test-outbox
|
||||||
@ -669,7 +670,7 @@ a number of utility methods in the ``django.test.utils`` module.
|
|||||||
|
|
||||||
``teardown_test_environment()``
|
``teardown_test_environment()``
|
||||||
Performs any global post-test teardown, such as removing the instrumentation
|
Performs any global post-test teardown, such as removing the instrumentation
|
||||||
of the template rendering system and restoring normal email services.
|
of the template rendering system and restoring normal e-mail services.
|
||||||
|
|
||||||
``create_test_db(verbosity=1, autoclobber=False)``
|
``create_test_db(verbosity=1, autoclobber=False)``
|
||||||
Creates a new test database, and run ``syncdb`` against it.
|
Creates a new test database, and run ``syncdb`` against it.
|
||||||
|
@ -18,7 +18,7 @@ other Form, with one additional method: save(). The save()
|
|||||||
method updates the model instance. It also takes a commit=True parameter.
|
method updates the model instance. It also takes a commit=True parameter.
|
||||||
|
|
||||||
The function django.newforms.save_instance() takes a bound form instance and a
|
The function django.newforms.save_instance() takes a bound form instance and a
|
||||||
model instance and saves the form's clean_data into the instance. It also takes
|
model instance and saves the form's cleaned_data into the instance. It also takes
|
||||||
a commit=True parameter.
|
a commit=True parameter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ __test__ = {'API_TESTS': """
|
|||||||
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
|
>>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'url': u'entertainment', 'name': u'Entertainment'}
|
{'url': u'entertainment', 'name': u'Entertainment'}
|
||||||
>>> obj = f.save()
|
>>> obj = f.save()
|
||||||
>>> obj
|
>>> obj
|
||||||
@ -105,7 +105,7 @@ True
|
|||||||
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
|
>>> f = CategoryForm({'name': "It's a test", 'url': 'test'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'url': u'test', 'name': u"It's a test"}
|
{'url': u'test', 'name': u"It's a test"}
|
||||||
>>> obj = f.save()
|
>>> obj = f.save()
|
||||||
>>> obj
|
>>> obj
|
||||||
@ -119,7 +119,7 @@ save() on the resulting model instance.
|
|||||||
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
|
>>> f = CategoryForm({'name': 'Third test', 'url': 'third'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'url': u'third', 'name': u'Third test'}
|
{'url': u'third', 'name': u'Third test'}
|
||||||
>>> obj = f.save(commit=False)
|
>>> obj = f.save(commit=False)
|
||||||
>>> obj
|
>>> obj
|
||||||
@ -134,10 +134,10 @@ If you call save() with invalid data, you'll get a ValueError.
|
|||||||
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
>>> f = CategoryForm({'name': '', 'url': 'foo'})
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{'name': [u'This field is required.']}
|
{'name': [u'This field is required.']}
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'CategoryForm' object has no attribute 'clean_data'
|
AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
|
||||||
>>> f.save()
|
>>> f.save()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
@ -179,6 +179,18 @@ fields with the 'choices' attribute are represented by a ChoiceField.
|
|||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
|
</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
|
||||||
|
|
||||||
|
You can restrict a form to a subset of the complete list of fields
|
||||||
|
by providing a 'fields' argument. If you try to save a
|
||||||
|
model created with such a form, you need to ensure that the fields
|
||||||
|
that are _not_ on the form have default values, or are allowed to have
|
||||||
|
a value of None. If a field isn't specified on a form, the object created
|
||||||
|
from the form can't provide a value for that field!
|
||||||
|
>>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date'))
|
||||||
|
>>> f = PartialArticleForm(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>
|
||||||
|
|
||||||
You can pass a custom Form class to form_for_model. Make sure it's a
|
You can pass a custom Form class to form_for_model. Make sure it's a
|
||||||
subclass of BaseForm, not Form.
|
subclass of BaseForm, not Form.
|
||||||
>>> class CustomForm(BaseForm):
|
>>> class CustomForm(BaseForm):
|
||||||
@ -224,7 +236,23 @@ current values are inserted as 'initial' data in each Field.
|
|||||||
<option value="2">It's a test</option>
|
<option value="2">It's a test</option>
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'})
|
>>> f = TestArticleForm({'headline': u'Test headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'})
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> test_art = f.save()
|
||||||
|
>>> test_art.id
|
||||||
|
1
|
||||||
|
>>> test_art = Article.objects.get(id=1)
|
||||||
|
>>> test_art.headline
|
||||||
|
'Test headline'
|
||||||
|
|
||||||
|
You can create a form over a subset of the available fields
|
||||||
|
by specifying a 'fields' argument to form_for_instance.
|
||||||
|
>>> PartialArticleForm = form_for_instance(art, fields=('headline','pub_date'))
|
||||||
|
>>> f = PartialArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04'}, auto_id=False)
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
||||||
|
<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
@ -496,6 +524,6 @@ ValidationError: [u'Select a valid choice. 10 is not one of the available choice
|
|||||||
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
||||||
"""}
|
"""}
|
||||||
|
@ -38,14 +38,14 @@ the COUNT field appropriately.
|
|||||||
... }
|
... }
|
||||||
|
|
||||||
We treat FormSet pretty much like we would treat a normal Form. FormSet has an
|
We treat FormSet pretty much like we would treat a normal Form. FormSet has an
|
||||||
is_valid method, and a clean_data or errors attribute depending on whether all
|
is_valid method, and a cleaned_data or errors attribute depending on whether all
|
||||||
the forms passed validation. However, unlike a Form instance, clean_data and
|
the forms passed validation. However, unlike a Form instance, cleaned_data and
|
||||||
errors will be a list of dicts rather than just a single dict.
|
errors will be a list of dicts rather than just a single dict.
|
||||||
|
|
||||||
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
||||||
>>> formset.is_valid()
|
>>> formset.is_valid()
|
||||||
True
|
True
|
||||||
>>> formset.clean_data
|
>>> formset.cleaned_data
|
||||||
[{'votes': 100, 'choice': u'Calexico'}]
|
[{'votes': 100, 'choice': u'Calexico'}]
|
||||||
|
|
||||||
|
|
||||||
@ -64,12 +64,12 @@ False
|
|||||||
>>> formset.errors
|
>>> formset.errors
|
||||||
[{'votes': [u'This field is required.']}]
|
[{'votes': [u'This field is required.']}]
|
||||||
|
|
||||||
Like a Form instance, clean_data won't exist if the formset wasn't validated.
|
Like a Form instance, cleaned_data won't exist if the formset wasn't validated.
|
||||||
|
|
||||||
>>> formset.clean_data
|
>>> formset.cleaned_data
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'ChoiceFormSet' object has no attribute 'clean_data'
|
AttributeError: 'ChoiceFormSet' object has no attribute 'cleaned_data'
|
||||||
|
|
||||||
|
|
||||||
We can also prefill a FormSet with existing data by providing an ``initial``
|
We can also prefill a FormSet with existing data by providing an ``initial``
|
||||||
@ -99,7 +99,7 @@ Let's simulate what would happen if we submitted this form.
|
|||||||
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
||||||
>>> formset.is_valid()
|
>>> formset.is_valid()
|
||||||
True
|
True
|
||||||
>>> formset.clean_data
|
>>> formset.cleaned_data
|
||||||
[{'votes': 100, 'choice': u'Calexico'}]
|
[{'votes': 100, 'choice': u'Calexico'}]
|
||||||
|
|
||||||
But the second form was blank! Shouldn't we get some errors? No. If we display
|
But the second form was blank! Shouldn't we get some errors? No. If we display
|
||||||
@ -176,7 +176,7 @@ number of forms to be completed.
|
|||||||
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
||||||
>>> formset.is_valid()
|
>>> formset.is_valid()
|
||||||
True
|
True
|
||||||
>>> formset.clean_data
|
>>> formset.cleaned_data
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ We can just fill out one of the forms.
|
|||||||
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
||||||
>>> formset.is_valid()
|
>>> formset.is_valid()
|
||||||
True
|
True
|
||||||
>>> formset.clean_data
|
>>> formset.cleaned_data
|
||||||
[{'votes': 100, 'choice': u'Calexico'}]
|
[{'votes': 100, 'choice': u'Calexico'}]
|
||||||
|
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ False
|
|||||||
We can easily add deletion ability to a FormSet with an agrument to
|
We can easily add deletion ability to a FormSet with an agrument to
|
||||||
formset_for_form. This will add a boolean field to each form instance. When
|
formset_for_form. This will add a boolean field to each form instance. When
|
||||||
that boolean field is True, the cleaned data will be in formset.deleted_data
|
that boolean field is True, the cleaned data will be in formset.deleted_data
|
||||||
rather than formset.clean_data
|
rather than formset.cleaned_data
|
||||||
|
|
||||||
>>> ChoiceFormSet = formset_for_form(Choice, deletable=True)
|
>>> ChoiceFormSet = formset_for_form(Choice, deletable=True)
|
||||||
|
|
||||||
@ -299,7 +299,7 @@ To delete something, we just need to set that form's special delete field to
|
|||||||
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
||||||
>>> formset.is_valid()
|
>>> formset.is_valid()
|
||||||
True
|
True
|
||||||
>>> formset.clean_data
|
>>> formset.cleaned_data
|
||||||
[{'votes': 100, 'DELETE': False, 'choice': u'Calexico'}]
|
[{'votes': 100, 'DELETE': False, 'choice': u'Calexico'}]
|
||||||
>>> formset.deleted_data
|
>>> formset.deleted_data
|
||||||
[{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}]
|
[{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}]
|
||||||
@ -308,7 +308,7 @@ True
|
|||||||
|
|
||||||
We can also add ordering ability to a FormSet with an agrument to
|
We can also add ordering ability to a FormSet with an agrument to
|
||||||
formset_for_form. This will add a integer field to each form instance. When
|
formset_for_form. This will add a integer field to each form instance. When
|
||||||
form validation succeeds, formset.clean_data will have the data in the correct
|
form validation succeeds, formset.cleaned_data will have the data in the correct
|
||||||
order specified by the ordering fields. If a number is duplicated in the set
|
order specified by the ordering fields. If a number is duplicated in the set
|
||||||
of ordering fields, for instance form 0 and form 3 are both marked as 1, then
|
of ordering fields, for instance form 0 and form 3 are both marked as 1, then
|
||||||
the form index used as a secondary ordering criteria. In order to put
|
the form index used as a secondary ordering criteria. In order to put
|
||||||
@ -346,8 +346,8 @@ something at the front of the list, you'd need to set it's order to 0.
|
|||||||
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
||||||
>>> formset.is_valid()
|
>>> formset.is_valid()
|
||||||
True
|
True
|
||||||
>>> for clean_data in formset.clean_data:
|
>>> for cleaned_data in formset.cleaned_data:
|
||||||
... print clean_data
|
... print cleaned_data
|
||||||
{'votes': 500, 'ORDER': 0, 'choice': u'The Decemberists'}
|
{'votes': 500, 'ORDER': 0, 'choice': u'The Decemberists'}
|
||||||
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'}
|
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'}
|
||||||
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'}
|
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'}
|
||||||
@ -408,8 +408,8 @@ Let's delete Fergie, and put The Decemberists ahead of Calexico.
|
|||||||
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
|
||||||
>>> formset.is_valid()
|
>>> formset.is_valid()
|
||||||
True
|
True
|
||||||
>>> for clean_data in formset.clean_data:
|
>>> for cleaned_data in formset.cleaned_data:
|
||||||
... print clean_data
|
... print cleaned_data
|
||||||
{'votes': 500, 'DELETE': False, 'ORDER': 0, 'choice': u'The Decemberists'}
|
{'votes': 500, 'DELETE': False, 'ORDER': 0, 'choice': u'The Decemberists'}
|
||||||
{'votes': 100, 'DELETE': False, 'ORDER': 1, 'choice': u'Calexico'}
|
{'votes': 100, 'DELETE': False, 'ORDER': 1, 'choice': u'Calexico'}
|
||||||
>>> formset.deleted_data
|
>>> formset.deleted_data
|
||||||
|
@ -34,4 +34,18 @@ Unicode decoding problems...
|
|||||||
>>> f = SomeForm()
|
>>> f = SomeForm()
|
||||||
>>> f.as_p()
|
>>> f.as_p()
|
||||||
u'<p><label for="id_somechoice_0">Somechoice:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="0" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="1" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="2" name="somechoice" /> Nainen</label></li>\n</ul></p>'
|
u'<p><label for="id_somechoice_0">Somechoice:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="0" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="1" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="2" name="somechoice" /> Nainen</label></li>\n</ul></p>'
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# Miscellaneous Tests #
|
||||||
|
#######################
|
||||||
|
|
||||||
|
There once was a problem with Form fields called "data". Let's make sure that
|
||||||
|
doesn't come back.
|
||||||
|
>>> class DataForm(Form):
|
||||||
|
... data = CharField(max_length=10)
|
||||||
|
>>> f = DataForm({'data': 'xyzzy'})
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.cleaned_data
|
||||||
|
{'data': u'xyzzy'}
|
||||||
"""
|
"""
|
||||||
|
@ -1775,7 +1775,7 @@ True
|
|||||||
u''
|
u''
|
||||||
>>> p.errors.as_text()
|
>>> p.errors.as_text()
|
||||||
u''
|
u''
|
||||||
>>> p.clean_data
|
>>> p.cleaned_data
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
>>> print p['first_name']
|
>>> print p['first_name']
|
||||||
<input type="text" name="first_name" value="John" id="id_first_name" />
|
<input type="text" name="first_name" value="John" id="id_first_name" />
|
||||||
@ -1811,10 +1811,10 @@ True
|
|||||||
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
>>> p.clean_data
|
>>> p.cleaned_data
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'Person' object has no attribute 'clean_data'
|
AttributeError: 'Person' object has no attribute 'cleaned_data'
|
||||||
>>> print p
|
>>> print p
|
||||||
<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_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_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>
|
||||||
@ -1845,10 +1845,10 @@ False
|
|||||||
{}
|
{}
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
>>> p.clean_data
|
>>> p.cleaned_data
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'Person' object has no attribute 'clean_data'
|
AttributeError: 'Person' object has no attribute 'cleaned_data'
|
||||||
>>> print p
|
>>> print p
|
||||||
<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><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></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><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
|
||||||
@ -1887,10 +1887,10 @@ u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is re
|
|||||||
* This field is required.
|
* This field is required.
|
||||||
* birthday
|
* birthday
|
||||||
* This field is required.
|
* This field is required.
|
||||||
>>> p.clean_data
|
>>> p.cleaned_data
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AttributeError: 'Person' object has no attribute 'clean_data'
|
AttributeError: 'Person' object has no attribute 'cleaned_data'
|
||||||
>>> p['first_name'].errors
|
>>> p['first_name'].errors
|
||||||
[u'This field is required.']
|
[u'This field is required.']
|
||||||
>>> p['first_name'].errors.as_ul()
|
>>> p['first_name'].errors.as_ul()
|
||||||
@ -1906,17 +1906,45 @@ u'* This field is required.'
|
|||||||
>>> print p['birthday']
|
>>> print p['birthday']
|
||||||
<input type="text" name="birthday" id="id_birthday" />
|
<input type="text" name="birthday" id="id_birthday" />
|
||||||
|
|
||||||
clean_data will always *only* contain a key for fields defined in the
|
cleaned_data will always *only* contain a key for fields defined in the
|
||||||
Form, even if you pass extra data when you define the Form. In this
|
Form, even if you pass extra data when you define the Form. In this
|
||||||
example, we pass a bunch of extra fields to the form constructor,
|
example, we pass a bunch of extra fields to the form constructor,
|
||||||
but clean_data contains only the form's fields.
|
but cleaned_data contains only the form's fields.
|
||||||
>>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'}
|
>>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'}
|
||||||
>>> p = Person(data)
|
>>> p = Person(data)
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
True
|
True
|
||||||
>>> p.clean_data
|
>>> p.cleaned_data
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
|
|
||||||
|
cleaned_data will include a key and value for *all* fields defined in the Form,
|
||||||
|
even if the Form's data didn't include a value for fields that are not
|
||||||
|
required. In this example, the data dictionary doesn't include a value for the
|
||||||
|
"nick_name" field, but cleaned_data includes it. For CharFields, it's set to the
|
||||||
|
empty string.
|
||||||
|
>>> class OptionalPersonForm(Form):
|
||||||
|
... first_name = CharField()
|
||||||
|
... last_name = CharField()
|
||||||
|
... nick_name = CharField(required=False)
|
||||||
|
>>> data = {'first_name': u'John', 'last_name': u'Lennon'}
|
||||||
|
>>> f = OptionalPersonForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.cleaned_data
|
||||||
|
{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
|
||||||
|
|
||||||
|
For DateFields, it's set to None.
|
||||||
|
>>> class OptionalPersonForm(Form):
|
||||||
|
... first_name = CharField()
|
||||||
|
... last_name = CharField()
|
||||||
|
... birth_date = DateField(required=False)
|
||||||
|
>>> data = {'first_name': u'John', 'last_name': u'Lennon'}
|
||||||
|
>>> f = OptionalPersonForm(data)
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.cleaned_data
|
||||||
|
{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'}
|
||||||
|
|
||||||
"auto_id" tells the Form to add an "id" attribute to each form element.
|
"auto_id" tells the Form to add an "id" attribute to each form element.
|
||||||
If it's a string that contains '%s', Django will use that as a format string
|
If it's a string that contains '%s', Django will use that as a format string
|
||||||
into which the field's name will be inserted. It will also put a <label> around
|
into which the field's name will be inserted. It will also put a <label> around
|
||||||
@ -2265,19 +2293,19 @@ returns a list of input.
|
|||||||
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
|
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'composers': [u'J'], 'name': u'Yesterday'}
|
{'composers': [u'J'], 'name': u'Yesterday'}
|
||||||
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
|
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'composers': [u'J', u'P'], 'name': u'Yesterday'}
|
{'composers': [u'J', u'P'], 'name': u'Yesterday'}
|
||||||
|
|
||||||
Validation errors are HTML-escaped when output as HTML.
|
Validation errors are HTML-escaped when output as HTML.
|
||||||
>>> class EscapingForm(Form):
|
>>> class EscapingForm(Form):
|
||||||
... special_name = CharField()
|
... special_name = CharField()
|
||||||
... def clean_special_name(self):
|
... def clean_special_name(self):
|
||||||
... raise ValidationError("Something's wrong with '%s'" % self.clean_data['special_name'])
|
... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name'])
|
||||||
|
|
||||||
>>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False)
|
>>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
@ -2292,7 +2320,7 @@ There are a couple of ways to do multiple-field validation. If you want the
|
|||||||
validation message to be associated with a particular field, implement the
|
validation message to be associated with a particular field, implement the
|
||||||
clean_XXX() method on the Form, where XXX is the field name. As in
|
clean_XXX() method on the Form, where XXX is the field name. As in
|
||||||
Field.clean(), the clean_XXX() method should return the cleaned value. In the
|
Field.clean(), the clean_XXX() method should return the cleaned value. In the
|
||||||
clean_XXX() method, you have access to self.clean_data, which is a dictionary
|
clean_XXX() method, you have access to self.cleaned_data, which is a dictionary
|
||||||
of all the data that has been cleaned *so far*, in order by the fields,
|
of all the data that has been cleaned *so far*, in order by the fields,
|
||||||
including the current field (e.g., the field XXX if you're in clean_XXX()).
|
including the current field (e.g., the field XXX if you're in clean_XXX()).
|
||||||
>>> class UserRegistration(Form):
|
>>> class UserRegistration(Form):
|
||||||
@ -2300,9 +2328,9 @@ including the current field (e.g., the field XXX if you're in clean_XXX()).
|
|||||||
... password1 = CharField(widget=PasswordInput)
|
... password1 = CharField(widget=PasswordInput)
|
||||||
... password2 = CharField(widget=PasswordInput)
|
... password2 = CharField(widget=PasswordInput)
|
||||||
... def clean_password2(self):
|
... def clean_password2(self):
|
||||||
... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
|
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
|
||||||
... raise ValidationError(u'Please make sure your passwords match.')
|
... raise ValidationError(u'Please make sure your passwords match.')
|
||||||
... return self.clean_data['password2']
|
... return self.cleaned_data['password2']
|
||||||
>>> f = UserRegistration(auto_id=False)
|
>>> f = UserRegistration(auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
@ -2315,14 +2343,14 @@ including the current field (e.g., the field XXX if you're in clean_XXX()).
|
|||||||
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
||||||
|
|
||||||
Another way of doing multiple-field validation is by implementing the
|
Another way of doing multiple-field validation is by implementing the
|
||||||
Form's clean() method. If you do this, any ValidationError raised by that
|
Form's clean() method. If you do this, any ValidationError raised by that
|
||||||
method will not be associated with a particular field; it will have a
|
method will not be associated with a particular field; it will have a
|
||||||
special-case association with the field named '__all__'.
|
special-case association with the field named '__all__'.
|
||||||
Note that in Form.clean(), you have access to self.clean_data, a dictionary of
|
Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of
|
||||||
all the fields/values that have *not* raised a ValidationError. Also note
|
all the fields/values that have *not* raised a ValidationError. Also note
|
||||||
Form.clean() is required to return a dictionary of all clean data.
|
Form.clean() is required to return a dictionary of all clean data.
|
||||||
>>> class UserRegistration(Form):
|
>>> class UserRegistration(Form):
|
||||||
@ -2330,9 +2358,9 @@ Form.clean() is required to return a dictionary of all clean data.
|
|||||||
... password1 = CharField(widget=PasswordInput)
|
... password1 = CharField(widget=PasswordInput)
|
||||||
... password2 = CharField(widget=PasswordInput)
|
... password2 = CharField(widget=PasswordInput)
|
||||||
... def clean(self):
|
... def clean(self):
|
||||||
... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
|
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
|
||||||
... raise ValidationError(u'Please make sure your passwords match.')
|
... raise ValidationError(u'Please make sure your passwords match.')
|
||||||
... return self.clean_data
|
... return self.cleaned_data
|
||||||
>>> f = UserRegistration(auto_id=False)
|
>>> f = UserRegistration(auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
@ -2359,7 +2387,7 @@ Form.clean() is required to return a dictionary of all clean data.
|
|||||||
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
||||||
|
|
||||||
# Dynamic construction ########################################################
|
# Dynamic construction ########################################################
|
||||||
@ -2753,6 +2781,64 @@ then the latter will get precedence.
|
|||||||
<li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li>
|
<li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li>
|
||||||
<li>Password: <input type="password" name="password" /></li>
|
<li>Password: <input type="password" name="password" /></li>
|
||||||
|
|
||||||
|
# Callable initial data ########################################################
|
||||||
|
|
||||||
|
The previous technique dealt with raw values as initial data, but it's also
|
||||||
|
possible to specify callable data.
|
||||||
|
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10)
|
||||||
|
... password = CharField(widget=PasswordInput)
|
||||||
|
|
||||||
|
We need to define functions that get called later.
|
||||||
|
>>> def initial_django():
|
||||||
|
... return 'django'
|
||||||
|
>>> def initial_stephane():
|
||||||
|
... return 'stephane'
|
||||||
|
|
||||||
|
Here, we're not submitting any data, so the initial value will be displayed.
|
||||||
|
>>> p = UserRegistration(initial={'username': initial_django}, 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>
|
||||||
|
|
||||||
|
The 'initial' parameter is meaningless if you pass data.
|
||||||
|
>>> p = UserRegistration({}, initial={'username': initial_django}, 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''}, initial={'username': initial_django}, 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'}, initial={'username': initial_django}, 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>
|
||||||
|
|
||||||
|
A callable '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'}, initial={'username': initial_django})
|
||||||
|
>>> p.errors
|
||||||
|
{'username': [u'This field is required.']}
|
||||||
|
>>> p.is_valid()
|
||||||
|
False
|
||||||
|
|
||||||
|
If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(),
|
||||||
|
then the latter will get precedence.
|
||||||
|
>>> class UserRegistration(Form):
|
||||||
|
... username = CharField(max_length=10, initial=initial_django)
|
||||||
|
... password = CharField(widget=PasswordInput)
|
||||||
|
>>> 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>
|
||||||
|
>>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False)
|
||||||
|
>>> print p.as_ul()
|
||||||
|
<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li>
|
||||||
|
<li>Password: <input type="password" name="password" /></li>
|
||||||
|
|
||||||
# Help text ###################################################################
|
# Help text ###################################################################
|
||||||
|
|
||||||
You can specify descriptive text for a field by using the 'help_text' argument
|
You can specify descriptive text for a field by using the 'help_text' argument
|
||||||
@ -2869,7 +2955,7 @@ actual field name.
|
|||||||
{}
|
{}
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
True
|
True
|
||||||
>>> p.clean_data
|
>>> p.cleaned_data
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
|
|
||||||
Let's try submitting some bad data to make sure form.errors and field.errors
|
Let's try submitting some bad data to make sure form.errors and field.errors
|
||||||
@ -2913,12 +2999,12 @@ of the same form.
|
|||||||
>>> p1 = Person(data, prefix='person1')
|
>>> p1 = Person(data, prefix='person1')
|
||||||
>>> p1.is_valid()
|
>>> p1.is_valid()
|
||||||
True
|
True
|
||||||
>>> p1.clean_data
|
>>> p1.cleaned_data
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
>>> p2 = Person(data, prefix='person2')
|
>>> p2 = Person(data, prefix='person2')
|
||||||
>>> p2.is_valid()
|
>>> p2.is_valid()
|
||||||
True
|
True
|
||||||
>>> p2.clean_data
|
>>> p2.cleaned_data
|
||||||
{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
|
{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
|
||||||
|
|
||||||
By default, forms append a hyphen between the prefix and the field name, but a
|
By default, forms append a hyphen between the prefix and the field name, but a
|
||||||
@ -2944,7 +3030,7 @@ self.prefix.
|
|||||||
>>> p = Person(data, prefix='foo')
|
>>> p = Person(data, prefix='foo')
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
True
|
True
|
||||||
>>> p.clean_data
|
>>> p.cleaned_data
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||||
|
|
||||||
# Forms with NullBooleanFields ################################################
|
# Forms with NullBooleanFields ################################################
|
||||||
@ -3006,16 +3092,16 @@ is different than its data. This is handled transparently, though.
|
|||||||
... password1 = CharField(widget=PasswordInput)
|
... password1 = CharField(widget=PasswordInput)
|
||||||
... password2 = CharField(widget=PasswordInput)
|
... password2 = CharField(widget=PasswordInput)
|
||||||
... def clean(self):
|
... def clean(self):
|
||||||
... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
|
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
|
||||||
... raise ValidationError(u'Please make sure your passwords match.')
|
... raise ValidationError(u'Please make sure your passwords match.')
|
||||||
... return self.clean_data
|
... return self.cleaned_data
|
||||||
>>> def my_function(method, post_data):
|
>>> def my_function(method, post_data):
|
||||||
... if method == 'POST':
|
... if method == 'POST':
|
||||||
... form = UserRegistration(post_data, auto_id=False)
|
... form = UserRegistration(post_data, auto_id=False)
|
||||||
... else:
|
... else:
|
||||||
... form = UserRegistration(auto_id=False)
|
... form = UserRegistration(auto_id=False)
|
||||||
... if form.is_valid():
|
... if form.is_valid():
|
||||||
... return 'VALID: %r' % form.clean_data
|
... return 'VALID: %r' % form.cleaned_data
|
||||||
... t = Template('<form action="" method="post">\n<table>\n{{ form }}\n</table>\n<input type="submit" />\n</form>')
|
... t = Template('<form action="" method="post">\n<table>\n{{ form }}\n</table>\n<input type="submit" />\n</form>')
|
||||||
... return t.render(Context({'form': form}))
|
... return t.render(Context({'form': form}))
|
||||||
|
|
||||||
@ -3053,9 +3139,9 @@ VALID: {'username': u'adrian', 'password1': u'secret', 'password2': u'secret'}
|
|||||||
... password1 = CharField(widget=PasswordInput)
|
... password1 = CharField(widget=PasswordInput)
|
||||||
... password2 = CharField(widget=PasswordInput)
|
... password2 = CharField(widget=PasswordInput)
|
||||||
... def clean(self):
|
... def clean(self):
|
||||||
... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
|
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
|
||||||
... raise ValidationError(u'Please make sure your passwords match.')
|
... raise ValidationError(u'Please make sure your passwords match.')
|
||||||
... return self.clean_data
|
... return self.cleaned_data
|
||||||
|
|
||||||
You have full flexibility in displaying form fields in a template. Just pass a
|
You have full flexibility in displaying form fields in a template. Just pass a
|
||||||
Form instance to the template, and use "dot" access to refer to individual
|
Form instance to the template, and use "dot" access to refer to individual
|
||||||
@ -3405,7 +3491,7 @@ ValidationError: [u'This field is required.']
|
|||||||
</select>
|
</select>
|
||||||
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr>
|
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr>
|
||||||
|
|
||||||
>>> f.clean_data
|
>>> f.cleaned_data
|
||||||
{'field1': u'some text,JP,2007-04-25 06:24:00'}
|
{'field1': u'some text,JP,2007-04-25 06:24:00'}
|
||||||
|
|
||||||
#################################
|
#################################
|
||||||
|
@ -101,6 +101,12 @@ class Anchor(models.Model):
|
|||||||
|
|
||||||
data = models.CharField(maxlength=30)
|
data = models.CharField(maxlength=30)
|
||||||
|
|
||||||
|
class UniqueAnchor(models.Model):
|
||||||
|
"""This is a model that can be used as
|
||||||
|
something for other models to point at"""
|
||||||
|
|
||||||
|
data = models.CharField(unique=True, maxlength=30)
|
||||||
|
|
||||||
class FKData(models.Model):
|
class FKData(models.Model):
|
||||||
data = models.ForeignKey(Anchor, null=True)
|
data = models.ForeignKey(Anchor, null=True)
|
||||||
|
|
||||||
@ -116,6 +122,10 @@ class FKSelfData(models.Model):
|
|||||||
class M2MSelfData(models.Model):
|
class M2MSelfData(models.Model):
|
||||||
data = models.ManyToManyField('self', null=True, symmetrical=False)
|
data = models.ManyToManyField('self', null=True, symmetrical=False)
|
||||||
|
|
||||||
|
|
||||||
|
class FKDataToField(models.Model):
|
||||||
|
data = models.ForeignKey(UniqueAnchor, null=True, to_field='data')
|
||||||
|
|
||||||
# The following test classes are for validating the
|
# The following test classes are for validating the
|
||||||
# deserialization of objects that use a user-defined
|
# deserialization of objects that use a user-defined
|
||||||
# field as the primary key.
|
# field as the primary key.
|
||||||
|
@ -159,6 +159,7 @@ The end."""),
|
|||||||
|
|
||||||
(data_obj, 300, Anchor, "Anchor 1"),
|
(data_obj, 300, Anchor, "Anchor 1"),
|
||||||
(data_obj, 301, Anchor, "Anchor 2"),
|
(data_obj, 301, Anchor, "Anchor 2"),
|
||||||
|
(data_obj, 302, UniqueAnchor, "UAnchor 1"),
|
||||||
|
|
||||||
(fk_obj, 400, FKData, 300), # Post reference
|
(fk_obj, 400, FKData, 300), # Post reference
|
||||||
(fk_obj, 401, FKData, 500), # Pre reference
|
(fk_obj, 401, FKData, 500), # Pre reference
|
||||||
@ -184,8 +185,13 @@ The end."""),
|
|||||||
(m2m_obj, 445, M2MSelfData, []),
|
(m2m_obj, 445, M2MSelfData, []),
|
||||||
(m2m_obj, 446, M2MSelfData, []),
|
(m2m_obj, 446, M2MSelfData, []),
|
||||||
|
|
||||||
|
(fk_obj, 450, FKDataToField, "UAnchor 1"),
|
||||||
|
(fk_obj, 451, FKDataToField, "UAnchor 2"),
|
||||||
|
(fk_obj, 452, FKDataToField, None),
|
||||||
|
|
||||||
(data_obj, 500, Anchor, "Anchor 3"),
|
(data_obj, 500, Anchor, "Anchor 3"),
|
||||||
(data_obj, 501, Anchor, "Anchor 4"),
|
(data_obj, 501, Anchor, "Anchor 4"),
|
||||||
|
(data_obj, 502, UniqueAnchor, "UAnchor 2"),
|
||||||
|
|
||||||
(pk_obj, 601, BooleanPKData, True),
|
(pk_obj, 601, BooleanPKData, True),
|
||||||
(pk_obj, 602, BooleanPKData, False),
|
(pk_obj, 602, BooleanPKData, False),
|
||||||
|
@ -61,6 +61,34 @@ class AssertTemplateUsedTests(TestCase):
|
|||||||
except AssertionError, e:
|
except AssertionError, e:
|
||||||
self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']")
|
self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']")
|
||||||
|
|
||||||
|
class AssertRedirectsTests(TestCase):
|
||||||
|
def test_redirect_page(self):
|
||||||
|
"An assertion is raised if the original page couldn't be retrieved as expected"
|
||||||
|
# This page will redirect with code 301, not 302
|
||||||
|
response = self.client.get('/test_client/permanent_redirect_view/')
|
||||||
|
try:
|
||||||
|
self.assertRedirects(response, '/test_client/get_view/')
|
||||||
|
except AssertionError, e:
|
||||||
|
self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
|
||||||
|
|
||||||
|
def test_incorrect_target(self):
|
||||||
|
"An assertion is raised if the response redirects to another target"
|
||||||
|
response = self.client.get('/test_client/permanent_redirect_view/')
|
||||||
|
try:
|
||||||
|
# Should redirect to get_view
|
||||||
|
self.assertRedirects(response, '/test_client/some_view/')
|
||||||
|
except AssertionError, e:
|
||||||
|
self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
|
||||||
|
|
||||||
|
def test_target_page(self):
|
||||||
|
"An assertion is raised if the reponse redirect target cannot be retrieved as expected"
|
||||||
|
response = self.client.get('/test_client/double_redirect_view/')
|
||||||
|
try:
|
||||||
|
# The redirect target responds with a 301 code, not 200
|
||||||
|
self.assertRedirects(response, '/test_client/permanent_redirect_view/')
|
||||||
|
except AssertionError, e:
|
||||||
|
self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)")
|
||||||
|
|
||||||
class AssertFormErrorTests(TestCase):
|
class AssertFormErrorTests(TestCase):
|
||||||
def test_unknown_form(self):
|
def test_unknown_form(self):
|
||||||
"An assertion is raised if the form name is unknown"
|
"An assertion is raised if the form name is unknown"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user