Fixed #3257 -- Added newforms ModelChoiceField and ModelMultipleChoiceField, which are now used by form_for_model() and form_for_instance(). Thanks for the patch, Honza Kral, floguy@gmail.com and kilian.cavalotti
git-svn-id: http://code.djangoproject.com/svn/django/trunk@4547 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
907241e299
commit
e56934b9b9
|
@ -553,9 +553,9 @@ class ForeignKey(RelatedField, Field):
|
|||
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
|
||||
|
||||
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)
|
||||
return forms.ChoiceField(**defaults)
|
||||
return forms.ModelChoiceField(**defaults)
|
||||
|
||||
class OneToOneField(RelatedField, IntegerField):
|
||||
def __init__(self, to, to_field=None, **kwargs):
|
||||
|
@ -619,9 +619,9 @@ class OneToOneField(RelatedField, IntegerField):
|
|||
cls._meta.one_to_one_field = self
|
||||
|
||||
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)
|
||||
return forms.ChoiceField(**kwargs)
|
||||
return forms.ModelChoiceField(**kwargs)
|
||||
|
||||
class ManyToManyField(RelatedField, Field):
|
||||
def __init__(self, to, **kwargs):
|
||||
|
@ -742,9 +742,9 @@ class ManyToManyField(RelatedField, Field):
|
|||
# MultipleChoiceField takes a list of IDs.
|
||||
if kwargs.get('initial') is not None:
|
||||
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)
|
||||
return forms.MultipleChoiceField(**defaults)
|
||||
return forms.ModelMultipleChoiceField(**defaults)
|
||||
|
||||
class ManyToOneRel(object):
|
||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||
|
|
|
@ -4,8 +4,11 @@ and database field objects.
|
|||
"""
|
||||
|
||||
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList
|
||||
from fields import ChoiceField, MultipleChoiceField
|
||||
from widgets import Select, SelectMultiple
|
||||
|
||||
__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):
|
||||
"""
|
||||
|
@ -33,7 +36,7 @@ def save_instance(form, instance, commit=True):
|
|||
for f in opts.fields:
|
||||
if isinstance(f, models.AutoField):
|
||||
continue
|
||||
setattr(instance, f.attname, clean_data[f.name])
|
||||
setattr(instance, f.name, clean_data[f.name])
|
||||
if commit:
|
||||
instance.save()
|
||||
for f in opts.many_to_many:
|
||||
|
@ -96,3 +99,34 @@ def form_for_fields(field_list):
|
|||
"Returns a Form class for the given list of Django database field instances."
|
||||
fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list])
|
||||
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||
|
||||
class ModelChoiceField(ChoiceField):
|
||||
"A ChoiceField whose choices are a model QuerySet."
|
||||
def __init__(self, queryset, empty_label=u"---------", **kwargs):
|
||||
self.model = queryset.model
|
||||
choices = [(obj._get_pk_val(), str(obj)) for obj in queryset]
|
||||
if empty_label is not None:
|
||||
choices = [(u"", empty_label)] + choices
|
||||
ChoiceField.__init__(self, choices=choices, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
value = ChoiceField.clean(self, value)
|
||||
if not value:
|
||||
return None
|
||||
try:
|
||||
value = self.model._default_manager.get(pk=value)
|
||||
except self.model.DoesNotExist:
|
||||
raise ValidationError(gettext(u'Select a valid choice. That choice is not one of the available choices.'))
|
||||
return value
|
||||
|
||||
class ModelMultipleChoiceField(MultipleChoiceField):
|
||||
"A MultipleChoiceField whose choices are a model QuerySet."
|
||||
def __init__(self, queryset, **kwargs):
|
||||
self.model = queryset.model
|
||||
MultipleChoiceField.__init__(self, choices=[(obj._get_pk_val(), str(obj)) for obj in queryset], **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
value = MultipleChoiceField.clean(self, value)
|
||||
if not value:
|
||||
return []
|
||||
return self.model._default_manager.filter(pk__in=value)
|
||||
|
|
|
@ -281,4 +281,86 @@ existing Category instance.
|
|||
<Category: Third>
|
||||
>>> Category.objects.get(id=3)
|
||||
<Category: Third>
|
||||
|
||||
# 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>
|
||||
|
||||
>>> f = ModelChoiceField(Category.objects.filter(pk=1), required=False)
|
||||
>>> print f.clean('')
|
||||
None
|
||||
>>> f.clean('')
|
||||
>>> f.clean('1')
|
||||
<Category: Entertainment>
|
||||
>>> f.clean('2')
|
||||
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(['nonexistent'])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Select a valid choice. nonexistent is not one of the available choices.']
|
||||
>>> f.clean('hello')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a list of values.']
|
||||
>>> f = ModelMultipleChoiceField(Category.objects.all(), required=False)
|
||||
>>> f.clean([])
|
||||
[]
|
||||
>>> f.clean(())
|
||||
[]
|
||||
>>> f.clean(['4'])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
|
||||
>>> f.clean(['3', '4'])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
|
||||
>>> f.clean(['1', '5'])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Select a valid choice. 5 is not one of the available choices.']
|
||||
"""}
|
||||
|
|
Loading…
Reference in New Issue