1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

unicode: Merged from trunk up to [5213].

git-svn-id: http://code.djangoproject.com/svn/django/branches/unicode@5214 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-05-13 06:23:30 +00:00
parent bc044651aa
commit 28f66bb097
21 changed files with 544 additions and 131 deletions

View File

@ -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

View File

@ -1344,7 +1344,7 @@ msgstr "四月"
#: utils/dates.py:19 #: utils/dates.py:19
msgid "may" msgid "may"
msgstr "月" msgstr "月"
#: utils/dates.py:19 #: utils/dates.py:19
msgid "jun" msgid "jun"

View File

@ -46,7 +46,7 @@ msgstr "清除全部"
#: contrib/admin/media/js/dateparse.js:32 #: contrib/admin/media/js/dateparse.js:32
#: contrib/admin/media/js/calendar.js:24 #: contrib/admin/media/js/calendar.js:24
msgid "January February March April May June July August September October November December" msgid "January February March April May June July August September October November December"
msgstr "一月 二月 三月 四月 五月 六月 六月 七月 八月 九月 十月 十一月 十二月" msgstr "一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月"
#: contrib/admin/media/js/dateparse.js:33 #: contrib/admin/media/js/dateparse.js:33
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"

View File

@ -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):
""" """

View File

@ -59,8 +59,11 @@ def words(count, common=True):
word_list = [] word_list = []
c = len(word_list) c = len(word_list)
if count > c: if count > c:
count = min(count - c, len(WORDS)) count -= c
word_list += random.sample(WORDS, count - c) while count > 0:
c = min(count, len(WORDS))
count -= c
word_list += random.sample(WORDS, c)
else: else:
word_list = word_list[:count] word_list = word_list[:count]
return ' '.join(word_list) return ' '.join(word_list)

View File

@ -233,7 +233,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'),
@ -242,7 +242,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'),

View File

@ -184,7 +184,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'),
@ -193,7 +193,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'),

View File

@ -108,6 +108,10 @@ class QueryDict(MultiValueDict):
self._assert_mutable() self._assert_mutable()
MultiValueDict.__setitem__(self, key, value) MultiValueDict.__setitem__(self, key, value)
def __delitem__(self, key):
self._assert_mutable()
super(QueryDict, self).__delitem__(key)
def get(self, key, default=None): def get(self, key, default=None):
return str_to_unicode(MultiValueDict.get(self, key, default), self.encoding) return str_to_unicode(MultiValueDict.get(self, key, default), self.encoding)

View File

@ -255,6 +255,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)

View File

@ -12,17 +12,7 @@ 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.
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``. 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 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 clean_data = form.clean_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 clean_data:
continue continue
if fields and f.name not in fields:
continue
setattr(instance, f.name, clean_data[f.name]) setattr(instance, f.name, clean_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 fields and f.name not in fields:
continue
if f.name in clean_data: if f.name in clean_data:
setattr(instance, f.attname, clean_data[f.name]) setattr(instance, f.attname, clean_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
@ -50,13 +44,19 @@ def save_instance(form, instance, commit=True):
# 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."

View File

@ -1,4 +1,5 @@
import re, doctest, unittest import re, doctest, unittest
import sys
from urlparse import urlparse from urlparse import urlparse
from django.db import transaction from django.db import transaction
from django.core import management, mail from django.core import management, mail
@ -46,15 +47,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
@ -108,7 +109,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 +118,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 +128,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,
u"Template '%s' was not one of the templates used to render the response. Templates used: %s" % u"Template '%s' was not one of the templates used to render the response. Templates used: %s" %
(template_name, u', '.join(template_names))) (template_name, u', '.join(template_names)))
elif response.template: elif response.template:
@ -140,7 +141,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],
u"Template '%s' was used unexpectedly in rendering the response" % template_name) u"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,

View File

@ -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.

View File

@ -83,7 +83,7 @@ If you installed Django using ``setup.py install``, uninstalling
is as simple as deleting the ``django`` directory from your Python is as simple as deleting the ``django`` directory from your Python
``site-packages``. ``site-packages``.
If you installed Django from a Python Egg, remove the Django ``.egg` file, If you installed Django from a Python Egg, remove the Django ``.egg`` file,
and remove the reference to the egg in the file named ``easy-install.pth``. and remove the reference to the egg in the file named ``easy-install.pth``.
This file should also be located in your ``site-packages`` directory. This file should also be located in your ``site-packages`` directory.

View File

@ -870,6 +870,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 +1182,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
=======

View File

@ -717,7 +717,7 @@ object::
# split_contents() knows not to split quoted strings. # split_contents() knows not to split quoted strings.
tag_name, format_string = token.split_contents() tag_name, format_string = token.split_contents()
except ValueError: except ValueError:
raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents[0] raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0]
if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name
return CurrentTimeNode(format_string[1:-1]) return CurrentTimeNode(format_string[1:-1])
@ -846,7 +846,7 @@ Now your tag should begin to look like this::
# split_contents() knows not to split quoted strings. # split_contents() knows not to split quoted strings.
tag_name, date_to_be_formatted, format_string = token.split_contents() tag_name, date_to_be_formatted, format_string = token.split_contents()
except ValueError: except ValueError:
raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents[0] raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0]
if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name
return FormatTimeNode(date_to_be_formatted, format_string[1:-1]) return FormatTimeNode(date_to_be_formatted, format_string[1:-1])
@ -1080,7 +1080,7 @@ class, like so::
# Splitting by None == splitting by spaces. # Splitting by None == splitting by spaces.
tag_name, arg = token.contents.split(None, 1) tag_name, arg = token.contents.split(None, 1)
except ValueError: except ValueError:
raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents[0] raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0]
m = re.search(r'(.*?) as (\w+)', arg) m = re.search(r'(.*?) as (\w+)', arg)
if not m: if not m:
raise template.TemplateSyntaxError, "%r tag had invalid arguments" % tag_name raise template.TemplateSyntaxError, "%r tag had invalid arguments" % tag_name

View File

@ -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.

View File

@ -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&#39;s a test</option> <option value="2">It&#39;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
u'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()

View File

@ -2752,6 +2752,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

View File

@ -96,6 +96,12 @@ MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>"
>>> q['name'] >>> q['name']
u'john' u'john'
>>> del q['name']
>>> 'name' in q
False
>>> q['name'] = 'john'
>>> q.get('foo', 'default') >>> q.get('foo', 'default')
u'default' u'default'
@ -367,6 +373,11 @@ AttributeError: This QueryDict instance is immutable
>>> q.urlencode() >>> q.urlencode()
'vote=yes&vote=no' 'vote=yes&vote=no'
>>> del q['vote']
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
# QueryDicts must be able to handle invalid input encoding (in this case, bad # QueryDicts must be able to handle invalid input encoding (in this case, bad
# UTF-8 encoding). # UTF-8 encoding).
>>> q = QueryDict('foo=bar&foo=\xff') >>> q = QueryDict('foo=bar&foo=\xff')