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

1""" 

2Helper functions for creating Form classes from Django models 

3and database field objects. 

4""" 

5from itertools import chain 

6 

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 _ 

26 

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) 

41 

42ALL_FIELDS = "__all__" 

43 

44 

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 

51 

52 opts = instance._meta 

53 

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]) 

83 

84 for f in file_field_list: 

85 f.save_form_data(instance, cleaned_data[f.name]) 

86 

87 return instance 

88 

89 

90# ModelForms ################################################################# 

91 

92 

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. 

97 

98 ``fields`` is an optional list of field names. If provided, return only the 

99 named. 

100 

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 

116 

117 

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 

121 

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 ) 

133 

134 

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. 

151 

152 ``fields`` is an optional list of field names. If provided, return only the 

153 named fields. 

154 

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. 

158 

159 ``widgets`` is a dictionary of model field names mapped to a widget. 

160 

161 ``formfield_callback`` is a callable that takes a model field and returns 

162 a form field. 

163 

164 ``localized_fields`` is a list of names of fields which should be localized. 

165 

166 ``labels`` is a dictionary of model field names mapped to a label. 

167 

168 ``help_texts`` is a dictionary of model field names mapped to a help text. 

169 

170 ``error_messages`` is a dictionary of model field names mapped to a 

171 dictionary of error messages. 

172 

173 ``field_classes`` is a dictionary of model field names mapped to a form 

174 field class. 

175 

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 

184 

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 

206 

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] 

222 

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) 

229 

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 

243 

244 

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) 

256 

257 

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 

265 

266 formfield_callback = attrs.pop("formfield_callback", base_formfield_callback) 

267 

268 new_class = super().__new__(mcs, name, bases, attrs) 

269 

270 if bases == (BaseModelForm,): 

271 return new_class 

272 

273 opts = new_class._meta = ModelFormOptions(getattr(new_class, "Meta", None)) 

274 

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) 

291 

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 ) 

300 

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 

305 

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 ) 

320 

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 

333 

334 new_class.base_fields = fields 

335 

336 return new_class 

337 

338 

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) 

385 

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) 

400 

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) 

408 

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) 

413 

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 

430 

431 def clean(self): 

432 self._validate_unique = True 

433 return self.cleaned_data 

434 

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 

439 

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} 

446 

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 

458 

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] 

465 

466 self.add_error(None, errors) 

467 

468 def _post_clean(self): 

469 opts = self._meta 

470 

471 exclude = self._get_validation_exclusions() 

472 

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) 

483 

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) 

490 

491 try: 

492 self.instance.full_clean(exclude=exclude, validate_unique=False) 

493 except ValidationError as e: 

494 self._update_errors(e) 

495 

496 # Validate uniqueness if needed. 

497 if self._validate_unique: 

498 self.validate_unique() 

499 

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) 

510 

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]) 

531 

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 

555 

556 save.alters_data = True 

557 

558 

559class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass): 

560 pass 

561 

562 

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. 

580 

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. 

584 

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. 

588 

589 ``widgets`` is a dictionary of model field names mapped to a widget. 

590 

591 ``localized_fields`` is a list of names of fields which should be localized. 

592 

593 ``formfield_callback`` is a callable that takes a model field and returns 

594 a form field. 

595 

596 ``labels`` is a dictionary of model field names mapped to a label. 

597 

598 ``help_texts`` is a dictionary of model field names mapped to a help text. 

599 

600 ``error_messages`` is a dictionary of model field names mapped to a 

601 dictionary of error messages. 

602 

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. 

609 

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 

628 

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" 

637 

638 # Class attributes for the new form class. 

639 form_class_attrs = {"Meta": Meta, "formfield_callback": formfield_callback} 

640 

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 ) 

646 

647 # Instantiate type(form) in order to use the same metaclass as form. 

648 return type(form)(class_name, (form,), form_class_attrs) 

649 

650 

651# ModelFormSets ############################################################## 

652 

653 

654class BaseModelFormSet(BaseFormSet): 

655 """ 

656 A ``FormSet`` for editing a queryset and/or adding new objects to it. 

657 """ 

658 

659 model = None 

660 

661 # Set of fields that must be unique among forms of this set. 

662 unique_fields = set() 

663 

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 ) 

686 

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() 

692 

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) 

697 

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 

706 

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 

740 

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() 

747 

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) 

753 

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 

759 

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) 

763 

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) 

767 

768 def delete_existing(self, obj, commit=True): 

769 """Deletes an existing model instance.""" 

770 if commit: 

771 obj.delete() 

772 

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 = [] 

780 

781 def save_m2m(): 

782 for form in self.saved_forms: 

783 form.save_m2m() 

784 

785 self.save_m2m = save_m2m 

786 return self.save_existing_objects(commit) + self.save_new_objects(commit) 

787 

788 save.alters_data = True 

789 

790 def clean(self): 

791 self.validate_unique() 

792 

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) 

810 

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) 

881 

882 if errors: 

883 raise ValidationError(errors) 

884 

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 } 

896 

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 } 

906 

907 def get_form_error(self): 

908 return gettext("Please correct the duplicate values below.") 

909 

910 def save_existing_objects(self, commit=True): 

911 self.changed_objects = [] 

912 self.deleted_objects = [] 

913 if not self.initial_forms: 

914 return [] 

915 

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 

935 

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 

949 

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 

953 

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. 

960 

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 ) 

971 

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) 

999 

1000 

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 ) 

1035 

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 

1065 

1066 

1067# InlineFormSets ############################################################# 

1068 

1069 

1070class BaseInlineFormSet(BaseModelFormSet): 

1071 """A formset for child objects related to a parent.""" 

1072 

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) 

1096 

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) 

1103 

1104 def initial_form_count(self): 

1105 if self.save_as_new: 

1106 return 0 

1107 return super().initial_form_count() 

1108 

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 

1123 

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 

1131 

1132 @classmethod 

1133 def get_default_prefix(cls): 

1134 return cls.fk.remote_field.get_accessor_name(model=cls.model).replace("+", "") 

1135 

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) 

1142 

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 } 

1157 

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 

1163 

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) 

1173 

1174 form.fields[name] = InlineForeignKeyField(self.instance, **kwargs) 

1175 

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) 

1179 

1180 

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 

1191 

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 

1258 

1259 

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. 

1288 

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 

1322 

1323 

1324# Fields ##################################################################### 

1325 

1326 

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 """ 

1332 

1333 widget = HiddenInput 

1334 default_error_messages = { 

1335 "invalid_choice": _("The inline value did not match the parent instance."), 

1336 } 

1337 

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) 

1349 

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 

1366 

1367 def has_changed(self, initial, data): 

1368 return False 

1369 

1370 

1371class ModelChoiceIteratorValue: 

1372 def __init__(self, value, instance): 

1373 self.value = value 

1374 self.instance = instance 

1375 

1376 def __str__(self): 

1377 return str(self.value) 

1378 

1379 def __hash__(self): 

1380 return hash(self.value) 

1381 

1382 def __eq__(self, other): 

1383 if isinstance(other, ModelChoiceIteratorValue): 

1384 other = other.value 

1385 return self.value == other 

1386 

1387 

1388class ModelChoiceIterator: 

1389 def __init__(self, field): 

1390 self.field = field 

1391 self.queryset = field.queryset 

1392 

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) 

1402 

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) 

1408 

1409 def __bool__(self): 

1410 return self.field.empty_label is not None or self.queryset.exists() 

1411 

1412 def choice(self, obj): 

1413 return ( 

1414 ModelChoiceIteratorValue(self.field.prepare_value(obj), obj), 

1415 self.field.label_from_instance(obj), 

1416 ) 

1417 

1418 

1419class ModelChoiceField(ChoiceField): 

1420 """A ChoiceField whose choices are a model QuerySet.""" 

1421 

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 

1430 

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 

1466 

1467 def get_limit_choices_to(self): 

1468 """ 

1469 Return ``limit_choices_to`` for this form field. 

1470 

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 

1476 

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 

1483 

1484 def _get_queryset(self): 

1485 return self._queryset 

1486 

1487 def _set_queryset(self, queryset): 

1488 self._queryset = None if queryset is None else queryset.all() 

1489 self.widget.choices = self.choices 

1490 

1491 queryset = property(_get_queryset, _set_queryset) 

1492 

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) 

1502 

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 

1508 

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) 

1517 

1518 choices = property(_get_choices, ChoiceField._set_choices) 

1519 

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) 

1527 

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 

1543 

1544 def validate(self, value): 

1545 return Field.validate(self, value) 

1546 

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) 

1553 

1554 

1555class ModelMultipleChoiceField(ModelChoiceField): 

1556 """A MultipleChoiceField whose choices are a model QuerySet.""" 

1557 

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 } 

1567 

1568 def __init__(self, queryset, **kwargs): 

1569 super().__init__(queryset, empty_label=None, **kwargs) 

1570 

1571 def to_python(self, value): 

1572 if not value: 

1573 return [] 

1574 return list(self._check_values(value)) 

1575 

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 

1592 

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 

1629 

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) 

1639 

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 

1652 

1653 

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 )