#9245: Using choices in a mode field forces use of TypedChoiceField in a form field ------------------------+------------------------------------ Reporter: Tarken | Owner: badri Type: Bug | Status: reopened Component: Forms | Version: 1.0 Severity: Normal | Resolution: Keywords: | Triage Stage: Accepted Has patch: 1 | Needs documentation: 1 Needs tests: 0 | Patch needs improvement: 0 Easy pickings: 0 | UI/UX: 0 ------------------------+------------------------------------ Changes (by dgouldin):
* cc: dgouldin (added) Comment: Julien, this patch will not work as-is. Your new Field.choices_formfield function is called from Field.formfield using **defaults, but defaults will never include form_class. This means that Field.choices_formfield will always be called with its default value for form_class (None unless overridden by a custom Field subclass) even when the user's intent was to provide a custom form_class for that model field. So for this model: {{{ class Foo(models.Model): bar = models.IntegerField(choices=bar_choices, form_class=MyCustomFormClass) }}} MyCustomFormClass would never make it to bar's Field.choices_formfield, and so an instance of it would not be returned by Field.formfield. However, it also not work to simply call self.choices_formfield(form_class=form_class, **defaults) from Field.formfield. Subclasses of Field (such as IntegerField) frequently override formfield() and call super's formfield() with a form_class kwarg. This means that calling Field.choices_formfield with Field.formfield's form_class would break models like this: {{{ class Foo(models.Model): bar = models.IntegerField(choices=bar_choices) }}} In this case, IntegerField.formfield would call Field.formfield with form_class=forms.IntegerField, which would then be passed through to Field.choices_formfield, causing forms.TypedChoiceField not to be used as was intended. As the implementation sits currently, Field has no way to determine whether the intent of the user was to specify their own form_class or whether the Field subclass injected its own form_class before calling Field.formfield. This is broken, plain and simple. In order for Field.formfield to make a good decision about which class to use when instantiating a form field, it needs both pieces of information: the model field's default form_class, and the user's override of that default (if provided). Here's the solution I propose: {{{ class Field(object): default_form_class = forms.CharField # ... def formfield(self, form_class=None, **kwargs): if self.choices: form_class = form_class or forms.TypedChoiceField # ... else: form_class = form_class or self.default_form_class class IntegerField(Field): default_form_class = forms.IntegerField }}} This allows Field.formfield to have both pieces of information, and it alleviates the need for IntegerField to override formfield. It is not backwards compatible, as any model field subclasses which use this formfield override approach would need to be changed to fit the new default_form_class property approach. -- Ticket URL: <https://code.djangoproject.com/ticket/9245#comment:15> Django <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-updates@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.