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))
|
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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.']
|
||||||
"""}
|
"""}
|
||||||
|
|
Loading…
Reference in New Issue