mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +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:
parent
bc044651aa
commit
28f66bb097
1
AUTHORS
1
AUTHORS
@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
flavio.curella@gmail.com
|
flavio.curella@gmail.com
|
||||||
Jure Cuhalev <gandalf@owca.info>
|
Jure Cuhalev <gandalf@owca.info>
|
||||||
dackze+django@gmail.com
|
dackze+django@gmail.com
|
||||||
|
David Danier <goliath.mailinglist@gmx.de>
|
||||||
Dirk Datzert <dummy@habmalnefrage.de>
|
Dirk Datzert <dummy@habmalnefrage.de>
|
||||||
Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/>
|
Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/>
|
||||||
dave@thebarproject.com
|
dave@thebarproject.com
|
||||||
|
Binary file not shown.
@ -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"
|
||||||
|
Binary file not shown.
@ -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"
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -1,66 +1,69 @@
|
|||||||
"""
|
"""
|
||||||
Utility functions for generating "lorem ipsum" Latin text.
|
Utility functions for generating "lorem ipsum" Latin text.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
COMMON_P = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
COMMON_P = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
||||||
WORDS = ('exercitationem', 'perferendis', 'perspiciatis', 'laborum', 'eveniet', 'sunt', 'iure', 'nam', 'nobis', 'eum', 'cum', 'officiis', 'excepturi', 'odio', 'consectetur', 'quasi', 'aut', 'quisquam', 'vel', 'eligendi', 'itaque', 'non', 'odit', 'tempore', 'quaerat', 'dignissimos', 'facilis', 'neque', 'nihil', 'expedita', 'vitae', 'vero', 'ipsum', 'nisi', 'animi', 'cumque', 'pariatur', 'velit', 'modi', 'natus', 'iusto', 'eaque', 'sequi', 'illo', 'sed', 'ex', 'et', 'voluptatibus', 'tempora', 'veritatis', 'ratione', 'assumenda', 'incidunt', 'nostrum', 'placeat', 'aliquid', 'fuga', 'provident', 'praesentium', 'rem', 'necessitatibus', 'suscipit', 'adipisci', 'quidem', 'possimus', 'voluptas', 'debitis', 'sint', 'accusantium', 'unde', 'sapiente', 'voluptate', 'qui', 'aspernatur', 'laudantium', 'soluta', 'amet', 'quo', 'aliquam', 'saepe', 'culpa', 'libero', 'ipsa', 'dicta', 'reiciendis', 'nesciunt', 'doloribus', 'autem', 'impedit', 'minima', 'maiores', 'repudiandae', 'ipsam', 'obcaecati', 'ullam', 'enim', 'totam', 'delectus', 'ducimus', 'quis', 'voluptates', 'dolores', 'molestiae', 'harum', 'dolorem', 'quia', 'voluptatem', 'molestias', 'magni', 'distinctio', 'omnis', 'illum', 'dolorum', 'voluptatum', 'ea', 'quas', 'quam', 'corporis', 'quae', 'blanditiis', 'atque', 'deserunt', 'laboriosam', 'earum', 'consequuntur', 'hic', 'cupiditate', 'quibusdam', 'accusamus', 'ut', 'rerum', 'error', 'minus', 'eius', 'ab', 'ad', 'nemo', 'fugit', 'officia', 'at', 'in', 'id', 'quos', 'reprehenderit', 'numquam', 'iste', 'fugiat', 'sit', 'inventore', 'beatae', 'repellendus', 'magnam', 'recusandae', 'quod', 'explicabo', 'doloremque', 'aperiam', 'consequatur', 'asperiores', 'commodi', 'optio', 'dolor', 'labore', 'temporibus', 'repellat', 'veniam', 'architecto', 'est', 'esse', 'mollitia', 'nulla', 'a', 'similique', 'eos', 'alias', 'dolore', 'tenetur', 'deleniti', 'porro', 'facere', 'maxime', 'corrupti')
|
WORDS = ('exercitationem', 'perferendis', 'perspiciatis', 'laborum', 'eveniet', 'sunt', 'iure', 'nam', 'nobis', 'eum', 'cum', 'officiis', 'excepturi', 'odio', 'consectetur', 'quasi', 'aut', 'quisquam', 'vel', 'eligendi', 'itaque', 'non', 'odit', 'tempore', 'quaerat', 'dignissimos', 'facilis', 'neque', 'nihil', 'expedita', 'vitae', 'vero', 'ipsum', 'nisi', 'animi', 'cumque', 'pariatur', 'velit', 'modi', 'natus', 'iusto', 'eaque', 'sequi', 'illo', 'sed', 'ex', 'et', 'voluptatibus', 'tempora', 'veritatis', 'ratione', 'assumenda', 'incidunt', 'nostrum', 'placeat', 'aliquid', 'fuga', 'provident', 'praesentium', 'rem', 'necessitatibus', 'suscipit', 'adipisci', 'quidem', 'possimus', 'voluptas', 'debitis', 'sint', 'accusantium', 'unde', 'sapiente', 'voluptate', 'qui', 'aspernatur', 'laudantium', 'soluta', 'amet', 'quo', 'aliquam', 'saepe', 'culpa', 'libero', 'ipsa', 'dicta', 'reiciendis', 'nesciunt', 'doloribus', 'autem', 'impedit', 'minima', 'maiores', 'repudiandae', 'ipsam', 'obcaecati', 'ullam', 'enim', 'totam', 'delectus', 'ducimus', 'quis', 'voluptates', 'dolores', 'molestiae', 'harum', 'dolorem', 'quia', 'voluptatem', 'molestias', 'magni', 'distinctio', 'omnis', 'illum', 'dolorum', 'voluptatum', 'ea', 'quas', 'quam', 'corporis', 'quae', 'blanditiis', 'atque', 'deserunt', 'laboriosam', 'earum', 'consequuntur', 'hic', 'cupiditate', 'quibusdam', 'accusamus', 'ut', 'rerum', 'error', 'minus', 'eius', 'ab', 'ad', 'nemo', 'fugit', 'officia', 'at', 'in', 'id', 'quos', 'reprehenderit', 'numquam', 'iste', 'fugiat', 'sit', 'inventore', 'beatae', 'repellendus', 'magnam', 'recusandae', 'quod', 'explicabo', 'doloremque', 'aperiam', 'consequatur', 'asperiores', 'commodi', 'optio', 'dolor', 'labore', 'temporibus', 'repellat', 'veniam', 'architecto', 'est', 'esse', 'mollitia', 'nulla', 'a', 'similique', 'eos', 'alias', 'dolore', 'tenetur', 'deleniti', 'porro', 'facere', 'maxime', 'corrupti')
|
||||||
COMMON_WORDS = ('lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipisicing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua')
|
COMMON_WORDS = ('lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipisicing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua')
|
||||||
|
|
||||||
def sentence():
|
def sentence():
|
||||||
"""
|
"""
|
||||||
Returns a randomly generated sentence of lorem ipsum text.
|
Returns a randomly generated sentence of lorem ipsum text.
|
||||||
|
|
||||||
The first word is capitalized, and the sentence ends in either a period or
|
The first word is capitalized, and the sentence ends in either a period or
|
||||||
question mark. Commas are added at random.
|
question mark. Commas are added at random.
|
||||||
"""
|
"""
|
||||||
# Determine the number of comma-separated sections and number of words in
|
# Determine the number of comma-separated sections and number of words in
|
||||||
# each section for this sentence.
|
# each section for this sentence.
|
||||||
sections = [' '.join(random.sample(WORDS, random.randint(3, 12))) for i in range(random.randint(1, 5))]
|
sections = [' '.join(random.sample(WORDS, random.randint(3, 12))) for i in range(random.randint(1, 5))]
|
||||||
s = ', '.join(sections)
|
s = ', '.join(sections)
|
||||||
# Convert to sentence case and add end punctuation.
|
# Convert to sentence case and add end punctuation.
|
||||||
return '%s%s%s' % (s[0].upper(), s[1:], random.choice('?.'))
|
return '%s%s%s' % (s[0].upper(), s[1:], random.choice('?.'))
|
||||||
|
|
||||||
def paragraph():
|
def paragraph():
|
||||||
"""
|
"""
|
||||||
Returns a randomly generated paragraph of lorem ipsum text.
|
Returns a randomly generated paragraph of lorem ipsum text.
|
||||||
|
|
||||||
The paragraph consists of between 1 and 4 sentences, inclusive.
|
The paragraph consists of between 1 and 4 sentences, inclusive.
|
||||||
"""
|
"""
|
||||||
return ' '.join([sentence() for i in range(random.randint(1, 4))])
|
return ' '.join([sentence() for i in range(random.randint(1, 4))])
|
||||||
|
|
||||||
def paragraphs(count, common=True):
|
def paragraphs(count, common=True):
|
||||||
"""
|
"""
|
||||||
Returns a list of paragraphs as returned by paragraph().
|
Returns a list of paragraphs as returned by paragraph().
|
||||||
|
|
||||||
If `common` is True, then the first paragraph will be the standard
|
If `common` is True, then the first paragraph will be the standard
|
||||||
'lorem ipsum' paragraph. Otherwise, the first paragraph will be random
|
'lorem ipsum' paragraph. Otherwise, the first paragraph will be random
|
||||||
Latin text. Either way, subsequent paragraphs will be random Latin text.
|
Latin text. Either way, subsequent paragraphs will be random Latin text.
|
||||||
"""
|
"""
|
||||||
paras = []
|
paras = []
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
if common and i == 0:
|
if common and i == 0:
|
||||||
paras.append(COMMON_P)
|
paras.append(COMMON_P)
|
||||||
else:
|
else:
|
||||||
paras.append(paragraph())
|
paras.append(paragraph())
|
||||||
return paras
|
return paras
|
||||||
|
|
||||||
def words(count, common=True):
|
def words(count, common=True):
|
||||||
"""
|
"""
|
||||||
Returns a string of `count` lorem ipsum words separated by a single space.
|
Returns a string of `count` lorem ipsum words separated by a single space.
|
||||||
|
|
||||||
If `common` is True, then the first 19 words will be the standard
|
If `common` is True, then the first 19 words will be the standard
|
||||||
'lorem ipsum' words. Otherwise, all words will be selected randomly.
|
'lorem ipsum' words. Otherwise, all words will be selected randomly.
|
||||||
"""
|
"""
|
||||||
if common:
|
if common:
|
||||||
word_list = list(COMMON_WORDS)
|
word_list = list(COMMON_WORDS)
|
||||||
else:
|
else:
|
||||||
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:
|
||||||
else:
|
c = min(count, len(WORDS))
|
||||||
word_list = word_list[:count]
|
count -= c
|
||||||
return ' '.join(word_list)
|
word_list += random.sample(WORDS, c)
|
||||||
|
else:
|
||||||
|
word_list = word_list[:count]
|
||||||
|
return ' '.join(word_list)
|
||||||
|
@ -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'),
|
||||||
|
@ -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'),
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
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
|
return save
|
||||||
|
|
||||||
def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()):
|
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."
|
||||||
|
@ -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
|
||||||
@ -45,16 +46,16 @@ class TestCase(unittest.TestCase):
|
|||||||
if hasattr(self, 'fixtures'):
|
if hasattr(self, 'fixtures'):
|
||||||
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,9 +141,9 @@ 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,
|
||||||
u"Template '%s' was used unexpectedly in rendering the response" % template_name)
|
u"Template '%s' was used unexpectedly in rendering the response" % template_name)
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -56,7 +56,7 @@ installed.
|
|||||||
either ``postgresql`` [for version 1] or ``postgresql_psycopg2`` [for version 2].)
|
either ``postgresql`` [for version 1] or ``postgresql_psycopg2`` [for version 2].)
|
||||||
|
|
||||||
If you're on Windows, check out the unofficial `compiled Windows version`_.
|
If you're on Windows, check out the unofficial `compiled Windows version`_.
|
||||||
|
|
||||||
* If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher.
|
* If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher.
|
||||||
You will also want to read the database-specific notes for the `MySQL backend`_.
|
You will also want to read the database-specific notes for the `MySQL backend`_.
|
||||||
|
|
||||||
@ -75,16 +75,16 @@ installed.
|
|||||||
Remove any old versions of Django
|
Remove any old versions of Django
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
If you are upgrading your installation of Django from a previous version,
|
If you are upgrading your installation of Django from a previous version,
|
||||||
you will need to uninstall the old Django version before installing the
|
you will need to uninstall the old Django version before installing the
|
||||||
new version.
|
new version.
|
||||||
|
|
||||||
If you installed Django using ``setup.py install``, uninstalling
|
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.
|
||||||
|
|
||||||
.. admonition:: Where are my ``site-packages`` stored?
|
.. admonition:: Where are my ``site-packages`` stored?
|
||||||
@ -92,7 +92,7 @@ This file should also be located in your ``site-packages`` directory.
|
|||||||
The location of the ``site-packages`` directory depends on the operating
|
The location of the ``site-packages`` directory depends on the operating
|
||||||
system, and the location in which Python was installed. However, the
|
system, and the location in which Python was installed. However, the
|
||||||
following locations are common:
|
following locations are common:
|
||||||
|
|
||||||
* If you're using Linux: ``/usr/lib/python2.X/site-packages``
|
* If you're using Linux: ``/usr/lib/python2.X/site-packages``
|
||||||
|
|
||||||
* If you're using Windows: ``C:\Python2.X\lib\site-packages``
|
* If you're using Windows: ``C:\Python2.X\lib\site-packages``
|
||||||
|
@ -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
|
|
||||||
=======
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -179,6 +179,18 @@ fields with the 'choices' attribute are represented by a ChoiceField.
|
|||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
|
</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
|
||||||
|
|
||||||
|
You can restrict a form to a subset of the complete list of fields
|
||||||
|
by providing a 'fields' argument. If you try to save a
|
||||||
|
model created with such a form, you need to ensure that the fields
|
||||||
|
that are _not_ on the form have default values, or are allowed to have
|
||||||
|
a value of None. If a field isn't specified on a form, the object created
|
||||||
|
from the form can't provide a value for that field!
|
||||||
|
>>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date'))
|
||||||
|
>>> f = PartialArticleForm(auto_id=False)
|
||||||
|
>>> print f
|
||||||
|
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
||||||
|
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
|
||||||
|
|
||||||
You can pass a custom Form class to form_for_model. Make sure it's a
|
You can pass a custom Form class to form_for_model. Make sure it's a
|
||||||
subclass of BaseForm, not Form.
|
subclass of BaseForm, not Form.
|
||||||
>>> class CustomForm(BaseForm):
|
>>> class CustomForm(BaseForm):
|
||||||
@ -224,7 +236,23 @@ current values are inserted as 'initial' data in each Field.
|
|||||||
<option value="2">It's a test</option>
|
<option value="2">It's a test</option>
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
>>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'})
|
>>> f = TestArticleForm({'headline': u'Test headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'})
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> test_art = f.save()
|
||||||
|
>>> test_art.id
|
||||||
|
1
|
||||||
|
>>> test_art = Article.objects.get(id=1)
|
||||||
|
>>> test_art.headline
|
||||||
|
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()
|
||||||
|
@ -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
|
||||||
|
@ -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')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user