mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
newforms-admin: Merged to [4563]
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@4565 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
67e0248806
commit
8f1fb6da3a
5
AUTHORS
5
AUTHORS
@ -125,7 +125,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Martin Maney <http://www.chipy.org/Martin_Maney>
|
Martin Maney <http://www.chipy.org/Martin_Maney>
|
||||||
masonsimon+django@gmail.com
|
masonsimon+django@gmail.com
|
||||||
Manuzhai
|
Manuzhai
|
||||||
Petar Marić
|
Petar Marić <http://www.petarmaric.com/>
|
||||||
mark@junklight.com
|
mark@junklight.com
|
||||||
Yasushi Masuda <whosaysni@gmail.com>
|
Yasushi Masuda <whosaysni@gmail.com>
|
||||||
mattycakes@gmail.com
|
mattycakes@gmail.com
|
||||||
@ -166,7 +166,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
smurf@smurf.noris.de
|
smurf@smurf.noris.de
|
||||||
sopel
|
sopel
|
||||||
Georgi Stanojevski <glisha@gmail.com>
|
Georgi Stanojevski <glisha@gmail.com>
|
||||||
Thomas Steinacher <tom@eggdrop.ch>
|
Thomas Steinacher <http://www.eggdrop.ch/>
|
||||||
nowell strite
|
nowell strite
|
||||||
Radek Švarz <http://www.svarz.cz/translate/>
|
Radek Švarz <http://www.svarz.cz/translate/>
|
||||||
Swaroop C H <http://www.swaroopch.info>
|
Swaroop C H <http://www.swaroopch.info>
|
||||||
@ -183,6 +183,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Milton Waddams
|
Milton Waddams
|
||||||
wam-djangobug@wamber.net
|
wam-djangobug@wamber.net
|
||||||
Dan Watson <http://theidioteque.net/>
|
Dan Watson <http://theidioteque.net/>
|
||||||
|
Chris Wesseling <Chris.Wesseling@cwi.nl>
|
||||||
Rachel Willmer <http://www.willmer.com/kb/>
|
Rachel Willmer <http://www.willmer.com/kb/>
|
||||||
Gary Wilson <gary.wilson@gmail.com>
|
Gary Wilson <gary.wilson@gmail.com>
|
||||||
wojtek
|
wojtek
|
||||||
|
@ -4,7 +4,11 @@ USA-specific Form helpers
|
|||||||
|
|
||||||
from django.newforms import ValidationError
|
from django.newforms import ValidationError
|
||||||
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
|
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
|
||||||
|
from django.newforms.util import smart_unicode
|
||||||
from django.utils.translation import gettext
|
from django.utils.translation import gettext
|
||||||
|
import re
|
||||||
|
|
||||||
|
phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
|
||||||
|
|
||||||
class USZipCodeField(RegexField):
|
class USZipCodeField(RegexField):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -13,6 +17,17 @@ class USZipCodeField(RegexField):
|
|||||||
error_message=gettext(u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'),
|
error_message=gettext(u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'),
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
class USPhoneNumberField(Field):
|
||||||
|
def clean(self, value):
|
||||||
|
super(USPhoneNumberField, self).clean(value)
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
|
||||||
|
m = phone_digits_re.search(value)
|
||||||
|
if m:
|
||||||
|
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
|
||||||
|
raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.')
|
||||||
|
|
||||||
class USStateField(Field):
|
class USStateField(Field):
|
||||||
"""
|
"""
|
||||||
A form field that validates its input is a U.S. state name or abbreviation.
|
A form field that validates its input is a U.S. state name or abbreviation.
|
||||||
|
@ -750,6 +750,12 @@ class PhoneNumberField(IntegerField):
|
|||||||
def validate(self, field_data, all_data):
|
def validate(self, field_data, all_data):
|
||||||
validators.isValidPhone(field_data, all_data)
|
validators.isValidPhone(field_data, all_data)
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
from django.contrib.localflavor.usa.forms import USPhoneNumberField
|
||||||
|
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return USPhoneNumberField(**defaults)
|
||||||
|
|
||||||
class PositiveIntegerField(IntegerField):
|
class PositiveIntegerField(IntegerField):
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [oldforms.PositiveIntegerField]
|
return [oldforms.PositiveIntegerField]
|
||||||
|
@ -545,9 +545,9 @@ class ForeignKey(RelatedField, Field):
|
|||||||
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
defaults = {'queryset': self.rel.to._default_manager.all(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return forms.ChoiceField(**defaults)
|
return forms.ModelChoiceField(**defaults)
|
||||||
|
|
||||||
class OneToOneField(RelatedField, IntegerField):
|
class OneToOneField(RelatedField, IntegerField):
|
||||||
def __init__(self, to, to_field=None, **kwargs):
|
def __init__(self, to, to_field=None, **kwargs):
|
||||||
@ -606,9 +606,9 @@ class OneToOneField(RelatedField, IntegerField):
|
|||||||
cls._meta.one_to_one_field = self
|
cls._meta.one_to_one_field = self
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
defaults = {'queryset': self.rel.to._default_manager.all(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return forms.ChoiceField(**kwargs)
|
return forms.ModelChoiceField(**kwargs)
|
||||||
|
|
||||||
class ManyToManyField(RelatedField, Field):
|
class ManyToManyField(RelatedField, Field):
|
||||||
def __init__(self, to, **kwargs):
|
def __init__(self, to, **kwargs):
|
||||||
@ -716,9 +716,9 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
# MultipleChoiceField takes a list of IDs.
|
# MultipleChoiceField takes a list of IDs.
|
||||||
if kwargs.get('initial') is not None:
|
if kwargs.get('initial') is not None:
|
||||||
kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
|
kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
|
||||||
defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
defaults = {'queryset' : self.rel.to._default_manager.all(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return forms.MultipleChoiceField(**defaults)
|
return forms.ModelMultipleChoiceField(**defaults)
|
||||||
|
|
||||||
class ManyToOneRel(object):
|
class ManyToOneRel(object):
|
||||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||||
|
@ -194,7 +194,17 @@ class QuerySet(object):
|
|||||||
yield obj
|
yield obj
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
"Performs a SELECT COUNT() and returns the number of records as an integer."
|
"""
|
||||||
|
Performs a SELECT COUNT() and returns the number of records as an
|
||||||
|
integer.
|
||||||
|
|
||||||
|
If the queryset is already cached (i.e. self._result_cache is set) this
|
||||||
|
simply returns the length of the cached results set to avoid multiple
|
||||||
|
SELECT COUNT(*) calls.
|
||||||
|
"""
|
||||||
|
if self._result_cache is not None:
|
||||||
|
return len(self._result_cache)
|
||||||
|
|
||||||
counter = self._clone()
|
counter = self._clone()
|
||||||
counter._order_by = ()
|
counter._order_by = ()
|
||||||
counter._select_related = False
|
counter._select_related = False
|
||||||
|
@ -339,8 +339,9 @@ class ChoiceField(Field):
|
|||||||
|
|
||||||
def _set_choices(self, value):
|
def _set_choices(self, value):
|
||||||
# Setting choices also sets the choices on the widget.
|
# Setting choices also sets the choices on the widget.
|
||||||
self._choices = value
|
# choices can be any iterable, but we call list() on it because
|
||||||
self.widget.choices = value
|
# it will be consumed more than once.
|
||||||
|
self._choices = self.widget.choices = list(value)
|
||||||
|
|
||||||
choices = property(_get_choices, _set_choices)
|
choices = property(_get_choices, _set_choices)
|
||||||
|
|
||||||
|
@ -3,9 +3,14 @@ Helper functions for creating Form classes from Django models
|
|||||||
and database field objects.
|
and database field objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.utils.translation import gettext
|
||||||
|
from util import ValidationError
|
||||||
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
||||||
|
from fields import Field, ChoiceField
|
||||||
|
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')
|
||||||
|
|
||||||
def model_save(self, commit=True):
|
def model_save(self, commit=True):
|
||||||
"""
|
"""
|
||||||
@ -31,9 +36,9 @@ def save_instance(form, instance, commit=True):
|
|||||||
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
|
||||||
clean_data = form.clean_data
|
clean_data = form.clean_data
|
||||||
for f in opts.fields:
|
for f in opts.fields:
|
||||||
if isinstance(f, models.AutoField):
|
if not f.editable or isinstance(f, models.AutoField):
|
||||||
continue
|
continue
|
||||||
setattr(instance, f.attname, 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:
|
||||||
@ -63,6 +68,8 @@ def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfiel
|
|||||||
opts = model._meta
|
opts = model._meta
|
||||||
field_list = []
|
field_list = []
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
if not f.editable:
|
||||||
|
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))
|
||||||
@ -84,6 +91,8 @@ def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kw
|
|||||||
opts = model._meta
|
opts = model._meta
|
||||||
field_list = []
|
field_list = []
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
if not f.editable:
|
||||||
|
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:
|
||||||
@ -94,5 +103,88 @@ def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kw
|
|||||||
|
|
||||||
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."
|
||||||
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
|
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list if f.editable])
|
||||||
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||||
|
|
||||||
|
class QuerySetIterator(object):
|
||||||
|
def __init__(self, queryset, empty_label, cache_choices):
|
||||||
|
self.queryset, self.empty_label, self.cache_choices = queryset, empty_label, cache_choices
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
if self.empty_label is not None:
|
||||||
|
yield (u"", self.empty_label)
|
||||||
|
for obj in self.queryset:
|
||||||
|
yield (obj._get_pk_val(), str(obj))
|
||||||
|
# Clear the QuerySet cache if required.
|
||||||
|
if not self.cache_choices:
|
||||||
|
self.queryset._result_cache = None
|
||||||
|
|
||||||
|
class ModelChoiceField(ChoiceField):
|
||||||
|
"A ChoiceField whose choices are a model QuerySet."
|
||||||
|
# This class is a subclass of ChoiceField for purity, but it doesn't
|
||||||
|
# actually use any of ChoiceField's implementation.
|
||||||
|
def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
|
||||||
|
required=True, widget=Select, label=None, initial=None, help_text=None):
|
||||||
|
self.queryset = queryset
|
||||||
|
self.empty_label = empty_label
|
||||||
|
self.cache_choices = cache_choices
|
||||||
|
# Call Field instead of ChoiceField __init__() because we don't need
|
||||||
|
# ChoiceField.__init__().
|
||||||
|
Field.__init__(self, required, widget, label, initial, help_text)
|
||||||
|
self.widget.choices = self.choices
|
||||||
|
|
||||||
|
def _get_choices(self):
|
||||||
|
# If self._choices is set, then somebody must have manually set
|
||||||
|
# the property self.choices. In this case, just return self._choices.
|
||||||
|
if hasattr(self, '_choices'):
|
||||||
|
return self._choices
|
||||||
|
# Otherwise, execute the QuerySet in self.queryset to determine the
|
||||||
|
# choices dynamically. Return a fresh QuerySetIterator that has not
|
||||||
|
# been consumed. Note that we're instantiating a new QuerySetIterator
|
||||||
|
# *each* time _get_choices() is called (and, thus, each time
|
||||||
|
# self.choices is accessed) so that we can ensure the QuerySet has not
|
||||||
|
# been consumed.
|
||||||
|
return QuerySetIterator(self.queryset, self.empty_label, self.cache_choices)
|
||||||
|
|
||||||
|
def _set_choices(self, value):
|
||||||
|
# This method is copied from ChoiceField._set_choices(). It's necessary
|
||||||
|
# because property() doesn't allow a subclass to overwrite only
|
||||||
|
# _get_choices without implementing _set_choices.
|
||||||
|
self._choices = self.widget.choices = list(value)
|
||||||
|
|
||||||
|
choices = property(_get_choices, _set_choices)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
Field.clean(self, value)
|
||||||
|
if value in ('', None):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
value = self.queryset.model._default_manager.get(pk=value)
|
||||||
|
except self.queryset.model.DoesNotExist:
|
||||||
|
raise ValidationError(gettext(u'Select a valid choice. That choice is not one of the available choices.'))
|
||||||
|
return value
|
||||||
|
|
||||||
|
class ModelMultipleChoiceField(ModelChoiceField):
|
||||||
|
"A MultipleChoiceField whose choices are a model QuerySet."
|
||||||
|
hidden_widget = MultipleHiddenInput
|
||||||
|
def __init__(self, queryset, cache_choices=False, required=True,
|
||||||
|
widget=SelectMultiple, label=None, initial=None, help_text=None):
|
||||||
|
super(ModelMultipleChoiceField, self).__init__(queryset, None, cache_choices,
|
||||||
|
required, widget, label, initial, help_text)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
if self.required and not value:
|
||||||
|
raise ValidationError(gettext(u'This field is required.'))
|
||||||
|
elif not self.required and not value:
|
||||||
|
return []
|
||||||
|
if not isinstance(value, (list, tuple)):
|
||||||
|
raise ValidationError(gettext(u'Enter a list of values.'))
|
||||||
|
final_values = []
|
||||||
|
for val in value:
|
||||||
|
try:
|
||||||
|
obj = self.queryset.model._default_manager.get(pk=val)
|
||||||
|
except self.queryset.model.DoesNotExist:
|
||||||
|
raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % val)
|
||||||
|
else:
|
||||||
|
final_values.append(obj)
|
||||||
|
return final_values
|
||||||
|
@ -580,6 +580,8 @@ class FilterExpression(object):
|
|||||||
def args_check(name, func, provided):
|
def args_check(name, func, provided):
|
||||||
provided = list(provided)
|
provided = list(provided)
|
||||||
plen = len(provided)
|
plen = len(provided)
|
||||||
|
# Check to see if a decorator is providing the real function.
|
||||||
|
func = getattr(func, '_decorated_function', func)
|
||||||
args, varargs, varkw, defaults = getargspec(func)
|
args, varargs, varkw, defaults = getargspec(func)
|
||||||
# First argument is filter input.
|
# First argument is filter input.
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
@ -812,7 +814,7 @@ class Library(object):
|
|||||||
raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)
|
raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)
|
||||||
|
|
||||||
def tag_function(self,func):
|
def tag_function(self,func):
|
||||||
self.tags[func.__name__] = func
|
self.tags[getattr(func, "_decorated_function", func).__name__] = func
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def filter(self, name=None, filter_func=None):
|
def filter(self, name=None, filter_func=None):
|
||||||
@ -836,7 +838,7 @@ class Library(object):
|
|||||||
raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func)
|
raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func)
|
||||||
|
|
||||||
def filter_function(self, func):
|
def filter_function(self, func):
|
||||||
self.filters[func.__name__] = func
|
self.filters[getattr(func, "_decorated_function", func).__name__] = func
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def simple_tag(self,func):
|
def simple_tag(self,func):
|
||||||
@ -850,9 +852,9 @@ class Library(object):
|
|||||||
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
||||||
return func(*resolved_vars)
|
return func(*resolved_vars)
|
||||||
|
|
||||||
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
|
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
|
||||||
compile_func.__doc__ = func.__doc__
|
compile_func.__doc__ = func.__doc__
|
||||||
self.tag(func.__name__, compile_func)
|
self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
|
def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
|
||||||
@ -886,9 +888,9 @@ class Library(object):
|
|||||||
self.nodelist = t.nodelist
|
self.nodelist = t.nodelist
|
||||||
return self.nodelist.render(context_class(dict))
|
return self.nodelist.render(context_class(dict))
|
||||||
|
|
||||||
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
|
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
|
||||||
compile_func.__doc__ = func.__doc__
|
compile_func.__doc__ = func.__doc__
|
||||||
self.tag(func.__name__, compile_func)
|
self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
|
||||||
return func
|
return func
|
||||||
return dec
|
return dec
|
||||||
|
|
||||||
|
@ -8,6 +8,38 @@ import random as random_module
|
|||||||
|
|
||||||
register = Library()
|
register = Library()
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# STRING DECORATOR #
|
||||||
|
#######################
|
||||||
|
|
||||||
|
def smart_string(obj):
|
||||||
|
# FUTURE: Unicode strings should probably be normalized to a specific
|
||||||
|
# encoding and non-unicode strings should be converted to unicode too.
|
||||||
|
# if isinstance(obj, unicode):
|
||||||
|
# obj = obj.encode(settings.DEFAULT_CHARSET)
|
||||||
|
# else:
|
||||||
|
# obj = unicode(obj, settings.DEFAULT_CHARSET)
|
||||||
|
# FUTURE: Replace dumb string logic below with cool unicode logic above.
|
||||||
|
if not isinstance(obj, basestring):
|
||||||
|
obj = str(obj)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def stringfilter(func):
|
||||||
|
"""
|
||||||
|
Decorator for filters which should only receive strings. The object passed
|
||||||
|
as the first positional argument will be converted to a string.
|
||||||
|
"""
|
||||||
|
def _dec(*args, **kwargs):
|
||||||
|
if args:
|
||||||
|
args = list(args)
|
||||||
|
args[0] = smart_string(args[0])
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
# Include a reference to the real function (used to check original
|
||||||
|
# arguments by the template parser).
|
||||||
|
_dec._decorated_function = getattr(func, '_decorated_function', func)
|
||||||
|
return _dec
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# STRINGS #
|
# STRINGS #
|
||||||
###################
|
###################
|
||||||
@ -16,16 +48,18 @@ register = Library()
|
|||||||
def addslashes(value):
|
def addslashes(value):
|
||||||
"Adds slashes - useful for passing strings to JavaScript, for example."
|
"Adds slashes - useful for passing strings to JavaScript, for example."
|
||||||
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
||||||
|
addslashes = stringfilter(addslashes)
|
||||||
|
|
||||||
def capfirst(value):
|
def capfirst(value):
|
||||||
"Capitalizes the first character of the value"
|
"Capitalizes the first character of the value"
|
||||||
value = str(value)
|
|
||||||
return value and value[0].upper() + value[1:]
|
return value and value[0].upper() + value[1:]
|
||||||
|
capfirst = stringfilter(capfirst)
|
||||||
|
|
||||||
def fix_ampersands(value):
|
def fix_ampersands(value):
|
||||||
"Replaces ampersands with ``&`` entities"
|
"Replaces ampersands with ``&`` entities"
|
||||||
from django.utils.html import fix_ampersands
|
from django.utils.html import fix_ampersands
|
||||||
return fix_ampersands(value)
|
return fix_ampersands(value)
|
||||||
|
fix_ampersands = stringfilter(fix_ampersands)
|
||||||
|
|
||||||
def floatformat(text, arg=-1):
|
def floatformat(text, arg=-1):
|
||||||
"""
|
"""
|
||||||
@ -52,7 +86,7 @@ def floatformat(text, arg=-1):
|
|||||||
try:
|
try:
|
||||||
d = int(arg)
|
d = int(arg)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return str(f)
|
return smart_string(f)
|
||||||
m = f - int(f)
|
m = f - int(f)
|
||||||
if not m and d < 0:
|
if not m and d < 0:
|
||||||
return '%d' % int(f)
|
return '%d' % int(f)
|
||||||
@ -69,22 +103,26 @@ def linenumbers(value):
|
|||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
|
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
|
||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
|
linenumbers = stringfilter(linenumbers)
|
||||||
|
|
||||||
def lower(value):
|
def lower(value):
|
||||||
"Converts a string into all lowercase"
|
"Converts a string into all lowercase"
|
||||||
return value.lower()
|
return value.lower()
|
||||||
|
lower = stringfilter(lower)
|
||||||
|
|
||||||
def make_list(value):
|
def make_list(value):
|
||||||
"""
|
"""
|
||||||
Returns the value turned into a list. For an integer, it's a list of
|
Returns the value turned into a list. For an integer, it's a list of
|
||||||
digits. For a string, it's a list of characters.
|
digits. For a string, it's a list of characters.
|
||||||
"""
|
"""
|
||||||
return list(str(value))
|
return list(value)
|
||||||
|
make_list = stringfilter(make_list)
|
||||||
|
|
||||||
def slugify(value):
|
def slugify(value):
|
||||||
"Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
|
"Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
|
||||||
value = re.sub('[^\w\s-]', '', value).strip().lower()
|
value = re.sub('[^\w\s-]', '', value).strip().lower()
|
||||||
return re.sub('[-\s]+', '-', value)
|
return re.sub('[-\s]+', '-', value)
|
||||||
|
slugify = stringfilter(slugify)
|
||||||
|
|
||||||
def stringformat(value, arg):
|
def stringformat(value, arg):
|
||||||
"""
|
"""
|
||||||
@ -96,13 +134,14 @@ def stringformat(value, arg):
|
|||||||
of Python string formatting
|
of Python string formatting
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return ("%" + arg) % value
|
return ("%" + str(arg)) % value
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def title(value):
|
def title(value):
|
||||||
"Converts a string into titlecase"
|
"Converts a string into titlecase"
|
||||||
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
|
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
|
||||||
|
title = stringfilter(title)
|
||||||
|
|
||||||
def truncatewords(value, arg):
|
def truncatewords(value, arg):
|
||||||
"""
|
"""
|
||||||
@ -118,6 +157,7 @@ def truncatewords(value, arg):
|
|||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
return truncate_words(value, length)
|
return truncate_words(value, length)
|
||||||
|
truncatewords = stringfilter(truncatewords)
|
||||||
|
|
||||||
def truncatewords_html(value, arg):
|
def truncatewords_html(value, arg):
|
||||||
"""
|
"""
|
||||||
@ -133,10 +173,12 @@ def truncatewords_html(value, arg):
|
|||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
return truncate_html_words(value, length)
|
return truncate_html_words(value, length)
|
||||||
|
truncatewords_html = stringfilter(truncatewords_html)
|
||||||
|
|
||||||
def upper(value):
|
def upper(value):
|
||||||
"Converts a string into all uppercase"
|
"Converts a string into all uppercase"
|
||||||
return value.upper()
|
return value.upper()
|
||||||
|
upper = stringfilter(upper)
|
||||||
|
|
||||||
def urlencode(value):
|
def urlencode(value):
|
||||||
"Escapes a value for use in a URL"
|
"Escapes a value for use in a URL"
|
||||||
@ -144,11 +186,13 @@ def urlencode(value):
|
|||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
return urllib.quote(value)
|
return urllib.quote(value)
|
||||||
|
urlencode = stringfilter(urlencode)
|
||||||
|
|
||||||
def urlize(value):
|
def urlize(value):
|
||||||
"Converts URLs in plain text into clickable links"
|
"Converts URLs in plain text into clickable links"
|
||||||
from django.utils.html import urlize
|
from django.utils.html import urlize
|
||||||
return urlize(value, nofollow=True)
|
return urlize(value, nofollow=True)
|
||||||
|
urlize = stringfilter(urlize)
|
||||||
|
|
||||||
def urlizetrunc(value, limit):
|
def urlizetrunc(value, limit):
|
||||||
"""
|
"""
|
||||||
@ -159,10 +203,12 @@ def urlizetrunc(value, limit):
|
|||||||
"""
|
"""
|
||||||
from django.utils.html import urlize
|
from django.utils.html import urlize
|
||||||
return urlize(value, trim_url_limit=int(limit), nofollow=True)
|
return urlize(value, trim_url_limit=int(limit), nofollow=True)
|
||||||
|
urlizetrunc = stringfilter(urlizetrunc)
|
||||||
|
|
||||||
def wordcount(value):
|
def wordcount(value):
|
||||||
"Returns the number of words"
|
"Returns the number of words"
|
||||||
return len(value.split())
|
return len(value.split())
|
||||||
|
wordcount = stringfilter(wordcount)
|
||||||
|
|
||||||
def wordwrap(value, arg):
|
def wordwrap(value, arg):
|
||||||
"""
|
"""
|
||||||
@ -171,7 +217,8 @@ def wordwrap(value, arg):
|
|||||||
Argument: number of characters to wrap the text at.
|
Argument: number of characters to wrap the text at.
|
||||||
"""
|
"""
|
||||||
from django.utils.text import wrap
|
from django.utils.text import wrap
|
||||||
return wrap(str(value), int(arg))
|
return wrap(value, int(arg))
|
||||||
|
wordwrap = stringfilter(wordwrap)
|
||||||
|
|
||||||
def ljust(value, arg):
|
def ljust(value, arg):
|
||||||
"""
|
"""
|
||||||
@ -179,7 +226,8 @@ def ljust(value, arg):
|
|||||||
|
|
||||||
Argument: field size
|
Argument: field size
|
||||||
"""
|
"""
|
||||||
return str(value).ljust(int(arg))
|
return value.ljust(int(arg))
|
||||||
|
ljust = stringfilter(ljust)
|
||||||
|
|
||||||
def rjust(value, arg):
|
def rjust(value, arg):
|
||||||
"""
|
"""
|
||||||
@ -187,15 +235,18 @@ def rjust(value, arg):
|
|||||||
|
|
||||||
Argument: field size
|
Argument: field size
|
||||||
"""
|
"""
|
||||||
return str(value).rjust(int(arg))
|
return value.rjust(int(arg))
|
||||||
|
rjust = stringfilter(rjust)
|
||||||
|
|
||||||
def center(value, arg):
|
def center(value, arg):
|
||||||
"Centers the value in a field of a given width"
|
"Centers the value in a field of a given width"
|
||||||
return str(value).center(int(arg))
|
return value.center(int(arg))
|
||||||
|
center = stringfilter(center)
|
||||||
|
|
||||||
def cut(value, arg):
|
def cut(value, arg):
|
||||||
"Removes all values of arg from the given string"
|
"Removes all values of arg from the given string"
|
||||||
return value.replace(arg, '')
|
return value.replace(arg, '')
|
||||||
|
cut = stringfilter(cut)
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# HTML STRINGS #
|
# HTML STRINGS #
|
||||||
@ -205,15 +256,18 @@ def escape(value):
|
|||||||
"Escapes a string's HTML"
|
"Escapes a string's HTML"
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
return escape(value)
|
return escape(value)
|
||||||
|
escape = stringfilter(escape)
|
||||||
|
|
||||||
def linebreaks(value):
|
def linebreaks(value):
|
||||||
"Converts newlines into <p> and <br />s"
|
"Converts newlines into <p> and <br />s"
|
||||||
from django.utils.html import linebreaks
|
from django.utils.html import linebreaks
|
||||||
return linebreaks(value)
|
return linebreaks(value)
|
||||||
|
linebreaks = stringfilter(linebreaks)
|
||||||
|
|
||||||
def linebreaksbr(value):
|
def linebreaksbr(value):
|
||||||
"Converts newlines into <br />s"
|
"Converts newlines into <br />s"
|
||||||
return value.replace('\n', '<br />')
|
return value.replace('\n', '<br />')
|
||||||
|
linebreaksbr = stringfilter(linebreaksbr)
|
||||||
|
|
||||||
def removetags(value, tags):
|
def removetags(value, tags):
|
||||||
"Removes a space separated list of [X]HTML tags from the output"
|
"Removes a space separated list of [X]HTML tags from the output"
|
||||||
@ -224,13 +278,13 @@ def removetags(value, tags):
|
|||||||
value = starttag_re.sub('', value)
|
value = starttag_re.sub('', value)
|
||||||
value = endtag_re.sub('', value)
|
value = endtag_re.sub('', value)
|
||||||
return value
|
return value
|
||||||
|
removetags = stringfilter(removetags)
|
||||||
|
|
||||||
def striptags(value):
|
def striptags(value):
|
||||||
"Strips all [X]HTML tags"
|
"Strips all [X]HTML tags"
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
if not isinstance(value, basestring):
|
|
||||||
value = str(value)
|
|
||||||
return strip_tags(value)
|
return strip_tags(value)
|
||||||
|
striptags = stringfilter(striptags)
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# LISTS #
|
# LISTS #
|
||||||
@ -265,7 +319,7 @@ def first(value):
|
|||||||
def join(value, arg):
|
def join(value, arg):
|
||||||
"Joins a list with a string, like Python's ``str.join(list)``"
|
"Joins a list with a string, like Python's ``str.join(list)``"
|
||||||
try:
|
try:
|
||||||
return arg.join(map(str, value))
|
return arg.join(map(smart_string, value))
|
||||||
except AttributeError: # fail silently but nicely
|
except AttributeError: # fail silently but nicely
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -654,6 +654,18 @@ decorator instead::
|
|||||||
If you leave off the ``name`` argument, as in the second example above, Django
|
If you leave off the ``name`` argument, as in the second example above, Django
|
||||||
will use the function's name as the filter name.
|
will use the function's name as the filter name.
|
||||||
|
|
||||||
|
Template filters which expect strings
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
If you are writing a template filter which only expects a string as the first
|
||||||
|
argument, you should use the included decorator ``stringfilter`` which will convert
|
||||||
|
an object to it's string value before being passed to your function::
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
@template.stringfilter
|
||||||
|
def lower(value):
|
||||||
|
return value.lower()
|
||||||
|
|
||||||
Writing custom template tags
|
Writing custom template tags
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
2
setup.py
2
setup.py
@ -28,7 +28,7 @@ for dirpath, dirnames, filenames in os.walk(django_dir):
|
|||||||
|
|
||||||
# Small hack for working with bdist_wininst.
|
# Small hack for working with bdist_wininst.
|
||||||
# See http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html
|
# See http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html
|
||||||
if sys.argv[1] == 'bdist_wininst':
|
if len(sys.argv) > 1 and sys.argv[1] == 'bdist_wininst':
|
||||||
for file_info in data_files:
|
for file_info in data_files:
|
||||||
file_info[0] = '/PURELIB/%s' % file_info[0]
|
file_info[0] = '/PURELIB/%s' % file_info[0]
|
||||||
|
|
||||||
|
@ -40,13 +40,27 @@ class Writer(models.Model):
|
|||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
headline = models.CharField(maxlength=50)
|
headline = models.CharField(maxlength=50)
|
||||||
pub_date = models.DateField()
|
pub_date = models.DateField()
|
||||||
|
created = models.DateField(editable=False)
|
||||||
writer = models.ForeignKey(Writer)
|
writer = models.ForeignKey(Writer)
|
||||||
article = models.TextField()
|
article = models.TextField()
|
||||||
categories = models.ManyToManyField(Category, blank=True)
|
categories = models.ManyToManyField(Category, blank=True)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
import datetime
|
||||||
|
if not self.id:
|
||||||
|
self.created = datetime.date.today()
|
||||||
|
return super(Article, self).save()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.headline
|
return self.headline
|
||||||
|
|
||||||
|
class PhoneNumber(models.Model):
|
||||||
|
phone = models.PhoneNumberField()
|
||||||
|
description = models.CharField(maxlength=20)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.phone
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
|
>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
@ -281,4 +295,170 @@ existing Category instance.
|
|||||||
<Category: Third>
|
<Category: Third>
|
||||||
>>> Category.objects.get(id=3)
|
>>> Category.objects.get(id=3)
|
||||||
<Category: Third>
|
<Category: Third>
|
||||||
|
|
||||||
|
Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
|
||||||
|
at runtime, based on the data in the database when the form is displayed, not
|
||||||
|
the data in the database when the form is instantiated.
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
>>> f = ArticleForm(auto_id=False)
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Headline: <input type="text" name="headline" maxlength="50" /></li>
|
||||||
|
<li>Pub date: <input type="text" name="pub_date" /></li>
|
||||||
|
<li>Writer: <select name="writer">
|
||||||
|
<option value="" selected="selected">---------</option>
|
||||||
|
<option value="1">Mike Royko</option>
|
||||||
|
<option value="2">Bob Woodward</option>
|
||||||
|
</select></li>
|
||||||
|
<li>Article: <textarea name="article"></textarea></li>
|
||||||
|
<li>Categories: <select multiple="multiple" name="categories">
|
||||||
|
<option value="1">Entertainment</option>
|
||||||
|
<option value="2">It's a test</option>
|
||||||
|
<option value="3">Third</option>
|
||||||
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
|
>>> Category.objects.create(name='Fourth', url='4th')
|
||||||
|
<Category: Fourth>
|
||||||
|
>>> Writer.objects.create(name='Carl Bernstein')
|
||||||
|
<Writer: Carl Bernstein>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Headline: <input type="text" name="headline" maxlength="50" /></li>
|
||||||
|
<li>Pub date: <input type="text" name="pub_date" /></li>
|
||||||
|
<li>Writer: <select name="writer">
|
||||||
|
<option value="" selected="selected">---------</option>
|
||||||
|
<option value="1">Mike Royko</option>
|
||||||
|
<option value="2">Bob Woodward</option>
|
||||||
|
<option value="3">Carl Bernstein</option>
|
||||||
|
</select></li>
|
||||||
|
<li>Article: <textarea name="article"></textarea></li>
|
||||||
|
<li>Categories: <select multiple="multiple" name="categories">
|
||||||
|
<option value="1">Entertainment</option>
|
||||||
|
<option value="2">It's a test</option>
|
||||||
|
<option value="3">Third</option>
|
||||||
|
<option value="4">Fourth</option>
|
||||||
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
|
|
||||||
|
# ModelChoiceField ############################################################
|
||||||
|
|
||||||
|
>>> from django.newforms import ModelChoiceField, ModelMultipleChoiceField
|
||||||
|
|
||||||
|
>>> f = ModelChoiceField(Category.objects.all())
|
||||||
|
>>> f.clean('')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean(0)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
|
||||||
|
>>> f.clean(3)
|
||||||
|
<Category: Third>
|
||||||
|
>>> f.clean(2)
|
||||||
|
<Category: It's a test>
|
||||||
|
|
||||||
|
# Add a Category object *after* the ModelChoiceField has already been
|
||||||
|
# instantiated. This proves clean() checks the database during clean() rather
|
||||||
|
# than caching it at time of instantiation.
|
||||||
|
>>> Category.objects.create(name='Fifth', url='5th')
|
||||||
|
<Category: Fifth>
|
||||||
|
>>> f.clean(5)
|
||||||
|
<Category: Fifth>
|
||||||
|
|
||||||
|
# Delete a Category object *after* the ModelChoiceField has already been
|
||||||
|
# instantiated. This proves clean() checks the database during clean() rather
|
||||||
|
# than caching it at time of instantiation.
|
||||||
|
>>> Category.objects.get(url='5th').delete()
|
||||||
|
>>> f.clean(5)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
|
||||||
|
|
||||||
|
>>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False)
|
||||||
|
>>> print f.clean('')
|
||||||
|
None
|
||||||
|
>>> f.clean('')
|
||||||
|
>>> f.clean('1')
|
||||||
|
<Category: Entertainment>
|
||||||
|
>>> f.clean('100')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
|
||||||
|
|
||||||
|
# ModelMultipleChoiceField ####################################################
|
||||||
|
|
||||||
|
>>> f = ModelMultipleChoiceField(Category.objects.all())
|
||||||
|
>>> f.clean(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean([])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean([1])
|
||||||
|
[<Category: Entertainment>]
|
||||||
|
>>> f.clean([2])
|
||||||
|
[<Category: It's a test>]
|
||||||
|
>>> f.clean(['1'])
|
||||||
|
[<Category: Entertainment>]
|
||||||
|
>>> f.clean(['1', '2'])
|
||||||
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
>>> f.clean([1, '2'])
|
||||||
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
>>> f.clean((1, '2'))
|
||||||
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
>>> f.clean(['100'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. 100 is not one of the available choices.']
|
||||||
|
>>> f.clean('hello')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a list of values.']
|
||||||
|
|
||||||
|
# Add a Category object *after* the ModelChoiceField has already been
|
||||||
|
# instantiated. This proves clean() checks the database during clean() rather
|
||||||
|
# than caching it at time of instantiation.
|
||||||
|
>>> Category.objects.create(id=6, name='Sixth', url='6th')
|
||||||
|
<Category: Sixth>
|
||||||
|
>>> f.clean([6])
|
||||||
|
[<Category: Sixth>]
|
||||||
|
|
||||||
|
# Delete a Category object *after* the ModelChoiceField has already been
|
||||||
|
# instantiated. This proves clean() checks the database during clean() rather
|
||||||
|
# than caching it at time of instantiation.
|
||||||
|
>>> Category.objects.get(url='6th').delete()
|
||||||
|
>>> f.clean([6])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. 6 is not one of the available choices.']
|
||||||
|
|
||||||
|
>>> f = ModelMultipleChoiceField(Category.objects.all(), required=False)
|
||||||
|
>>> f.clean([])
|
||||||
|
[]
|
||||||
|
>>> f.clean(())
|
||||||
|
[]
|
||||||
|
>>> f.clean(['10'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
|
||||||
|
>>> f.clean(['3', '10'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
|
||||||
|
>>> f.clean(['1', '10'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
|
||||||
|
|
||||||
|
# PhoneNumberField ############################################################
|
||||||
|
|
||||||
|
>>> PhoneNumberForm = form_for_model(PhoneNumber)
|
||||||
|
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||||
|
>>> f.is_valid()
|
||||||
|
True
|
||||||
|
>>> f.clean_data
|
||||||
|
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
||||||
"""}
|
"""}
|
||||||
|
@ -388,7 +388,53 @@ False
|
|||||||
>>> phone2numeric('0800 flowers')
|
>>> phone2numeric('0800 flowers')
|
||||||
'0800 3569377'
|
'0800 3569377'
|
||||||
|
|
||||||
|
# Filters shouldn't break if passed non-strings
|
||||||
|
>>> addslashes(123)
|
||||||
|
'123'
|
||||||
|
>>> linenumbers(123)
|
||||||
|
'1. 123'
|
||||||
|
>>> lower(123)
|
||||||
|
'123'
|
||||||
|
>>> make_list(123)
|
||||||
|
['1', '2', '3']
|
||||||
|
>>> slugify(123)
|
||||||
|
'123'
|
||||||
|
>>> title(123)
|
||||||
|
'123'
|
||||||
|
>>> truncatewords(123, 2)
|
||||||
|
'123'
|
||||||
|
>>> upper(123)
|
||||||
|
'123'
|
||||||
|
>>> urlencode(123)
|
||||||
|
'123'
|
||||||
|
>>> urlize(123)
|
||||||
|
'123'
|
||||||
|
>>> urlizetrunc(123, 1)
|
||||||
|
'123'
|
||||||
|
>>> wordcount(123)
|
||||||
|
1
|
||||||
|
>>> wordwrap(123, 2)
|
||||||
|
'123'
|
||||||
|
>>> ljust('123', 4)
|
||||||
|
'123 '
|
||||||
|
>>> rjust('123', 4)
|
||||||
|
' 123'
|
||||||
|
>>> center('123', 5)
|
||||||
|
' 123 '
|
||||||
|
>>> center('123', 6)
|
||||||
|
' 123 '
|
||||||
|
>>> cut(123, '2')
|
||||||
|
'13'
|
||||||
|
>>> escape(123)
|
||||||
|
'123'
|
||||||
|
>>> linebreaks(123)
|
||||||
|
'<p>123</p>'
|
||||||
|
>>> linebreaksbr(123)
|
||||||
|
'123'
|
||||||
|
>>> removetags(123, 'a')
|
||||||
|
'123'
|
||||||
|
>>> striptags(123)
|
||||||
|
'123'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -318,6 +318,7 @@ The value is compared to its str():
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
The 'choices' argument can be any iterable:
|
The 'choices' argument can be any iterable:
|
||||||
|
>>> from itertools import chain
|
||||||
>>> def get_choices():
|
>>> def get_choices():
|
||||||
... for i in range(5):
|
... for i in range(5):
|
||||||
... yield (i, i)
|
... yield (i, i)
|
||||||
@ -329,6 +330,17 @@ The 'choices' argument can be any iterable:
|
|||||||
<option value="3">3</option>
|
<option value="3">3</option>
|
||||||
<option value="4">4</option>
|
<option value="4">4</option>
|
||||||
</select>
|
</select>
|
||||||
|
>>> things = ({'id': 1, 'name': 'And Boom'}, {'id': 2, 'name': 'One More Thing!'})
|
||||||
|
>>> class SomeForm(Form):
|
||||||
|
... somechoice = ChoiceField(choices=chain((('', '-'*9),), [(thing['id'], thing['name']) for thing in things]))
|
||||||
|
>>> f = SomeForm()
|
||||||
|
>>> f.as_table()
|
||||||
|
u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>'
|
||||||
|
>>> f.as_table()
|
||||||
|
u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="" selected="selected">---------</option>\n<option value="1">And Boom</option>\n<option value="2">One More Thing!</option>\n</select></td></tr>'
|
||||||
|
>>> f = SomeForm({'somechoice': 2})
|
||||||
|
>>> f.as_table()
|
||||||
|
u'<tr><th><label for="id_somechoice">Somechoice:</label></th><td><select name="somechoice" id="id_somechoice">\n<option value="">---------</option>\n<option value="1">And Boom</option>\n<option value="2" selected="selected">One More Thing!</option>\n</select></td></tr>'
|
||||||
|
|
||||||
You can also pass 'choices' to the constructor:
|
You can also pass 'choices' to the constructor:
|
||||||
>>> w = Select(choices=[(1, 1), (2, 2), (3, 3)])
|
>>> w = Select(choices=[(1, 1), (2, 2), (3, 3)])
|
||||||
@ -1999,6 +2011,19 @@ For a form with a <select>, use ChoiceField:
|
|||||||
<option value="J">Java</option>
|
<option value="J">Java</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
A subtlety: If one of the choices' value is the empty string and the form is
|
||||||
|
unbound, then the <option> for the empty-string choice will get selected="selected".
|
||||||
|
>>> class FrameworkForm(Form):
|
||||||
|
... name = CharField()
|
||||||
|
... language = ChoiceField(choices=[('', '------'), ('P', 'Python'), ('J', 'Java')])
|
||||||
|
>>> f = FrameworkForm(auto_id=False)
|
||||||
|
>>> print f['language']
|
||||||
|
<select name="language">
|
||||||
|
<option value="" selected="selected">------</option>
|
||||||
|
<option value="P">Python</option>
|
||||||
|
<option value="J">Java</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
You can specify widget attributes in the Widget constructor.
|
You can specify widget attributes in the Widget constructor.
|
||||||
>>> class FrameworkForm(Form):
|
>>> class FrameworkForm(Form):
|
||||||
... name = CharField()
|
... name = CharField()
|
||||||
@ -3304,6 +3329,75 @@ u''
|
|||||||
>>> f.clean('')
|
>>> f.clean('')
|
||||||
u''
|
u''
|
||||||
|
|
||||||
|
# USPhoneNumberField ##########################################################
|
||||||
|
|
||||||
|
USPhoneNumberField validates that the data is a valid U.S. phone number,
|
||||||
|
including the area code. It's normalized to XXX-XXX-XXXX format.
|
||||||
|
>>> from django.contrib.localflavor.usa.forms import USPhoneNumberField
|
||||||
|
>>> f = USPhoneNumberField()
|
||||||
|
>>> f.clean('312-555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('3125551212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312 555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('(312) 555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312 555 1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312.555.1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312.555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean(' (312) 555.1212 ')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('555-1212')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||||
|
>>> f.clean('312-55-1212')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||||
|
>>> f.clean(None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
>>> f.clean('')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'This field is required.']
|
||||||
|
|
||||||
|
>>> f = USPhoneNumberField(required=False)
|
||||||
|
>>> f.clean('312-555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('3125551212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312 555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('(312) 555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312 555 1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312.555.1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('312.555-1212')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean(' (312) 555.1212 ')
|
||||||
|
u'312-555-1212'
|
||||||
|
>>> f.clean('555-1212')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||||
|
>>> f.clean('312-55-1212')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||||
|
>>> f.clean(None)
|
||||||
|
u''
|
||||||
|
>>> f.clean('')
|
||||||
|
u''
|
||||||
|
|
||||||
# USStateField ################################################################
|
# USStateField ################################################################
|
||||||
|
|
||||||
USStateField validates that the data is either an abbreviation or name of a
|
USStateField validates that the data is either an abbreviation or name of a
|
||||||
|
Loading…
x
Reference in New Issue
Block a user