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