Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/forms/models.py: 16%
709 statements
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
1"""
2Helper functions for creating Form classes from Django models
3and database field objects.
4"""
5from itertools import chain
7from django.core.exceptions import (
8 NON_FIELD_ERRORS,
9 FieldError,
10 ImproperlyConfigured,
11 ValidationError,
12)
13from django.db.models.utils import AltersData
14from django.forms.fields import ChoiceField, Field
15from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass
16from django.forms.formsets import BaseFormSet, formset_factory
17from django.forms.utils import ErrorList
18from django.forms.widgets import (
19 HiddenInput,
20 MultipleHiddenInput,
21 RadioSelect,
22 SelectMultiple,
23)
24from django.utils.text import capfirst, get_text_list
25from django.utils.translation import gettext
26from django.utils.translation import gettext_lazy as _
28__all__ = (
29 "ModelForm",
30 "BaseModelForm",
31 "model_to_dict",
32 "fields_for_model",
33 "ModelChoiceField",
34 "ModelMultipleChoiceField",
35 "ALL_FIELDS",
36 "BaseModelFormSet",
37 "modelformset_factory",
38 "BaseInlineFormSet",
39 "inlineformset_factory",
40 "modelform_factory",
41)
43ALL_FIELDS = "__all__"
46def construct_instance(form, instance, fields=None, exclude=None):
47 """
48 Construct and return a model instance from the bound ``form``'s
49 ``cleaned_data``, but do not save the returned instance to the database.
50 """
51 from django.db import models
53 opts = instance._meta
55 cleaned_data = form.cleaned_data
56 file_field_list = []
57 for f in opts.fields:
58 if (
59 not f.editable
60 or isinstance(f, models.AutoField)
61 or f.name not in cleaned_data
62 ):
63 continue
64 if fields is not None and f.name not in fields:
65 continue
66 if exclude and f.name in exclude:
67 continue
68 # Leave defaults for fields that aren't in POST data, except for
69 # checkbox inputs because they don't appear in POST data if not checked.
70 if (
71 f.has_default()
72 and form[f.name].field.widget.value_omitted_from_data(
73 form.data, form.files, form.add_prefix(f.name)
74 )
75 and cleaned_data.get(f.name) in form[f.name].field.empty_values
76 ):
77 continue
78 # Defer saving file-type fields until after the other fields, so a
79 # callable upload_to can use the values from other fields.
80 if isinstance(f, models.FileField):
81 file_field_list.append(f)
82 else:
83 f.save_form_data(instance, cleaned_data[f.name])
85 for f in file_field_list:
86 f.save_form_data(instance, cleaned_data[f.name])
88 return instance
91# ModelForms #################################################################
94def model_to_dict(instance, fields=None, exclude=None):
95 """
96 Return a dict containing the data in ``instance`` suitable for passing as
97 a Form's ``initial`` keyword argument.
99 ``fields`` is an optional list of field names. If provided, return only the
100 named.
102 ``exclude`` is an optional list of field names. If provided, exclude the
103 named from the returned dict, even if they are listed in the ``fields``
104 argument.
105 """
106 opts = instance._meta
107 data = {}
108 for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
109 if not getattr(f, "editable", False):
110 continue
111 if fields is not None and f.name not in fields:
112 continue
113 if exclude and f.name in exclude:
114 continue
115 data[f.name] = f.value_from_object(instance)
116 return data
119def apply_limit_choices_to_to_formfield(formfield):
120 """Apply limit_choices_to to the formfield's queryset if needed."""
121 from django.db.models import Exists, OuterRef, Q
123 if hasattr(formfield, "queryset") and hasattr(formfield, "get_limit_choices_to"):
124 limit_choices_to = formfield.get_limit_choices_to()
125 if limit_choices_to:
126 complex_filter = limit_choices_to
127 if not isinstance(complex_filter, Q):
128 complex_filter = Q(**limit_choices_to)
129 complex_filter &= Q(pk=OuterRef("pk"))
130 # Use Exists() to avoid potential duplicates.
131 formfield.queryset = formfield.queryset.filter(
132 Exists(formfield.queryset.model._base_manager.filter(complex_filter)),
133 )
136def fields_for_model(
137 model,
138 fields=None,
139 exclude=None,
140 widgets=None,
141 formfield_callback=None,
142 localized_fields=None,
143 labels=None,
144 help_texts=None,
145 error_messages=None,
146 field_classes=None,
147 *,
148 apply_limit_choices_to=True,
149):
150 """
151 Return a dictionary containing form fields for the given model.
153 ``fields`` is an optional list of field names. If provided, return only the
154 named fields.
156 ``exclude`` is an optional list of field names. If provided, exclude the
157 named fields from the returned fields, even if they are listed in the
158 ``fields`` argument.
160 ``widgets`` is a dictionary of model field names mapped to a widget.
162 ``formfield_callback`` is a callable that takes a model field and returns
163 a form field.
165 ``localized_fields`` is a list of names of fields which should be localized.
167 ``labels`` is a dictionary of model field names mapped to a label.
169 ``help_texts`` is a dictionary of model field names mapped to a help text.
171 ``error_messages`` is a dictionary of model field names mapped to a
172 dictionary of error messages.
174 ``field_classes`` is a dictionary of model field names mapped to a form
175 field class.
177 ``apply_limit_choices_to`` is a boolean indicating if limit_choices_to
178 should be applied to a field's queryset.
179 """
180 field_dict = {}
181 ignored = []
182 opts = model._meta
183 # Avoid circular import
184 from django.db.models import Field as ModelField
186 sortable_private_fields = [
187 f for f in opts.private_fields if isinstance(f, ModelField)
188 ]
189 for f in sorted(
190 chain(opts.concrete_fields, sortable_private_fields, opts.many_to_many)
191 ):
192 if not getattr(f, "editable", False):
193 if (
194 fields is not None
195 and f.name in fields
196 and (exclude is None or f.name not in exclude)
197 ):
198 raise FieldError(
199 "'%s' cannot be specified for %s model form as it is a "
200 "non-editable field" % (f.name, model.__name__)
201 )
202 continue
203 if fields is not None and f.name not in fields:
204 continue
205 if exclude and f.name in exclude:
206 continue
208 kwargs = {}
209 if widgets and f.name in widgets:
210 kwargs["widget"] = widgets[f.name]
211 if localized_fields == ALL_FIELDS or (
212 localized_fields and f.name in localized_fields
213 ):
214 kwargs["localize"] = True
215 if labels and f.name in labels:
216 kwargs["label"] = labels[f.name]
217 if help_texts and f.name in help_texts:
218 kwargs["help_text"] = help_texts[f.name]
219 if error_messages and f.name in error_messages:
220 kwargs["error_messages"] = error_messages[f.name]
221 if field_classes and f.name in field_classes:
222 kwargs["form_class"] = field_classes[f.name]
224 if formfield_callback is None:
225 formfield = f.formfield(**kwargs)
226 elif not callable(formfield_callback):
227 raise TypeError("formfield_callback must be a function or callable")
228 else:
229 formfield = formfield_callback(f, **kwargs)
231 if formfield:
232 if apply_limit_choices_to:
233 apply_limit_choices_to_to_formfield(formfield)
234 field_dict[f.name] = formfield
235 else:
236 ignored.append(f.name)
237 if fields:
238 field_dict = {
239 f: field_dict.get(f)
240 for f in fields
241 if (not exclude or f not in exclude) and f not in ignored
242 }
243 return field_dict
246class ModelFormOptions:
247 def __init__(self, options=None):
248 self.model = getattr(options, "model", None)
249 self.fields = getattr(options, "fields", None)
250 self.exclude = getattr(options, "exclude", None)
251 self.widgets = getattr(options, "widgets", None)
252 self.localized_fields = getattr(options, "localized_fields", None)
253 self.labels = getattr(options, "labels", None)
254 self.help_texts = getattr(options, "help_texts", None)
255 self.error_messages = getattr(options, "error_messages", None)
256 self.field_classes = getattr(options, "field_classes", None)
257 self.formfield_callback = getattr(options, "formfield_callback", None)
260class ModelFormMetaclass(DeclarativeFieldsMetaclass):
261 def __new__(mcs, name, bases, attrs):
262 new_class = super().__new__(mcs, name, bases, attrs)
264 if bases == (BaseModelForm,):
265 return new_class
267 opts = new_class._meta = ModelFormOptions(getattr(new_class, "Meta", None))
269 # We check if a string was passed to `fields` or `exclude`,
270 # which is likely to be a mistake where the user typed ('foo') instead
271 # of ('foo',)
272 for opt in ["fields", "exclude", "localized_fields"]:
273 value = getattr(opts, opt)
274 if isinstance(value, str) and value != ALL_FIELDS:
275 msg = (
276 "%(model)s.Meta.%(opt)s cannot be a string. "
277 "Did you mean to type: ('%(value)s',)?"
278 % {
279 "model": new_class.__name__,
280 "opt": opt,
281 "value": value,
282 }
283 )
284 raise TypeError(msg)
286 if opts.model:
287 # If a model is defined, extract form fields from it.
288 if opts.fields is None and opts.exclude is None:
289 raise ImproperlyConfigured(
290 "Creating a ModelForm without either the 'fields' attribute "
291 "or the 'exclude' attribute is prohibited; form %s "
292 "needs updating." % name
293 )
295 if opts.fields == ALL_FIELDS:
296 # Sentinel for fields_for_model to indicate "get the list of
297 # fields from the model"
298 opts.fields = None
300 fields = fields_for_model(
301 opts.model,
302 opts.fields,
303 opts.exclude,
304 opts.widgets,
305 opts.formfield_callback,
306 opts.localized_fields,
307 opts.labels,
308 opts.help_texts,
309 opts.error_messages,
310 opts.field_classes,
311 # limit_choices_to will be applied during ModelForm.__init__().
312 apply_limit_choices_to=False,
313 )
315 # make sure opts.fields doesn't specify an invalid field
316 none_model_fields = {k for k, v in fields.items() if not v}
317 missing_fields = none_model_fields.difference(new_class.declared_fields)
318 if missing_fields:
319 message = "Unknown field(s) (%s) specified for %s"
320 message %= (", ".join(missing_fields), opts.model.__name__)
321 raise FieldError(message)
322 # Override default model fields with any custom declared ones
323 # (plus, include all the other declared fields).
324 fields.update(new_class.declared_fields)
325 else:
326 fields = new_class.declared_fields
328 new_class.base_fields = fields
330 return new_class
333class BaseModelForm(BaseForm, AltersData):
334 def __init__(
335 self,
336 data=None,
337 files=None,
338 auto_id="id_%s",
339 prefix=None,
340 initial=None,
341 error_class=ErrorList,
342 label_suffix=None,
343 empty_permitted=False,
344 instance=None,
345 use_required_attribute=None,
346 renderer=None,
347 ):
348 opts = self._meta
349 if opts.model is None:
350 raise ValueError("ModelForm has no model class specified.")
351 if instance is None:
352 # if we didn't get an instance, instantiate a new one
353 self.instance = opts.model()
354 object_data = {}
355 else:
356 self.instance = instance
357 object_data = model_to_dict(instance, opts.fields, opts.exclude)
358 # if initial was provided, it should override the values from instance
359 if initial is not None:
360 object_data.update(initial)
361 # self._validate_unique will be set to True by BaseModelForm.clean().
362 # It is False by default so overriding self.clean() and failing to call
363 # super will stop validate_unique from being called.
364 self._validate_unique = False
365 super().__init__(
366 data,
367 files,
368 auto_id,
369 prefix,
370 object_data,
371 error_class,
372 label_suffix,
373 empty_permitted,
374 use_required_attribute=use_required_attribute,
375 renderer=renderer,
376 )
377 for formfield in self.fields.values():
378 apply_limit_choices_to_to_formfield(formfield)
380 def _get_validation_exclusions(self):
381 """
382 For backwards-compatibility, exclude several types of fields from model
383 validation. See tickets #12507, #12521, #12553.
384 """
385 exclude = set()
386 # Build up a list of fields that should be excluded from model field
387 # validation and unique checks.
388 for f in self.instance._meta.fields:
389 field = f.name
390 # Exclude fields that aren't on the form. The developer may be
391 # adding these values to the model after form validation.
392 if field not in self.fields:
393 exclude.add(f.name)
395 # Don't perform model validation on fields that were defined
396 # manually on the form and excluded via the ModelForm's Meta
397 # class. See #12901.
398 elif self._meta.fields and field not in self._meta.fields:
399 exclude.add(f.name)
400 elif self._meta.exclude and field in self._meta.exclude:
401 exclude.add(f.name)
403 # Exclude fields that failed form validation. There's no need for
404 # the model fields to validate them as well.
405 elif field in self._errors:
406 exclude.add(f.name)
408 # Exclude empty fields that are not required by the form, if the
409 # underlying model field is required. This keeps the model field
410 # from raising a required error. Note: don't exclude the field from
411 # validation if the model field allows blanks. If it does, the blank
412 # value may be included in a unique check, so cannot be excluded
413 # from validation.
414 else:
415 form_field = self.fields[field]
416 field_value = self.cleaned_data.get(field)
417 if (
418 not f.blank
419 and not form_field.required
420 and field_value in form_field.empty_values
421 ):
422 exclude.add(f.name)
423 return exclude
425 def clean(self):
426 self._validate_unique = True
427 return self.cleaned_data
429 def _update_errors(self, errors):
430 # Override any validation error messages defined at the model level
431 # with those defined at the form level.
432 opts = self._meta
434 # Allow the model generated by construct_instance() to raise
435 # ValidationError and have them handled in the same way as others.
436 if hasattr(errors, "error_dict"):
437 error_dict = errors.error_dict
438 else:
439 error_dict = {NON_FIELD_ERRORS: errors}
441 for field, messages in error_dict.items():
442 if (
443 field == NON_FIELD_ERRORS
444 and opts.error_messages
445 and NON_FIELD_ERRORS in opts.error_messages
446 ):
447 error_messages = opts.error_messages[NON_FIELD_ERRORS]
448 elif field in self.fields:
449 error_messages = self.fields[field].error_messages
450 else:
451 continue
453 for message in messages:
454 if (
455 isinstance(message, ValidationError)
456 and message.code in error_messages
457 ):
458 message.message = error_messages[message.code]
460 self.add_error(None, errors)
462 def _post_clean(self):
463 opts = self._meta
465 exclude = self._get_validation_exclusions()
467 # Foreign Keys being used to represent inline relationships
468 # are excluded from basic field value validation. This is for two
469 # reasons: firstly, the value may not be supplied (#12507; the
470 # case of providing new values to the admin); secondly the
471 # object being referred to may not yet fully exist (#12749).
472 # However, these fields *must* be included in uniqueness checks,
473 # so this can't be part of _get_validation_exclusions().
474 for name, field in self.fields.items():
475 if isinstance(field, InlineForeignKeyField):
476 exclude.add(name)
478 try:
479 self.instance = construct_instance(
480 self, self.instance, opts.fields, opts.exclude
481 )
482 except ValidationError as e:
483 self._update_errors(e)
485 try:
486 self.instance.full_clean(exclude=exclude, validate_unique=False)
487 except ValidationError as e:
488 self._update_errors(e)
490 # Validate uniqueness if needed.
491 if self._validate_unique:
492 self.validate_unique()
494 def validate_unique(self):
495 """
496 Call the instance's validate_unique() method and update the form's
497 validation errors if any were raised.
498 """
499 exclude = self._get_validation_exclusions()
500 try:
501 self.instance.validate_unique(exclude=exclude)
502 except ValidationError as e:
503 self._update_errors(e)
505 def _save_m2m(self):
506 """
507 Save the many-to-many fields and generic relations for this form.
508 """
509 cleaned_data = self.cleaned_data
510 exclude = self._meta.exclude
511 fields = self._meta.fields
512 opts = self.instance._meta
513 # Note that for historical reasons we want to include also
514 # private_fields here. (GenericRelation was previously a fake
515 # m2m field).
516 for f in chain(opts.many_to_many, opts.private_fields):
517 if not hasattr(f, "save_form_data"):
518 continue
519 if fields and f.name not in fields:
520 continue
521 if exclude and f.name in exclude:
522 continue
523 if f.name in cleaned_data:
524 f.save_form_data(self.instance, cleaned_data[f.name])
526 def save(self, commit=True):
527 """
528 Save this form's self.instance object if commit=True. Otherwise, add
529 a save_m2m() method to the form which can be called after the instance
530 is saved manually at a later time. Return the model instance.
531 """
532 if self.errors:
533 raise ValueError(
534 "The %s could not be %s because the data didn't validate."
535 % (
536 self.instance._meta.object_name,
537 "created" if self.instance._state.adding else "changed",
538 )
539 )
540 if commit:
541 # If committing, save the instance and the m2m data immediately.
542 self.instance.save()
543 self._save_m2m()
544 else:
545 # If not committing, add a method to the form to allow deferred
546 # saving of m2m data.
547 self.save_m2m = self._save_m2m
548 return self.instance
550 save.alters_data = True
553class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass):
554 pass
557def modelform_factory(
558 model,
559 form=ModelForm,
560 fields=None,
561 exclude=None,
562 formfield_callback=None,
563 widgets=None,
564 localized_fields=None,
565 labels=None,
566 help_texts=None,
567 error_messages=None,
568 field_classes=None,
569):
570 """
571 Return a ModelForm containing form fields for the given model. You can
572 optionally pass a `form` argument to use as a starting point for
573 constructing the ModelForm.
575 ``fields`` is an optional list of field names. If provided, include only
576 the named fields in the returned fields. If omitted or '__all__', use all
577 fields.
579 ``exclude`` is an optional list of field names. If provided, exclude the
580 named fields from the returned fields, even if they are listed in the
581 ``fields`` argument.
583 ``widgets`` is a dictionary of model field names mapped to a widget.
585 ``localized_fields`` is a list of names of fields which should be localized.
587 ``formfield_callback`` is a callable that takes a model field and returns
588 a form field.
590 ``labels`` is a dictionary of model field names mapped to a label.
592 ``help_texts`` is a dictionary of model field names mapped to a help text.
594 ``error_messages`` is a dictionary of model field names mapped to a
595 dictionary of error messages.
597 ``field_classes`` is a dictionary of model field names mapped to a form
598 field class.
599 """
600 # Create the inner Meta class. FIXME: ideally, we should be able to
601 # construct a ModelForm without creating and passing in a temporary
602 # inner class.
604 # Build up a list of attributes that the Meta object will have.
605 attrs = {"model": model}
606 if fields is not None:
607 attrs["fields"] = fields
608 if exclude is not None:
609 attrs["exclude"] = exclude
610 if widgets is not None:
611 attrs["widgets"] = widgets
612 if localized_fields is not None:
613 attrs["localized_fields"] = localized_fields
614 if labels is not None:
615 attrs["labels"] = labels
616 if help_texts is not None:
617 attrs["help_texts"] = help_texts
618 if error_messages is not None:
619 attrs["error_messages"] = error_messages
620 if field_classes is not None:
621 attrs["field_classes"] = field_classes
623 # If parent form class already has an inner Meta, the Meta we're
624 # creating needs to inherit from the parent's inner meta.
625 bases = (form.Meta,) if hasattr(form, "Meta") else ()
626 Meta = type("Meta", bases, attrs)
627 if formfield_callback:
628 Meta.formfield_callback = staticmethod(formfield_callback)
629 # Give this new form class a reasonable name.
630 class_name = model.__name__ + "Form"
632 # Class attributes for the new form class.
633 form_class_attrs = {"Meta": Meta}
635 if getattr(Meta, "fields", None) is None and getattr(Meta, "exclude", None) is None:
636 raise ImproperlyConfigured(
637 "Calling modelform_factory without defining 'fields' or "
638 "'exclude' explicitly is prohibited."
639 )
641 # Instantiate type(form) in order to use the same metaclass as form.
642 return type(form)(class_name, (form,), form_class_attrs)
645# ModelFormSets ##############################################################
648class BaseModelFormSet(BaseFormSet, AltersData):
649 """
650 A ``FormSet`` for editing a queryset and/or adding new objects to it.
651 """
653 model = None
654 edit_only = False
656 # Set of fields that must be unique among forms of this set.
657 unique_fields = set()
659 def __init__(
660 self,
661 data=None,
662 files=None,
663 auto_id="id_%s",
664 prefix=None,
665 queryset=None,
666 *,
667 initial=None,
668 **kwargs,
669 ):
670 self.queryset = queryset
671 self.initial_extra = initial
672 super().__init__(
673 **{
674 "data": data,
675 "files": files,
676 "auto_id": auto_id,
677 "prefix": prefix,
678 **kwargs,
679 }
680 )
682 def initial_form_count(self):
683 """Return the number of forms that are required in this FormSet."""
684 if not self.is_bound:
685 return len(self.get_queryset())
686 return super().initial_form_count()
688 def _existing_object(self, pk):
689 if not hasattr(self, "_object_dict"):
690 self._object_dict = {o.pk: o for o in self.get_queryset()}
691 return self._object_dict.get(pk)
693 def _get_to_python(self, field):
694 """
695 If the field is a related field, fetch the concrete field's (that
696 is, the ultimate pointed-to field's) to_python.
697 """
698 while field.remote_field is not None:
699 field = field.remote_field.get_related_field()
700 return field.to_python
702 def _construct_form(self, i, **kwargs):
703 pk_required = i < self.initial_form_count()
704 if pk_required:
705 if self.is_bound:
706 pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
707 try:
708 pk = self.data[pk_key]
709 except KeyError:
710 # The primary key is missing. The user may have tampered
711 # with POST data.
712 pass
713 else:
714 to_python = self._get_to_python(self.model._meta.pk)
715 try:
716 pk = to_python(pk)
717 except ValidationError:
718 # The primary key exists but is an invalid value. The
719 # user may have tampered with POST data.
720 pass
721 else:
722 kwargs["instance"] = self._existing_object(pk)
723 else:
724 kwargs["instance"] = self.get_queryset()[i]
725 elif self.initial_extra:
726 # Set initial values for extra forms
727 try:
728 kwargs["initial"] = self.initial_extra[i - self.initial_form_count()]
729 except IndexError:
730 pass
731 form = super()._construct_form(i, **kwargs)
732 if pk_required:
733 form.fields[self.model._meta.pk.name].required = True
734 return form
736 def get_queryset(self):
737 if not hasattr(self, "_queryset"):
738 if self.queryset is not None:
739 qs = self.queryset
740 else:
741 qs = self.model._default_manager.get_queryset()
743 # If the queryset isn't already ordered we need to add an
744 # artificial ordering here to make sure that all formsets
745 # constructed from this queryset have the same form order.
746 if not qs.ordered:
747 qs = qs.order_by(self.model._meta.pk.name)
749 # Removed queryset limiting here. As per discussion re: #13023
750 # on django-dev, max_num should not prevent existing
751 # related objects/inlines from being displayed.
752 self._queryset = qs
753 return self._queryset
755 def save_new(self, form, commit=True):
756 """Save and return a new model instance for the given form."""
757 return form.save(commit=commit)
759 def save_existing(self, form, instance, commit=True):
760 """Save and return an existing model instance for the given form."""
761 return form.save(commit=commit)
763 def delete_existing(self, obj, commit=True):
764 """Deletes an existing model instance."""
765 if commit:
766 obj.delete()
768 def save(self, commit=True):
769 """
770 Save model instances for every form, adding and changing instances
771 as necessary, and return the list of instances.
772 """
773 if not commit:
774 self.saved_forms = []
776 def save_m2m():
777 for form in self.saved_forms:
778 form.save_m2m()
780 self.save_m2m = save_m2m
781 if self.edit_only:
782 return self.save_existing_objects(commit)
783 else:
784 return self.save_existing_objects(commit) + self.save_new_objects(commit)
786 save.alters_data = True
788 def clean(self):
789 self.validate_unique()
791 def validate_unique(self):
792 # Collect unique_checks and date_checks to run from all the forms.
793 all_unique_checks = set()
794 all_date_checks = set()
795 forms_to_delete = self.deleted_forms
796 valid_forms = [
797 form
798 for form in self.forms
799 if form.is_valid() and form not in forms_to_delete
800 ]
801 for form in valid_forms:
802 exclude = form._get_validation_exclusions()
803 unique_checks, date_checks = form.instance._get_unique_checks(
804 exclude=exclude,
805 include_meta_constraints=True,
806 )
807 all_unique_checks.update(unique_checks)
808 all_date_checks.update(date_checks)
810 errors = []
811 # Do each of the unique checks (unique and unique_together)
812 for uclass, unique_check in all_unique_checks:
813 seen_data = set()
814 for form in valid_forms:
815 # Get the data for the set of fields that must be unique among
816 # the forms.
817 row_data = (
818 field if field in self.unique_fields else form.cleaned_data[field]
819 for field in unique_check
820 if field in form.cleaned_data
821 )
822 # Reduce Model instances to their primary key values
823 row_data = tuple(
824 d._get_pk_val() if hasattr(d, "_get_pk_val")
825 # Prevent "unhashable type: list" errors later on.
826 else tuple(d) if isinstance(d, list) else d
827 for d in row_data
828 )
829 if row_data and None not in row_data:
830 # if we've already seen it then we have a uniqueness failure
831 if row_data in seen_data:
832 # poke error messages into the right places and mark
833 # the form as invalid
834 errors.append(self.get_unique_error_message(unique_check))
835 form._errors[NON_FIELD_ERRORS] = self.error_class(
836 [self.get_form_error()],
837 renderer=self.renderer,
838 )
839 # Remove the data from the cleaned_data dict since it
840 # was invalid.
841 for field in unique_check:
842 if field in form.cleaned_data:
843 del form.cleaned_data[field]
844 # mark the data as seen
845 seen_data.add(row_data)
846 # iterate over each of the date checks now
847 for date_check in all_date_checks:
848 seen_data = set()
849 uclass, lookup, field, unique_for = date_check
850 for form in valid_forms:
851 # see if we have data for both fields
852 if (
853 form.cleaned_data
854 and form.cleaned_data[field] is not None
855 and form.cleaned_data[unique_for] is not None
856 ):
857 # if it's a date lookup we need to get the data for all the fields
858 if lookup == "date":
859 date = form.cleaned_data[unique_for]
860 date_data = (date.year, date.month, date.day)
861 # otherwise it's just the attribute on the date/datetime
862 # object
863 else:
864 date_data = (getattr(form.cleaned_data[unique_for], lookup),)
865 data = (form.cleaned_data[field],) + date_data
866 # if we've already seen it then we have a uniqueness failure
867 if data in seen_data:
868 # poke error messages into the right places and mark
869 # the form as invalid
870 errors.append(self.get_date_error_message(date_check))
871 form._errors[NON_FIELD_ERRORS] = self.error_class(
872 [self.get_form_error()],
873 renderer=self.renderer,
874 )
875 # Remove the data from the cleaned_data dict since it
876 # was invalid.
877 del form.cleaned_data[field]
878 # mark the data as seen
879 seen_data.add(data)
881 if errors:
882 raise ValidationError(errors)
884 def get_unique_error_message(self, unique_check):
885 if len(unique_check) == 1:
886 return gettext("Please correct the duplicate data for %(field)s.") % {
887 "field": unique_check[0],
888 }
889 else:
890 return gettext(
891 "Please correct the duplicate data for %(field)s, which must be unique."
892 ) % {
893 "field": get_text_list(unique_check, _("and")),
894 }
896 def get_date_error_message(self, date_check):
897 return gettext(
898 "Please correct the duplicate data for %(field_name)s "
899 "which must be unique for the %(lookup)s in %(date_field)s."
900 ) % {
901 "field_name": date_check[2],
902 "date_field": date_check[3],
903 "lookup": str(date_check[1]),
904 }
906 def get_form_error(self):
907 return gettext("Please correct the duplicate values below.")
909 def save_existing_objects(self, commit=True):
910 self.changed_objects = []
911 self.deleted_objects = []
912 if not self.initial_forms:
913 return []
915 saved_instances = []
916 forms_to_delete = self.deleted_forms
917 for form in self.initial_forms:
918 obj = form.instance
919 # If the pk is None, it means either:
920 # 1. The object is an unexpected empty model, created by invalid
921 # POST data such as an object outside the formset's queryset.
922 # 2. The object was already deleted from the database.
923 if obj.pk is None:
924 continue
925 if form in forms_to_delete:
926 self.deleted_objects.append(obj)
927 self.delete_existing(obj, commit=commit)
928 elif form.has_changed():
929 self.changed_objects.append((obj, form.changed_data))
930 saved_instances.append(self.save_existing(form, obj, commit=commit))
931 if not commit:
932 self.saved_forms.append(form)
933 return saved_instances
935 def save_new_objects(self, commit=True):
936 self.new_objects = []
937 for form in self.extra_forms:
938 if not form.has_changed():
939 continue
940 # If someone has marked an add form for deletion, don't save the
941 # object.
942 if self.can_delete and self._should_delete_form(form):
943 continue
944 self.new_objects.append(self.save_new(form, commit=commit))
945 if not commit:
946 self.saved_forms.append(form)
947 return self.new_objects
949 def add_fields(self, form, index):
950 """Add a hidden field for the object's primary key."""
951 from django.db.models import AutoField, ForeignKey, OneToOneField
953 self._pk_field = pk = self.model._meta.pk
954 # If a pk isn't editable, then it won't be on the form, so we need to
955 # add it here so we can tell which object is which when we get the
956 # data back. Generally, pk.editable should be false, but for some
957 # reason, auto_created pk fields and AutoField's editable attribute is
958 # True, so check for that as well.
960 def pk_is_not_editable(pk):
961 return (
962 (not pk.editable)
963 or (pk.auto_created or isinstance(pk, AutoField))
964 or (
965 pk.remote_field
966 and pk.remote_field.parent_link
967 and pk_is_not_editable(pk.remote_field.model._meta.pk)
968 )
969 )
971 if pk_is_not_editable(pk) or pk.name not in form.fields:
972 if form.is_bound:
973 # If we're adding the related instance, ignore its primary key
974 # as it could be an auto-generated default which isn't actually
975 # in the database.
976 pk_value = None if form.instance._state.adding else form.instance.pk
977 else:
978 try:
979 if index is not None:
980 pk_value = self.get_queryset()[index].pk
981 else:
982 pk_value = None
983 except IndexError:
984 pk_value = None
985 if isinstance(pk, (ForeignKey, OneToOneField)):
986 qs = pk.remote_field.model._default_manager.get_queryset()
987 else:
988 qs = self.model._default_manager.get_queryset()
989 qs = qs.using(form.instance._state.db)
990 if form._meta.widgets:
991 widget = form._meta.widgets.get(self._pk_field.name, HiddenInput)
992 else:
993 widget = HiddenInput
994 form.fields[self._pk_field.name] = ModelChoiceField(
995 qs, initial=pk_value, required=False, widget=widget
996 )
997 super().add_fields(form, index)
1000def modelformset_factory(
1001 model,
1002 form=ModelForm,
1003 formfield_callback=None,
1004 formset=BaseModelFormSet,
1005 extra=1,
1006 can_delete=False,
1007 can_order=False,
1008 max_num=None,
1009 fields=None,
1010 exclude=None,
1011 widgets=None,
1012 validate_max=False,
1013 localized_fields=None,
1014 labels=None,
1015 help_texts=None,
1016 error_messages=None,
1017 min_num=None,
1018 validate_min=False,
1019 field_classes=None,
1020 absolute_max=None,
1021 can_delete_extra=True,
1022 renderer=None,
1023 edit_only=False,
1024):
1025 """Return a FormSet class for the given Django model class."""
1026 meta = getattr(form, "Meta", None)
1027 if (
1028 getattr(meta, "fields", fields) is None
1029 and getattr(meta, "exclude", exclude) is None
1030 ):
1031 raise ImproperlyConfigured(
1032 "Calling modelformset_factory without defining 'fields' or "
1033 "'exclude' explicitly is prohibited."
1034 )
1036 form = modelform_factory(
1037 model,
1038 form=form,
1039 fields=fields,
1040 exclude=exclude,
1041 formfield_callback=formfield_callback,
1042 widgets=widgets,
1043 localized_fields=localized_fields,
1044 labels=labels,
1045 help_texts=help_texts,
1046 error_messages=error_messages,
1047 field_classes=field_classes,
1048 )
1049 FormSet = formset_factory(
1050 form,
1051 formset,
1052 extra=extra,
1053 min_num=min_num,
1054 max_num=max_num,
1055 can_order=can_order,
1056 can_delete=can_delete,
1057 validate_min=validate_min,
1058 validate_max=validate_max,
1059 absolute_max=absolute_max,
1060 can_delete_extra=can_delete_extra,
1061 renderer=renderer,
1062 )
1063 FormSet.model = model
1064 FormSet.edit_only = edit_only
1065 return FormSet
1068# InlineFormSets #############################################################
1071class BaseInlineFormSet(BaseModelFormSet):
1072 """A formset for child objects related to a parent."""
1074 def __init__(
1075 self,
1076 data=None,
1077 files=None,
1078 instance=None,
1079 save_as_new=False,
1080 prefix=None,
1081 queryset=None,
1082 **kwargs,
1083 ):
1084 if instance is None:
1085 self.instance = self.fk.remote_field.model()
1086 else:
1087 self.instance = instance
1088 self.save_as_new = save_as_new
1089 if queryset is None:
1090 queryset = self.model._default_manager
1091 if self.instance.pk is not None:
1092 qs = queryset.filter(**{self.fk.name: self.instance})
1093 else:
1094 qs = queryset.none()
1095 self.unique_fields = {self.fk.name}
1096 super().__init__(data, files, prefix=prefix, queryset=qs, **kwargs)
1098 # Add the generated field to form._meta.fields if it's defined to make
1099 # sure validation isn't skipped on that field.
1100 if self.form._meta.fields and self.fk.name not in self.form._meta.fields:
1101 if isinstance(self.form._meta.fields, tuple):
1102 self.form._meta.fields = list(self.form._meta.fields)
1103 self.form._meta.fields.append(self.fk.name)
1105 def initial_form_count(self):
1106 if self.save_as_new:
1107 return 0
1108 return super().initial_form_count()
1110 def _construct_form(self, i, **kwargs):
1111 form = super()._construct_form(i, **kwargs)
1112 if self.save_as_new:
1113 mutable = getattr(form.data, "_mutable", None)
1114 # Allow modifying an immutable QueryDict.
1115 if mutable is not None:
1116 form.data._mutable = True
1117 # Remove the primary key from the form's data, we are only
1118 # creating new instances
1119 form.data[form.add_prefix(self._pk_field.name)] = None
1120 # Remove the foreign key from the form's data
1121 form.data[form.add_prefix(self.fk.name)] = None
1122 if mutable is not None:
1123 form.data._mutable = mutable
1125 # Set the fk value here so that the form can do its validation.
1126 fk_value = self.instance.pk
1127 if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
1128 fk_value = getattr(self.instance, self.fk.remote_field.field_name)
1129 fk_value = getattr(fk_value, "pk", fk_value)
1130 setattr(form.instance, self.fk.get_attname(), fk_value)
1131 return form
1133 @classmethod
1134 def get_default_prefix(cls):
1135 return cls.fk.remote_field.get_accessor_name(model=cls.model).replace("+", "")
1137 def save_new(self, form, commit=True):
1138 # Ensure the latest copy of the related instance is present on each
1139 # form (it may have been saved after the formset was originally
1140 # instantiated).
1141 setattr(form.instance, self.fk.name, self.instance)
1142 return super().save_new(form, commit=commit)
1144 def add_fields(self, form, index):
1145 super().add_fields(form, index)
1146 if self._pk_field == self.fk:
1147 name = self._pk_field.name
1148 kwargs = {"pk_field": True}
1149 else:
1150 # The foreign key field might not be on the form, so we poke at the
1151 # Model field to get the label, since we need that for error messages.
1152 name = self.fk.name
1153 kwargs = {
1154 "label": getattr(
1155 form.fields.get(name), "label", capfirst(self.fk.verbose_name)
1156 )
1157 }
1159 # The InlineForeignKeyField assumes that the foreign key relation is
1160 # based on the parent model's pk. If this isn't the case, set to_field
1161 # to correctly resolve the initial form value.
1162 if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
1163 kwargs["to_field"] = self.fk.remote_field.field_name
1165 # If we're adding a new object, ignore a parent's auto-generated key
1166 # as it will be regenerated on the save request.
1167 if self.instance._state.adding:
1168 if kwargs.get("to_field") is not None:
1169 to_field = self.instance._meta.get_field(kwargs["to_field"])
1170 else:
1171 to_field = self.instance._meta.pk
1172 if to_field.has_default():
1173 setattr(self.instance, to_field.attname, None)
1175 form.fields[name] = InlineForeignKeyField(self.instance, **kwargs)
1177 def get_unique_error_message(self, unique_check):
1178 unique_check = [field for field in unique_check if field != self.fk.name]
1179 return super().get_unique_error_message(unique_check)
1182def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
1183 """
1184 Find and return the ForeignKey from model to parent if there is one
1185 (return None if can_fail is True and no such field exists). If fk_name is
1186 provided, assume it is the name of the ForeignKey field. Unless can_fail is
1187 True, raise an exception if there isn't a ForeignKey from model to
1188 parent_model.
1189 """
1190 # avoid circular import
1191 from django.db.models import ForeignKey
1193 opts = model._meta
1194 if fk_name:
1195 fks_to_parent = [f for f in opts.fields if f.name == fk_name]
1196 if len(fks_to_parent) == 1:
1197 fk = fks_to_parent[0]
1198 parent_list = parent_model._meta.get_parent_list()
1199 if (
1200 not isinstance(fk, ForeignKey)
1201 or (
1202 # ForeignKey to proxy models.
1203 fk.remote_field.model._meta.proxy
1204 and fk.remote_field.model._meta.proxy_for_model not in parent_list
1205 )
1206 or (
1207 # ForeignKey to concrete models.
1208 not fk.remote_field.model._meta.proxy
1209 and fk.remote_field.model != parent_model
1210 and fk.remote_field.model not in parent_list
1211 )
1212 ):
1213 raise ValueError(
1214 "fk_name '%s' is not a ForeignKey to '%s'."
1215 % (fk_name, parent_model._meta.label)
1216 )
1217 elif not fks_to_parent:
1218 raise ValueError(
1219 "'%s' has no field named '%s'." % (model._meta.label, fk_name)
1220 )
1221 else:
1222 # Try to discover what the ForeignKey from model to parent_model is
1223 parent_list = parent_model._meta.get_parent_list()
1224 fks_to_parent = [
1225 f
1226 for f in opts.fields
1227 if isinstance(f, ForeignKey)
1228 and (
1229 f.remote_field.model == parent_model
1230 or f.remote_field.model in parent_list
1231 or (
1232 f.remote_field.model._meta.proxy
1233 and f.remote_field.model._meta.proxy_for_model in parent_list
1234 )
1235 )
1236 ]
1237 if len(fks_to_parent) == 1:
1238 fk = fks_to_parent[0]
1239 elif not fks_to_parent:
1240 if can_fail:
1241 return
1242 raise ValueError(
1243 "'%s' has no ForeignKey to '%s'."
1244 % (
1245 model._meta.label,
1246 parent_model._meta.label,
1247 )
1248 )
1249 else:
1250 raise ValueError(
1251 "'%s' has more than one ForeignKey to '%s'. You must specify "
1252 "a 'fk_name' attribute."
1253 % (
1254 model._meta.label,
1255 parent_model._meta.label,
1256 )
1257 )
1258 return fk
1261def inlineformset_factory(
1262 parent_model,
1263 model,
1264 form=ModelForm,
1265 formset=BaseInlineFormSet,
1266 fk_name=None,
1267 fields=None,
1268 exclude=None,
1269 extra=3,
1270 can_order=False,
1271 can_delete=True,
1272 max_num=None,
1273 formfield_callback=None,
1274 widgets=None,
1275 validate_max=False,
1276 localized_fields=None,
1277 labels=None,
1278 help_texts=None,
1279 error_messages=None,
1280 min_num=None,
1281 validate_min=False,
1282 field_classes=None,
1283 absolute_max=None,
1284 can_delete_extra=True,
1285 renderer=None,
1286 edit_only=False,
1287):
1288 """
1289 Return an ``InlineFormSet`` for the given kwargs.
1291 ``fk_name`` must be provided if ``model`` has more than one ``ForeignKey``
1292 to ``parent_model``.
1293 """
1294 fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
1295 # enforce a max_num=1 when the foreign key to the parent model is unique.
1296 if fk.unique:
1297 max_num = 1
1298 kwargs = {
1299 "form": form,
1300 "formfield_callback": formfield_callback,
1301 "formset": formset,
1302 "extra": extra,
1303 "can_delete": can_delete,
1304 "can_order": can_order,
1305 "fields": fields,
1306 "exclude": exclude,
1307 "min_num": min_num,
1308 "max_num": max_num,
1309 "widgets": widgets,
1310 "validate_min": validate_min,
1311 "validate_max": validate_max,
1312 "localized_fields": localized_fields,
1313 "labels": labels,
1314 "help_texts": help_texts,
1315 "error_messages": error_messages,
1316 "field_classes": field_classes,
1317 "absolute_max": absolute_max,
1318 "can_delete_extra": can_delete_extra,
1319 "renderer": renderer,
1320 "edit_only": edit_only,
1321 }
1322 FormSet = modelformset_factory(model, **kwargs)
1323 FormSet.fk = fk
1324 return FormSet
1327# Fields #####################################################################
1330class InlineForeignKeyField(Field):
1331 """
1332 A basic integer field that deals with validating the given value to a
1333 given parent instance in an inline.
1334 """
1336 widget = HiddenInput
1337 default_error_messages = {
1338 "invalid_choice": _("The inline value did not match the parent instance."),
1339 }
1341 def __init__(self, parent_instance, *args, pk_field=False, to_field=None, **kwargs):
1342 self.parent_instance = parent_instance
1343 self.pk_field = pk_field
1344 self.to_field = to_field
1345 if self.parent_instance is not None:
1346 if self.to_field:
1347 kwargs["initial"] = getattr(self.parent_instance, self.to_field)
1348 else:
1349 kwargs["initial"] = self.parent_instance.pk
1350 kwargs["required"] = False
1351 super().__init__(*args, **kwargs)
1353 def clean(self, value):
1354 if value in self.empty_values:
1355 if self.pk_field:
1356 return None
1357 # if there is no value act as we did before.
1358 return self.parent_instance
1359 # ensure the we compare the values as equal types.
1360 if self.to_field:
1361 orig = getattr(self.parent_instance, self.to_field)
1362 else:
1363 orig = self.parent_instance.pk
1364 if str(value) != str(orig):
1365 raise ValidationError(
1366 self.error_messages["invalid_choice"], code="invalid_choice"
1367 )
1368 return self.parent_instance
1370 def has_changed(self, initial, data):
1371 return False
1374class ModelChoiceIteratorValue:
1375 def __init__(self, value, instance):
1376 self.value = value
1377 self.instance = instance
1379 def __str__(self):
1380 return str(self.value)
1382 def __hash__(self):
1383 return hash(self.value)
1385 def __eq__(self, other):
1386 if isinstance(other, ModelChoiceIteratorValue):
1387 other = other.value
1388 return self.value == other
1391class ModelChoiceIterator:
1392 def __init__(self, field):
1393 self.field = field
1394 self.queryset = field.queryset
1396 def __iter__(self):
1397 if self.field.empty_label is not None:
1398 yield ("", self.field.empty_label)
1399 queryset = self.queryset
1400 # Can't use iterator() when queryset uses prefetch_related()
1401 if not queryset._prefetch_related_lookups:
1402 queryset = queryset.iterator()
1403 for obj in queryset:
1404 yield self.choice(obj)
1406 def __len__(self):
1407 # count() adds a query but uses less memory since the QuerySet results
1408 # won't be cached. In most cases, the choices will only be iterated on,
1409 # and __len__() won't be called.
1410 return self.queryset.count() + (1 if self.field.empty_label is not None else 0)
1412 def __bool__(self):
1413 return self.field.empty_label is not None or self.queryset.exists()
1415 def choice(self, obj):
1416 return (
1417 ModelChoiceIteratorValue(self.field.prepare_value(obj), obj),
1418 self.field.label_from_instance(obj),
1419 )
1422class ModelChoiceField(ChoiceField):
1423 """A ChoiceField whose choices are a model QuerySet."""
1425 # This class is a subclass of ChoiceField for purity, but it doesn't
1426 # actually use any of ChoiceField's implementation.
1427 default_error_messages = {
1428 "invalid_choice": _(
1429 "Select a valid choice. That choice is not one of the available choices."
1430 ),
1431 }
1432 iterator = ModelChoiceIterator
1434 def __init__(
1435 self,
1436 queryset,
1437 *,
1438 empty_label="---------",
1439 required=True,
1440 widget=None,
1441 label=None,
1442 initial=None,
1443 help_text="",
1444 to_field_name=None,
1445 limit_choices_to=None,
1446 blank=False,
1447 **kwargs,
1448 ):
1449 # Call Field instead of ChoiceField __init__() because we don't need
1450 # ChoiceField.__init__().
1451 Field.__init__(
1452 self,
1453 required=required,
1454 widget=widget,
1455 label=label,
1456 initial=initial,
1457 help_text=help_text,
1458 **kwargs,
1459 )
1460 if (required and initial is not None) or (
1461 isinstance(self.widget, RadioSelect) and not blank
1462 ):
1463 self.empty_label = None
1464 else:
1465 self.empty_label = empty_label
1466 self.queryset = queryset
1467 self.limit_choices_to = limit_choices_to # limit the queryset later.
1468 self.to_field_name = to_field_name
1470 def get_limit_choices_to(self):
1471 """
1472 Return ``limit_choices_to`` for this form field.
1474 If it is a callable, invoke it and return the result.
1475 """
1476 if callable(self.limit_choices_to):
1477 return self.limit_choices_to()
1478 return self.limit_choices_to
1480 def __deepcopy__(self, memo):
1481 result = super(ChoiceField, self).__deepcopy__(memo)
1482 # Need to force a new ModelChoiceIterator to be created, bug #11183
1483 if self.queryset is not None:
1484 result.queryset = self.queryset.all()
1485 return result
1487 def _get_queryset(self):
1488 return self._queryset
1490 def _set_queryset(self, queryset):
1491 self._queryset = None if queryset is None else queryset.all()
1492 self.widget.choices = self.choices
1494 queryset = property(_get_queryset, _set_queryset)
1496 # this method will be used to create object labels by the QuerySetIterator.
1497 # Override it to customize the label.
1498 def label_from_instance(self, obj):
1499 """
1500 Convert objects into strings and generate the labels for the choices
1501 presented by this object. Subclasses can override this method to
1502 customize the display of the choices.
1503 """
1504 return str(obj)
1506 def _get_choices(self):
1507 # If self._choices is set, then somebody must have manually set
1508 # the property self.choices. In this case, just return self._choices.
1509 if hasattr(self, "_choices"):
1510 return self._choices
1512 # Otherwise, execute the QuerySet in self.queryset to determine the
1513 # choices dynamically. Return a fresh ModelChoiceIterator that has not been
1514 # consumed. Note that we're instantiating a new ModelChoiceIterator *each*
1515 # time _get_choices() is called (and, thus, each time self.choices is
1516 # accessed) so that we can ensure the QuerySet has not been consumed. This
1517 # construct might look complicated but it allows for lazy evaluation of
1518 # the queryset.
1519 return self.iterator(self)
1521 choices = property(_get_choices, ChoiceField._set_choices)
1523 def prepare_value(self, value):
1524 if hasattr(value, "_meta"):
1525 if self.to_field_name:
1526 return value.serializable_value(self.to_field_name)
1527 else:
1528 return value.pk
1529 return super().prepare_value(value)
1531 def to_python(self, value):
1532 if value in self.empty_values:
1533 return None
1534 try:
1535 key = self.to_field_name or "pk"
1536 if isinstance(value, self.queryset.model):
1537 value = getattr(value, key)
1538 value = self.queryset.get(**{key: value})
1539 except (ValueError, TypeError, self.queryset.model.DoesNotExist):
1540 raise ValidationError(
1541 self.error_messages["invalid_choice"],
1542 code="invalid_choice",
1543 params={"value": value},
1544 )
1545 return value
1547 def validate(self, value):
1548 return Field.validate(self, value)
1550 def has_changed(self, initial, data):
1551 if self.disabled:
1552 return False
1553 initial_value = initial if initial is not None else ""
1554 data_value = data if data is not None else ""
1555 return str(self.prepare_value(initial_value)) != str(data_value)
1558class ModelMultipleChoiceField(ModelChoiceField):
1559 """A MultipleChoiceField whose choices are a model QuerySet."""
1561 widget = SelectMultiple
1562 hidden_widget = MultipleHiddenInput
1563 default_error_messages = {
1564 "invalid_list": _("Enter a list of values."),
1565 "invalid_choice": _(
1566 "Select a valid choice. %(value)s is not one of the available choices."
1567 ),
1568 "invalid_pk_value": _("“%(pk)s” is not a valid value."),
1569 }
1571 def __init__(self, queryset, **kwargs):
1572 super().__init__(queryset, empty_label=None, **kwargs)
1574 def to_python(self, value):
1575 if not value:
1576 return []
1577 return list(self._check_values(value))
1579 def clean(self, value):
1580 value = self.prepare_value(value)
1581 if self.required and not value:
1582 raise ValidationError(self.error_messages["required"], code="required")
1583 elif not self.required and not value:
1584 return self.queryset.none()
1585 if not isinstance(value, (list, tuple)):
1586 raise ValidationError(
1587 self.error_messages["invalid_list"],
1588 code="invalid_list",
1589 )
1590 qs = self._check_values(value)
1591 # Since this overrides the inherited ModelChoiceField.clean
1592 # we run custom validators here
1593 self.run_validators(value)
1594 return qs
1596 def _check_values(self, value):
1597 """
1598 Given a list of possible PK values, return a QuerySet of the
1599 corresponding objects. Raise a ValidationError if a given value is
1600 invalid (not a valid PK, not in the queryset, etc.)
1601 """
1602 key = self.to_field_name or "pk"
1603 # deduplicate given values to avoid creating many querysets or
1604 # requiring the database backend deduplicate efficiently.
1605 try:
1606 value = frozenset(value)
1607 except TypeError:
1608 # list of lists isn't hashable, for example
1609 raise ValidationError(
1610 self.error_messages["invalid_list"],
1611 code="invalid_list",
1612 )
1613 for pk in value:
1614 try:
1615 self.queryset.filter(**{key: pk})
1616 except (ValueError, TypeError):
1617 raise ValidationError(
1618 self.error_messages["invalid_pk_value"],
1619 code="invalid_pk_value",
1620 params={"pk": pk},
1621 )
1622 qs = self.queryset.filter(**{"%s__in" % key: value})
1623 pks = {str(getattr(o, key)) for o in qs}
1624 for val in value:
1625 if str(val) not in pks:
1626 raise ValidationError(
1627 self.error_messages["invalid_choice"],
1628 code="invalid_choice",
1629 params={"value": val},
1630 )
1631 return qs
1633 def prepare_value(self, value):
1634 if (
1635 hasattr(value, "__iter__")
1636 and not isinstance(value, str)
1637 and not hasattr(value, "_meta")
1638 ):
1639 prepare_value = super().prepare_value
1640 return [prepare_value(v) for v in value]
1641 return super().prepare_value(value)
1643 def has_changed(self, initial, data):
1644 if self.disabled:
1645 return False
1646 if initial is None:
1647 initial = []
1648 if data is None:
1649 data = []
1650 if len(initial) != len(data):
1651 return True
1652 initial_set = {str(value) for value in self.prepare_value(initial)}
1653 data_set = {str(value) for value in data}
1654 return data_set != initial_set
1657def modelform_defines_fields(form_class):
1658 return hasattr(form_class, "_meta") and (
1659 form_class._meta.fields is not None or form_class._meta.exclude is not None
1660 )