Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #3257 -- Added newforms ModelChoiceField and ModelMultipleChoic…

…eField, 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
  • Loading branch information...
commit e56934b9b903535ace50f46d892dab4225b67f61 1 parent 907241e
@adrianholovaty adrianholovaty authored
View
12 django/db/models/fields/related.py
@@ -553,9 +553,9 @@ def contribute_to_related_class(self, cls, related):
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 @@ def contribute_to_related_class(self, cls, related):
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 @@ def formfield(self, **kwargs):
# 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,
View
38 django/newforms/models.py
@@ -4,8 +4,11 @@
"""
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)
View
82 tests/modeltests/model_forms/models.py
@@ -281,4 +281,86 @@ def __str__(self):
<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.']
"""}
Please sign in to comment.
Something went wrong with that request. Please try again.