mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
boulder-oracle-sprint: Merged to [5234]
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5235 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
bcc26d8eea
commit
c53378eb4b
2
AUTHORS
2
AUTHORS
@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
flavio.curella@gmail.com
|
||||
Jure Cuhalev <gandalf@owca.info>
|
||||
dackze+django@gmail.com
|
||||
David Danier <goliath.mailinglist@gmx.de>
|
||||
Dirk Datzert <dummy@habmalnefrage.de>
|
||||
Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/>
|
||||
dave@thebarproject.com
|
||||
@ -85,6 +86,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Alex Dedul
|
||||
deric@monowerks.com
|
||||
Max Derkachev <mderk@yandex.ru>
|
||||
Jordan Dimov <s3x3y1@gmail.com>
|
||||
dne@mayonnaise.net
|
||||
Maximillian Dornseif <md@hudora.de>
|
||||
Jeremy Dunck <http://dunck.us/>
|
||||
|
@ -38,6 +38,7 @@ LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGES = (
|
||||
('ar', gettext_noop('Arabic')),
|
||||
('bn', gettext_noop('Bengali')),
|
||||
('bg', gettext_noop('Bulgarian')),
|
||||
('ca', gettext_noop('Catalan')),
|
||||
('cs', gettext_noop('Czech')),
|
||||
('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 "Утре"
|
||||
|
@ -41,7 +41,7 @@ class ISIdNumberField(RegexField):
|
||||
method is modulo 11.
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
|
@ -1463,21 +1463,25 @@ def load_data(fixture_labels, verbosity=1):
|
||||
if verbosity > 1:
|
||||
print "No %s fixture '%s' in %s." % \
|
||||
(format, fixture_name, humanize(fixture_dir))
|
||||
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)
|
||||
|
||||
if count[0] > 0:
|
||||
sequence_sql = backend.get_sql_sequence_reset(style, models)
|
||||
if sequence_sql:
|
||||
if verbosity > 1:
|
||||
print "Resetting sequences"
|
||||
for line in sequence_sql:
|
||||
cursor.execute(line)
|
||||
|
||||
transaction.commit()
|
||||
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.args = "[--verbosity] fixture, fixture, ..."
|
||||
|
||||
|
@ -37,7 +37,7 @@ class Serializer(base.Serializer):
|
||||
def handle_fk_field(self, obj, field):
|
||||
related = getattr(obj, field.name)
|
||||
if related is not None:
|
||||
related = related._get_pk_val()
|
||||
related = getattr(related, field.rel.field_name)
|
||||
self._current[field.name] = related
|
||||
|
||||
def handle_m2m_field(self, obj, field):
|
||||
@ -80,7 +80,10 @@ def Deserializer(object_list, **options):
|
||||
|
||||
# Handle FK fields
|
||||
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
|
||||
else:
|
||||
|
@ -82,7 +82,7 @@ class Serializer(base.Serializer):
|
||||
self._start_relational_field(field)
|
||||
related = getattr(obj, field.name)
|
||||
if related is not None:
|
||||
self.xml.characters(str(related._get_pk_val()))
|
||||
self.xml.characters(str(getattr(related, field.rel.field_name)))
|
||||
else:
|
||||
self.xml.addQuickElement("None")
|
||||
self.xml.endElement("field")
|
||||
@ -181,7 +181,7 @@ class Deserializer(base.Deserializer):
|
||||
if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
|
||||
return None
|
||||
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))
|
||||
|
||||
def _handle_m2m_field_node(self, node, field):
|
||||
|
@ -242,7 +242,7 @@ def get_sql_sequence_reset(style, model_list):
|
||||
if isinstance(f, models.AutoField):
|
||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||
(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_FIELD(quote_name(f.column)),
|
||||
style.SQL_KEYWORD('FROM'),
|
||||
@ -251,7 +251,7 @@ def get_sql_sequence_reset(style, model_list):
|
||||
for f in model._meta.many_to_many:
|
||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||
(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_FIELD(quote_name('id')),
|
||||
style.SQL_KEYWORD('FROM'),
|
||||
|
@ -199,7 +199,7 @@ def get_sql_sequence_reset(style, model_list):
|
||||
if isinstance(f, models.AutoField):
|
||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||
(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_FIELD(quote_name(f.column)),
|
||||
style.SQL_KEYWORD('FROM'),
|
||||
@ -208,7 +208,7 @@ def get_sql_sequence_reset(style, model_list):
|
||||
for f in model._meta.many_to_many:
|
||||
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
|
||||
(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_FIELD(quote_name('id')),
|
||||
style.SQL_KEYWORD('FROM'),
|
||||
|
@ -184,8 +184,8 @@ class BaseForm(StrAndUnicode):
|
||||
try:
|
||||
value = field.clean(value)
|
||||
self.clean_data[name] = value
|
||||
if hasattr(self, 'clean_%s' % name):
|
||||
value = getattr(self, 'clean_%s' % name)()
|
||||
if hasattr(self, 'do_clean_%s' % name):
|
||||
value = getattr(self, 'do_clean_%s' % name)()
|
||||
self.clean_data[name] = value
|
||||
except ValidationError, e:
|
||||
errors[name] = e.messages
|
||||
@ -255,6 +255,8 @@ class BoundField(StrAndUnicode):
|
||||
attrs['id'] = auto_id
|
||||
if not self.form.is_bound:
|
||||
data = self.form.initial.get(self.name, self.field.initial)
|
||||
if callable(data):
|
||||
data = data()
|
||||
else:
|
||||
data = self.data
|
||||
return widget.render(self.html_name, data, attrs=attrs)
|
||||
|
@ -12,17 +12,7 @@ from widgets import Select, SelectMultiple, MultipleHiddenInput
|
||||
__all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
||||
'ModelChoiceField', 'ModelMultipleChoiceField')
|
||||
|
||||
def model_save(self, commit=True):
|
||||
"""
|
||||
Creates and returns model instance according to self.clean_data.
|
||||
|
||||
This method is created for any form_for_model Form.
|
||||
"""
|
||||
if self.errors:
|
||||
raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
|
||||
return save_instance(self, self._model(), commit)
|
||||
|
||||
def save_instance(form, instance, commit=True):
|
||||
def save_instance(form, instance, fields=None, fail_message='saved', commit=True):
|
||||
"""
|
||||
Saves bound Form ``form``'s clean_data into model instance ``instance``.
|
||||
|
||||
@ -33,15 +23,19 @@ def save_instance(form, instance, commit=True):
|
||||
from django.db import models
|
||||
opts = instance.__class__._meta
|
||||
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
|
||||
for f in opts.fields:
|
||||
if not f.editable or isinstance(f, models.AutoField) or not f.name in clean_data:
|
||||
continue
|
||||
if fields and f.name not in fields:
|
||||
continue
|
||||
setattr(instance, f.name, clean_data[f.name])
|
||||
if commit:
|
||||
instance.save()
|
||||
for f in opts.many_to_many:
|
||||
if fields and f.name not in fields:
|
||||
continue
|
||||
if f.name in clean_data:
|
||||
setattr(instance, f.attname, clean_data[f.name])
|
||||
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
|
||||
@ -50,13 +44,19 @@ def save_instance(form, instance, commit=True):
|
||||
# exception in that case.
|
||||
return instance
|
||||
|
||||
def make_instance_save(instance):
|
||||
"Returns the save() method for a form_for_instance Form."
|
||||
def make_model_save(model, fields, fail_message):
|
||||
"Returns the save() method for a Form."
|
||||
def save(self, commit=True):
|
||||
return save_instance(self, instance, commit)
|
||||
return save_instance(self, model(), fields, fail_message, commit)
|
||||
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.
|
||||
|
||||
@ -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:
|
||||
if not f.editable:
|
||||
continue
|
||||
if fields and not f.name in fields:
|
||||
continue
|
||||
formfield = formfield_callback(f)
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
|
||||
base_fields = SortedDictFromList(field_list)
|
||||
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.
|
||||
|
||||
@ -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:
|
||||
if not f.editable:
|
||||
continue
|
||||
if fields and not f.name in fields:
|
||||
continue
|
||||
current_value = f.value_from_object(instance)
|
||||
formfield = formfield_callback(f, initial=current_value)
|
||||
if formfield:
|
||||
field_list.append((f.name, formfield))
|
||||
fields = SortedDictFromList(field_list)
|
||||
base_fields = SortedDictFromList(field_list)
|
||||
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):
|
||||
"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)
|
||||
mail.outbox = []
|
||||
|
||||
def run(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
|
||||
to super().setUp().
|
||||
|
||||
def __call__(self, result=None):
|
||||
"""
|
||||
Wrapper around default __call__ method to perform common Django test
|
||||
set up. This means that user-defined Test Cases aren't required to
|
||||
include a call to super().setUp().
|
||||
"""
|
||||
self.client = Client()
|
||||
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):
|
||||
"""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,
|
||||
"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))
|
||||
scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
|
||||
self.assertEqual(path, expected_path,
|
||||
@ -70,7 +70,7 @@ class TestCase(unittest.TestCase):
|
||||
redirect_response = self.client.get(path)
|
||||
self.assertEqual(redirect_response.status_code, target_status_code,
|
||||
"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):
|
||||
"""Assert that a response indicates that a page was retreived successfully,
|
||||
@ -108,7 +108,7 @@ class TestCase(unittest.TestCase):
|
||||
for err in errors:
|
||||
if field:
|
||||
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)" %
|
||||
(field, form, i, err, list(context[form].errors[field])))
|
||||
elif field in context[form].fields:
|
||||
@ -117,7 +117,7 @@ class TestCase(unittest.TestCase):
|
||||
else:
|
||||
self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
|
||||
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)" %
|
||||
(form, i, err, list(context[form].non_field_errors())))
|
||||
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"
|
||||
if isinstance(response.template, list):
|
||||
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_name, template_names))
|
||||
elif response.template:
|
||||
@ -140,7 +140,7 @@ class TestCase(unittest.TestCase):
|
||||
def assertTemplateNotUsed(self, response, template_name):
|
||||
"Assert that the template with the provided name was NOT used in rendering the response"
|
||||
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)
|
||||
elif response.template:
|
||||
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
|
||||
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),
|
||||
|
||||
|
@ -236,7 +236,7 @@ To pluralize, specify both the singular and plural forms with the
|
||||
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and
|
||||
``{% endblocktrans %}``. Example::
|
||||
|
||||
{% blocktrans count list|count as counter %}
|
||||
{% blocktrans count list|length as counter %}
|
||||
There is only one {{ name }} object.
|
||||
{% plural %}
|
||||
There are {{ counter }} {{ name }} objects.
|
||||
|
@ -9,28 +9,30 @@ framework. This document explains how to use this new library.
|
||||
Migration plan
|
||||
==============
|
||||
|
||||
``django.newforms`` currently is only available in Django beginning
|
||||
with the 0.96 release. the Django development version -- i.e., it's
|
||||
not available in the Django 0.95 release. For the next Django release,
|
||||
our plan is to do the following:
|
||||
``django.newforms`` is new in Django's 0.96 release, but, as it won't be new
|
||||
forever, we plan to rename it to ``django.forms`` in the future. The current
|
||||
``django.forms`` package will be available as ``django.oldforms`` until Django
|
||||
1.0, when we plan to remove it for good.
|
||||
|
||||
* As of revision [4208], we've copied the current ``django.forms`` to
|
||||
``django.oldforms``. This allows you to upgrade your code *now* rather
|
||||
than waiting for the backwards-incompatible change and rushing to fix
|
||||
your code after the fact. Just change your import statements like this::
|
||||
That has direct repercussions on the forward compatibility of your code. Please
|
||||
read the following migration plan and code accordingly:
|
||||
|
||||
* 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 oldforms as forms # new
|
||||
|
||||
* At an undecided future date, we will move the current ``django.newforms``
|
||||
to ``django.forms``. This will be a backwards-incompatible change, and
|
||||
anybody who is still using the old version of ``django.forms`` at that
|
||||
time will need to change their import statements, as described in the
|
||||
previous bullet.
|
||||
* In the next Django release (0.97), we will move the current
|
||||
``django.newforms`` to ``django.forms``. This will be a
|
||||
backwards-incompatible change, and anybody who is still using the old
|
||||
version of ``django.forms`` at that time will need to change their import
|
||||
statements, as described in the previous bullet.
|
||||
|
||||
* We will remove ``django.oldforms`` in the release *after* the next Django
|
||||
release -- the release that comes after the release in which we're
|
||||
creating the new ``django.forms``.
|
||||
release -- either 0.98 or 1.0, whichever comes first.
|
||||
|
||||
With this in mind, we recommend you use the following import statement when
|
||||
using ``django.newforms``::
|
||||
@ -184,7 +186,7 @@ e-mail address::
|
||||
>>> f.is_valid()
|
||||
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
|
||||
{'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
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -274,6 +280,27 @@ but ``clean_data`` contains only the form's fields::
|
||||
>>> f.clean_data # Doesn't contain extra_field_1, etc.
|
||||
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
|
||||
|
||||
``clean_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 ``clean_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.clean_data
|
||||
{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
|
||||
|
||||
In this above example, the ``clean_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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -454,7 +481,7 @@ field::
|
||||
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``
|
||||
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::
|
||||
|
||||
>>> 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
|
||||
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
|
||||
positioning of the error messages depends on the output method you're using::
|
||||
will include the validation errors as a ``<ul class="errorlist">`` near the
|
||||
field. The particular positioning of the error messages depends on the output
|
||||
method you're using::
|
||||
|
||||
>>> data = {'subject': '',
|
||||
... '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" />
|
||||
|
||||
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': ''}
|
||||
>>> f = ContactForm(data, auto_id=False)
|
||||
@ -646,7 +675,7 @@ Core field arguments
|
||||
|
||||
Each ``Field`` class constructor takes at least these arguments. Some
|
||||
``Field`` classes take additional, field-specific arguments, but the following
|
||||
should *always* be available:
|
||||
should *always* be accepted:
|
||||
|
||||
``required``
|
||||
~~~~~~~~~~~~
|
||||
@ -704,7 +733,7 @@ field.)
|
||||
The ``label`` argument lets you specify the "human-friendly" label for this
|
||||
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
|
||||
spaces and upper-casing the first letter. Specify ``label`` if that default
|
||||
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
|
||||
rendering this ``Field``. See _`Widgets` below for more information.
|
||||
rendering this ``Field``. See "Widgets" below for more information.
|
||||
|
||||
``help_text``
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
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`` 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
|
||||
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>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
|
||||
----------------------
|
||||
|
||||
@ -870,6 +1106,308 @@ custom ``Field`` classes. To do this, just create a subclass of
|
||||
mentioned above (``required``, ``label``, ``initial``, ``widget``,
|
||||
``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
|
||||
================
|
||||
|
||||
@ -880,6 +1418,3 @@ what's possible.
|
||||
|
||||
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.
|
||||
|
||||
Widgets
|
||||
=======
|
||||
|
@ -177,7 +177,7 @@ tools that can be used to establish tests and test conditions.
|
||||
|
||||
* `Test Client`_
|
||||
* `TestCase`_
|
||||
* `Email services`_
|
||||
* `E-mail services`_
|
||||
|
||||
Test Client
|
||||
-----------
|
||||
@ -459,9 +459,9 @@ Emptying the test outbox
|
||||
**New in Django development version**
|
||||
|
||||
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
|
||||
~~~~~~~~~~
|
||||
@ -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
|
||||
response.
|
||||
|
||||
Email services
|
||||
--------------
|
||||
E-mail services
|
||||
---------------
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
If your view makes use of the `Django email services`_, you don't really
|
||||
want email to be sent every time you run a test using that view.
|
||||
If your view makes use of the `Django e-mail services`_, you don't really
|
||||
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
|
||||
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.
|
||||
For example, during test conditions, it would be possible to run the following
|
||||
code::
|
||||
@ -541,7 +542,7 @@ to mail.outbox::
|
||||
# Empty the test outbox
|
||||
mail.outbox = []
|
||||
|
||||
.. _`Django email services`: ../email/
|
||||
.. _`Django e-mail services`: ../email/
|
||||
.. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes
|
||||
.. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes
|
||||
.. _`previously`: #emptying-the-test-outbox
|
||||
@ -669,7 +670,7 @@ a number of utility methods in the ``django.test.utils`` module.
|
||||
|
||||
``teardown_test_environment()``
|
||||
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)``
|
||||
Creates a new test database, and run ``syncdb`` against it.
|
||||
|
@ -179,6 +179,18 @@ fields with the 'choices' attribute are represented by a ChoiceField.
|
||||
<option value="3">Third test</option>
|
||||
</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
|
||||
subclass of BaseForm, not Form.
|
||||
>>> 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="3">Third test</option>
|
||||
</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()
|
||||
True
|
||||
>>> new_art = f.save()
|
||||
|
@ -34,4 +34,18 @@ Unicode decoding problems...
|
||||
>>> f = SomeForm()
|
||||
>>> 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>'
|
||||
|
||||
#######################
|
||||
# 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.clean_data
|
||||
{'data': u'xyzzy'}
|
||||
"""
|
||||
|
@ -1916,6 +1916,34 @@ True
|
||||
>>> p.clean_data
|
||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
||||
|
||||
clean_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 clean_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.clean_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.clean_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.
|
||||
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
|
||||
@ -2275,7 +2303,7 @@ returns a list of input.
|
||||
Validation errors are HTML-escaped when output as HTML.
|
||||
>>> class EscapingForm(Form):
|
||||
... special_name = CharField()
|
||||
... def clean_special_name(self):
|
||||
... def do_clean_special_name(self):
|
||||
... raise ValidationError("Something's wrong with '%s'" % self.clean_data['special_name'])
|
||||
|
||||
>>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False)
|
||||
@ -2298,7 +2326,7 @@ including the current field (e.g., the field XXX if you're in clean_XXX()).
|
||||
... username = CharField(max_length=10)
|
||||
... password1 = CharField(widget=PasswordInput)
|
||||
... password2 = CharField(widget=PasswordInput)
|
||||
... def clean_password2(self):
|
||||
... def do_clean_password2(self):
|
||||
... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
|
||||
... raise ValidationError(u'Please make sure your passwords match.')
|
||||
... return self.clean_data['password2']
|
||||
@ -2752,6 +2780,64 @@ then the latter will get precedence.
|
||||
<li>Username: <input type="text" name="username" value="babik" maxlength="10" /></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 ###################################################################
|
||||
|
||||
You can specify descriptive text for a field by using the 'help_text' argument
|
||||
|
@ -101,6 +101,12 @@ class Anchor(models.Model):
|
||||
|
||||
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):
|
||||
data = models.ForeignKey(Anchor, null=True)
|
||||
|
||||
@ -116,6 +122,10 @@ class FKSelfData(models.Model):
|
||||
class M2MSelfData(models.Model):
|
||||
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
|
||||
# deserialization of objects that use a user-defined
|
||||
# field as the primary key.
|
||||
|
@ -159,6 +159,7 @@ The end."""),
|
||||
|
||||
(data_obj, 300, Anchor, "Anchor 1"),
|
||||
(data_obj, 301, Anchor, "Anchor 2"),
|
||||
(data_obj, 302, UniqueAnchor, "UAnchor 1"),
|
||||
|
||||
(fk_obj, 400, FKData, 300), # Post reference
|
||||
(fk_obj, 401, FKData, 500), # Pre reference
|
||||
@ -184,8 +185,13 @@ The end."""),
|
||||
(m2m_obj, 445, 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, 501, Anchor, "Anchor 4"),
|
||||
(data_obj, 502, UniqueAnchor, "UAnchor 2"),
|
||||
|
||||
(pk_obj, 601, BooleanPKData, True),
|
||||
(pk_obj, 602, BooleanPKData, False),
|
||||
|
@ -61,6 +61,34 @@ class AssertTemplateUsedTests(TestCase):
|
||||
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']")
|
||||
|
||||
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):
|
||||
def test_unknown_form(self):
|
||||
"An assertion is raised if the form name is unknown"
|
||||
|
Loading…
x
Reference in New Issue
Block a user