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:
Adrian Holovaty 2007-02-20 02:42:07 +00:00
parent 907241e299
commit e56934b9b9
3 changed files with 124 additions and 8 deletions

View File

@ -553,9 +553,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):
@ -619,9 +619,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):
@ -742,9 +742,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,

View File

@ -4,8 +4,11 @@ and database field objects.
""" """
from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList 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): def model_save(self, commit=True):
""" """
@ -33,7 +36,7 @@ def save_instance(form, instance, commit=True):
for f in opts.fields: for f in opts.fields:
if isinstance(f, models.AutoField): if 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:
@ -96,3 +99,34 @@ 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])
return type('FormForFields', (BaseForm,), {'base_fields': fields}) 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)

View File

@ -281,4 +281,86 @@ existing Category instance.
<Category: Third> <Category: Third>
>>> Category.objects.get(id=3) >>> Category.objects.get(id=3)
<Category: Third> <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.']
"""} """}