Ticket #9319: models.py

File models.py, 28.7 KB (added by tangc, 16 years ago)
Line 
1"""
2Helper functions for creating Form classes from Django models
3and database field objects.
4"""
5
6from django.utils.encoding import smart_unicode
7from django.utils.datastructures import SortedDict
8from django.utils.text import get_text_list, capfirst
9from django.utils.translation import ugettext_lazy as _
10
11from util import ValidationError, ErrorList
12from forms import BaseForm, get_declared_fields
13from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
14from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
15from widgets import media_property
16from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
17
18__all__ = (
19 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
20 'save_instance', 'form_for_fields', 'ModelChoiceField',
21 'ModelMultipleChoiceField',
22)
23
24
25def save_instance(form, instance, fields=None, fail_message='saved',
26 commit=True, exclude=None):
27 """
28 Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
29
30 If commit=True, then the changes to ``instance`` will be saved to the
31 database. Returns ``instance``.
32 """
33 from django.db import models
34 opts = instance._meta
35 if form.errors:
36 raise ValueError("The %s could not be %s because the data didn't"
37 " validate." % (opts.object_name, fail_message))
38 cleaned_data = form.cleaned_data
39 for f in opts.fields:
40 if not f.editable or isinstance(f, models.AutoField) \
41 or not f.name in cleaned_data:
42 continue
43 if fields and f.name not in fields:
44 continue
45 if exclude and f.name in exclude:
46 continue
47 f.save_form_data(instance, cleaned_data[f.name])
48 # Wrap up the saving of m2m data as a function.
49 def save_m2m():
50 opts = instance._meta
51 cleaned_data = form.cleaned_data
52 for f in opts.many_to_many:
53 if fields and f.name not in fields:
54 continue
55 if f.name in cleaned_data:
56 f.save_form_data(instance, cleaned_data[f.name])
57 if commit:
58 # If we are committing, save the instance and the m2m data immediately.
59 instance.save()
60 save_m2m()
61 else:
62 # We're not committing. Add a method to the form to allow deferred
63 # saving of m2m data.
64 form.save_m2m = save_m2m
65 return instance
66
67def make_model_save(model, fields, fail_message):
68 """Returns the save() method for a Form."""
69 def save(self, commit=True):
70 return save_instance(self, model(), fields, fail_message, commit)
71 return save
72
73def make_instance_save(instance, fields, fail_message):
74 """Returns the save() method for a Form."""
75 def save(self, commit=True):
76 return save_instance(self, instance, fields, fail_message, commit)
77 return save
78
79def form_for_fields(field_list):
80 """
81 Returns a Form class for the given list of Django database field instances.
82 """
83 fields = SortedDict([(f.name, f.formfield())
84 for f in field_list if f.editable])
85 return type('FormForFields', (BaseForm,), {'base_fields': fields})
86
87
88# ModelForms #################################################################
89
90def model_to_dict(instance, fields=None, exclude=None):
91 """
92 Returns a dict containing the data in ``instance`` suitable for passing as
93 a Form's ``initial`` keyword argument.
94
95 ``fields`` is an optional list of field names. If provided, only the named
96 fields will be included in the returned dict.
97
98 ``exclude`` is an optional list of field names. If provided, the named
99 fields will be excluded from the returned dict, even if they are listed in
100 the ``fields`` argument.
101 """
102 # avoid a circular import
103 from django.db.models.fields.related import ManyToManyField, OneToOneField
104 opts = instance._meta
105 data = {}
106 for f in opts.fields + opts.many_to_many:
107 if not f.editable:
108 continue
109 if fields and not f.name in fields:
110 continue
111 if exclude and f.name in exclude:
112 continue
113 if isinstance(f, ManyToManyField):
114 # If the object doesn't have a primry key yet, just use an empty
115 # list for its m2m fields. Calling f.value_from_object will raise
116 # an exception.
117 if instance.pk is None:
118 data[f.name] = []
119 else:
120 # MultipleChoiceWidget needs a list of pks, not object instances.
121 data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
122 else:
123 data[f.name] = f.value_from_object(instance)
124 return data
125
126def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
127 """
128 Returns a ``SortedDict`` containing form fields for the given model.
129
130 ``fields`` is an optional list of field names. If provided, only the named
131 fields will be included in the returned fields.
132
133 ``exclude`` is an optional list of field names. If provided, the named
134 fields will be excluded from the returned fields, even if they are listed
135 in the ``fields`` argument.
136 """
137 # TODO: if fields is provided, it would be nice to return fields in that order
138 field_list = []
139 opts = model._meta
140 for f in opts.fields + opts.many_to_many:
141 if not f.editable:
142 continue
143 if fields and not f.name in fields:
144 continue
145 if exclude and f.name in exclude:
146 continue
147 formfield = formfield_callback(f)
148 if formfield:
149 field_list.append((f.name, formfield))
150 return SortedDict(field_list)
151
152class ModelFormOptions(object):
153 def __init__(self, options=None):
154 self.model = getattr(options, 'model', None)
155 self.fields = getattr(options, 'fields', None)
156 self.exclude = getattr(options, 'exclude', None)
157
158
159class ModelFormMetaclass(type):
160 def __new__(cls, name, bases, attrs):
161 formfield_callback = attrs.pop('formfield_callback',
162 lambda f: f.formfield())
163 try:
164 parents = [b for b in bases if issubclass(b, ModelForm)]
165 except NameError:
166 # We are defining ModelForm itself.
167 parents = None
168 declared_fields = get_declared_fields(bases, attrs, False)
169 new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases,
170 attrs)
171 if not parents:
172 return new_class
173
174 if 'media' not in attrs:
175 new_class.media = media_property(new_class)
176 opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
177 if opts.model:
178 # If a model is defined, extract form fields from it.
179 fields = fields_for_model(opts.model, opts.fields,
180 opts.exclude, formfield_callback)
181 # Override default model fields with any custom declared ones
182 # (plus, include all the other declared fields).
183 fields.update(declared_fields)
184 else:
185 fields = declared_fields
186 new_class.declared_fields = declared_fields
187 new_class.base_fields = fields
188 return new_class
189
190class BaseModelForm(BaseForm):
191 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
192 initial=None, error_class=ErrorList, label_suffix=':',
193 empty_permitted=False, instance=None):
194 opts = self._meta
195 if instance is None:
196 # if we didn't get an instance, instantiate a new one
197 self.instance = opts.model()
198 object_data = {}
199 else:
200 self.instance = instance
201 object_data = model_to_dict(instance, opts.fields, opts.exclude)
202 # if initial was provided, it should override the values from instance
203 if initial is not None:
204 object_data.update(initial)
205 super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
206 error_class, label_suffix, empty_permitted)
207 def clean(self):
208 self.validate_unique()
209 return self.cleaned_data
210
211 def validate_unique(self):
212 from django.db.models.fields import FieldDoesNotExist
213
214 # Gather a list of checks to perform. Since this is a ModelForm, some
215 # fields may have been excluded; we can't perform a unique check on a
216 # form that is missing fields involved in that check.
217 unique_checks = []
218 for check in self.instance._meta.unique_together[:]:
219 fields_on_form = [field for field in check if field in self.fields]
220 if len(fields_on_form) == len(check):
221 unique_checks.append(check)
222
223 form_errors = []
224
225 # Gather a list of checks for fields declared as unique and add them to
226 # the list of checks. Again, skip fields not on the form.
227 for name, field in self.fields.items():
228 try:
229 f = self.instance._meta.get_field_by_name(name)[0]
230 except FieldDoesNotExist:
231 # This is an extra field that's not on the ModelForm, ignore it
232 continue
233 # MySQL can't handle ... WHERE pk IS NULL, so make sure we
234 # don't generate queries of that form.
235 is_null_pk = f.primary_key and self.cleaned_data[name] is None
236 if name in self.cleaned_data and f.unique and not is_null_pk:
237 unique_checks.append((name,))
238
239 # Don't run unique checks on fields that already have an error.
240 unique_checks = [check for check in unique_checks if not [x in self._errors for x in check if x in self._errors]]
241
242 for unique_check in unique_checks:
243 # Try to look up an existing object with the same values as this
244 # object's values for all the unique field.
245
246 lookup_kwargs = {}
247 print '------------------------------------'
248 print unique_check
249 for field_name in unique_check:
250 print '========================================================'
251 print self.cleaned_data
252 lookup_kwargs[field_name] = self.cleaned_data[field_name]
253
254 qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
255
256 # Exclude the current object from the query if we are editing an
257 # instance (as opposed to creating a new one)
258 if self.instance.pk is not None:
259 qs = qs.exclude(pk=self.instance.pk)
260
261 # This cute trick with extra/values is the most efficient way to
262 # tell if a particular query returns any results.
263 if qs.extra(select={'a': 1}).values('a').order_by():
264 model_name = capfirst(self.instance._meta.verbose_name)
265
266 # A unique field
267 if len(unique_check) == 1:
268 field_name = unique_check[0]
269 field_label = self.fields[field_name].label
270 # Insert the error into the error dict, very sneaky
271 self._errors[field_name] = ErrorList([
272 _(u"%(model_name)s with this %(field_label)s already exists.") % \
273 {'model_name': unicode(model_name),
274 'field_label': unicode(field_label)}
275 ])
276 # unique_together
277 else:
278 field_labels = [self.fields[field_name].label for field_name in unique_check]
279 field_labels = get_text_list(field_labels, _('and'))
280 form_errors.append(
281 _(u"%(model_name)s with this %(field_label)s already exists.") % \
282 {'model_name': unicode(model_name),
283 'field_label': unicode(field_labels)}
284 )
285
286 # Remove the data from the cleaned_data dict since it was invalid
287 #for field_name in unique_check:
288 # del self.cleaned_data[field_name]
289
290 if form_errors:
291 # Raise the unique together errors since they are considered form-wide.
292 raise ValidationError(form_errors)
293
294 def save(self, commit=True):
295 """
296 Saves this ``form``'s cleaned_data into model instance
297 ``self.instance``.
298
299 If commit=True, then the changes to ``instance`` will be saved to the
300 database. Returns ``instance``.
301 """
302 if self.instance.pk is None:
303 fail_message = 'created'
304 else:
305 fail_message = 'changed'
306 return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
307
308class ModelForm(BaseModelForm):
309 __metaclass__ = ModelFormMetaclass
310
311def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
312 formfield_callback=lambda f: f.formfield()):
313 # HACK: we should be able to construct a ModelForm without creating
314 # and passing in a temporary inner class
315 class Meta:
316 pass
317 setattr(Meta, 'model', model)
318 setattr(Meta, 'fields', fields)
319 setattr(Meta, 'exclude', exclude)
320 class_name = model.__name__ + 'Form'
321 return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
322 'formfield_callback': formfield_callback})
323
324
325# ModelFormSets ##############################################################
326
327class BaseModelFormSet(BaseFormSet):
328 """
329 A ``FormSet`` for editing a queryset and/or adding new objects to it.
330 """
331 model = None
332
333 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
334 queryset=None, **kwargs):
335 self.queryset = queryset
336 defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
337 defaults['initial'] = [model_to_dict(obj) for obj in self.get_queryset()]
338 defaults.update(kwargs)
339 super(BaseModelFormSet, self).__init__(**defaults)
340
341 def _construct_form(self, i, **kwargs):
342 if i < self._initial_form_count:
343 kwargs['instance'] = self.get_queryset()[i]
344 return super(BaseModelFormSet, self)._construct_form(i, **kwargs)
345
346 def get_queryset(self):
347 if not hasattr(self, '_queryset'):
348 if self.queryset is not None:
349 qs = self.queryset
350 else:
351 qs = self.model._default_manager.get_query_set()
352 if self.max_num > 0:
353 self._queryset = qs[:self.max_num]
354 else:
355 self._queryset = qs
356 return self._queryset
357
358 def save_new(self, form, commit=True):
359 """Saves and returns a new model instance for the given form."""
360 return save_instance(form, self.model(), exclude=[self._pk_field.name], commit=commit)
361
362 def save_existing(self, form, instance, commit=True):
363 """Saves and returns an existing model instance for the given form."""
364 return save_instance(form, instance, exclude=[self._pk_field.name], commit=commit)
365
366 def save(self, commit=True):
367 """Saves model instances for every form, adding and changing instances
368 as necessary, and returns the list of instances.
369 """
370 if not commit:
371 self.saved_forms = []
372 def save_m2m():
373 for form in self.saved_forms:
374 form.save_m2m()
375 self.save_m2m = save_m2m
376 return self.save_existing_objects(commit) + self.save_new_objects(commit)
377
378 def save_existing_objects(self, commit=True):
379 self.changed_objects = []
380 self.deleted_objects = []
381 if not self.get_queryset():
382 return []
383
384 # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
385 existing_objects = {}
386 for obj in self.get_queryset():
387 existing_objects[obj.pk] = obj
388 saved_instances = []
389 for form in self.initial_forms:
390 obj = existing_objects[form.cleaned_data[self._pk_field.name]]
391 if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
392 self.deleted_objects.append(obj)
393 obj.delete()
394 else:
395 if form.changed_data:
396 self.changed_objects.append((obj, form.changed_data))
397 saved_instances.append(self.save_existing(form, obj, commit=commit))
398 if not commit:
399 self.saved_forms.append(form)
400 return saved_instances
401
402 def save_new_objects(self, commit=True):
403 self.new_objects = []
404 for form in self.extra_forms:
405 if not form.has_changed():
406 continue
407 # If someone has marked an add form for deletion, don't save the
408 # object.
409 if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
410 continue
411 self.new_objects.append(self.save_new(form, commit=commit))
412 if not commit:
413 self.saved_forms.append(form)
414 return self.new_objects
415
416 def add_fields(self, form, index):
417 """Add a hidden field for the object's primary key."""
418 from django.db.models import AutoField
419 self._pk_field = pk = self.model._meta.pk
420 if pk.auto_created or isinstance(pk, AutoField):
421 form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput)
422 super(BaseModelFormSet, self).add_fields(form, index)
423
424def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
425 formset=BaseModelFormSet,
426 extra=1, can_delete=False, can_order=False,
427 max_num=0, fields=None, exclude=None):
428 """
429 Returns a FormSet class for the given Django model class.
430 """
431 form = modelform_factory(model, form=form, fields=fields, exclude=exclude,
432 formfield_callback=formfield_callback)
433 FormSet = formset_factory(form, formset, extra=extra, max_num=max_num,
434 can_order=can_order, can_delete=can_delete)
435 FormSet.model = model
436 return FormSet
437
438
439# InlineFormSets #############################################################
440
441class BaseInlineFormSet(BaseModelFormSet):
442 """A formset for child objects related to a parent."""
443 def __init__(self, data=None, files=None, instance=None,
444 save_as_new=False, prefix=None):
445 from django.db.models.fields.related import RelatedObject
446 self.instance = instance
447 self.save_as_new = save_as_new
448 # is there a better way to get the object descriptor?
449 self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
450 super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix or self.rel_name)
451
452 def _construct_forms(self):
453 if self.save_as_new:
454 self._total_form_count = self._initial_form_count
455 self._initial_form_count = 0
456 super(BaseInlineFormSet, self)._construct_forms()
457
458 def _construct_form(self, i, **kwargs):
459 form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs)
460 if self.save_as_new:
461 # Remove the primary key from the form's data, we are only
462 # creating new instances
463 form.data[form.add_prefix(self._pk_field.name)] = None
464 return form
465
466 def get_queryset(self):
467 """
468 Returns this FormSet's queryset, but restricted to children of
469 self.instance
470 """
471 kwargs = {self.fk.name: self.instance}
472 return self.model._default_manager.filter(**kwargs)
473
474 def save_new(self, form, commit=True):
475 kwargs = {self.fk.get_attname(): self.instance.pk}
476 new_obj = self.model(**kwargs)
477 return save_instance(form, new_obj, exclude=[self._pk_field.name], commit=commit)
478
479 def add_fields(self, form, index):
480 super(BaseInlineFormSet, self).add_fields(form, index)
481 if self._pk_field == self.fk:
482 form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput)
483
484def _get_foreign_key(parent_model, model, fk_name=None):
485 """
486 Finds and returns the ForeignKey from model to parent if there is one.
487 If fk_name is provided, assume it is the name of the ForeignKey field.
488 """
489 # avoid circular import
490 from django.db.models import ForeignKey
491 opts = model._meta
492 if fk_name:
493 fks_to_parent = [f for f in opts.fields if f.name == fk_name]
494 if len(fks_to_parent) == 1:
495 fk = fks_to_parent[0]
496 if not isinstance(fk, ForeignKey) or \
497 (fk.rel.to != parent_model and
498 fk.rel.to not in parent_model._meta.get_parent_list()):
499 raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
500 elif len(fks_to_parent) == 0:
501 raise Exception("%s has no field named '%s'" % (model, fk_name))
502 else:
503 # Try to discover what the ForeignKey from model to parent_model is
504 fks_to_parent = [
505 f for f in opts.fields
506 if isinstance(f, ForeignKey)
507 and (f.rel.to == parent_model
508 or f.rel.to in parent_model._meta.get_parent_list())
509 ]
510 if len(fks_to_parent) == 1:
511 fk = fks_to_parent[0]
512 elif len(fks_to_parent) == 0:
513 raise Exception("%s has no ForeignKey to %s" % (model, parent_model))
514 else:
515 raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model))
516 return fk
517
518
519def inlineformset_factory(parent_model, model, form=ModelForm,
520 formset=BaseInlineFormSet, fk_name=None,
521 fields=None, exclude=None,
522 extra=3, can_order=False, can_delete=True, max_num=0,
523 formfield_callback=lambda f: f.formfield()):
524 """
525 Returns an ``InlineFormSet`` for the given kwargs.
526
527 You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey``
528 to ``parent_model``.
529 """
530 fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
531 # enforce a max_num=1 when the foreign key to the parent model is unique.
532 if fk.unique:
533 max_num = 1
534 if exclude is not None:
535 exclude.append(fk.name)
536 else:
537 exclude = [fk.name]
538 kwargs = {
539 'form': form,
540 'formfield_callback': formfield_callback,
541 'formset': formset,
542 'extra': extra,
543 'can_delete': can_delete,
544 'can_order': can_order,
545 'fields': fields,
546 'exclude': exclude,
547 'max_num': max_num,
548 }
549 FormSet = modelformset_factory(model, **kwargs)
550 FormSet.fk = fk
551 return FormSet
552
553
554# Fields #####################################################################
555
556class ModelChoiceIterator(object):
557 def __init__(self, field):
558 self.field = field
559 self.queryset = field.queryset
560
561 def __iter__(self):
562 if self.field.empty_label is not None:
563 yield (u"", self.field.empty_label)
564 if self.field.cache_choices:
565 if self.field.choice_cache is None:
566 self.field.choice_cache = [
567 self.choice(obj) for obj in self.queryset.all()
568 ]
569 for choice in self.field.choice_cache:
570 yield choice
571 else:
572 for obj in self.queryset.all():
573 yield self.choice(obj)
574
575 def choice(self, obj):
576 if self.field.to_field_name:
577 # FIXME: The try..except shouldn't be necessary here. But this is
578 # going in just before 1.0, so I want to be careful. Will check it
579 # out later.
580 try:
581 key = getattr(obj, self.field.to_field_name).pk
582 except AttributeError:
583 key = getattr(obj, self.field.to_field_name)
584 else:
585 key = obj.pk
586 return (key, self.field.label_from_instance(obj))
587
588
589class ModelChoiceField(ChoiceField):
590 """A ChoiceField whose choices are a model QuerySet."""
591 # This class is a subclass of ChoiceField for purity, but it doesn't
592 # actually use any of ChoiceField's implementation.
593 default_error_messages = {
594 'invalid_choice': _(u'Select a valid choice. That choice is not one of'
595 u' the available choices.'),
596 }
597
598 def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
599 required=True, widget=None, label=None, initial=None,
600 help_text=None, to_field_name=None, *args, **kwargs):
601 self.empty_label = empty_label
602 self.cache_choices = cache_choices
603
604 # Call Field instead of ChoiceField __init__() because we don't need
605 # ChoiceField.__init__().
606 Field.__init__(self, required, widget, label, initial, help_text,
607 *args, **kwargs)
608 self.queryset = queryset
609 self.choice_cache = None
610 self.to_field_name = to_field_name
611
612 def _get_queryset(self):
613 return self._queryset
614
615 def _set_queryset(self, queryset):
616 self._queryset = queryset
617 self.widget.choices = self.choices
618
619 queryset = property(_get_queryset, _set_queryset)
620
621 # this method will be used to create object labels by the QuerySetIterator.
622 # Override it to customize the label.
623 def label_from_instance(self, obj):
624 """
625 This method is used to convert objects into strings; it's used to
626 generate the labels for the choices presented by this object. Subclasses
627 can override this method to customize the display of the choices.
628 """
629 return smart_unicode(obj)
630
631 def _get_choices(self):
632 # If self._choices is set, then somebody must have manually set
633 # the property self.choices. In this case, just return self._choices.
634 if hasattr(self, '_choices'):
635 return self._choices
636
637 # Otherwise, execute the QuerySet in self.queryset to determine the
638 # choices dynamically. Return a fresh QuerySetIterator that has not been
639 # consumed. Note that we're instantiating a new QuerySetIterator *each*
640 # time _get_choices() is called (and, thus, each time self.choices is
641 # accessed) so that we can ensure the QuerySet has not been consumed. This
642 # construct might look complicated but it allows for lazy evaluation of
643 # the queryset.
644 return ModelChoiceIterator(self)
645
646 choices = property(_get_choices, ChoiceField._set_choices)
647
648 def clean(self, value):
649 Field.clean(self, value)
650 if value in EMPTY_VALUES:
651 return None
652 try:
653 key = self.to_field_name or 'pk'
654 value = self.queryset.get(**{key: value})
655 except self.queryset.model.DoesNotExist:
656 raise ValidationError(self.error_messages['invalid_choice'])
657 return value
658
659class ModelMultipleChoiceField(ModelChoiceField):
660 """A MultipleChoiceField whose choices are a model QuerySet."""
661 widget = SelectMultiple
662 hidden_widget = MultipleHiddenInput
663 default_error_messages = {
664 'list': _(u'Enter a list of values.'),
665 'invalid_choice': _(u'Select a valid choice. %s is not one of the'
666 u' available choices.'),
667 }
668
669 def __init__(self, queryset, cache_choices=False, required=True,
670 widget=None, label=None, initial=None,
671 help_text=None, *args, **kwargs):
672 super(ModelMultipleChoiceField, self).__init__(queryset, None,
673 cache_choices, required, widget, label, initial, help_text,
674 *args, **kwargs)
675
676 def clean(self, value):
677 if self.required and not value:
678 raise ValidationError(self.error_messages['required'])
679 elif not self.required and not value:
680 return []
681 if not isinstance(value, (list, tuple)):
682 raise ValidationError(self.error_messages['list'])
683 final_values = []
684 for val in value:
685 try:
686 obj = self.queryset.get(pk=val)
687 except self.queryset.model.DoesNotExist:
688 raise ValidationError(self.error_messages['invalid_choice'] % val)
689 else:
690 final_values.append(obj)
691 return final_values
Back to Top