-
-
Notifications
You must be signed in to change notification settings - Fork 460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Form.clean method return annotation #954
Comments
I do see things differently... This is just how inheritance and types work. If you don't put Moreover, if you write a form class, it could always (later) be combined via multiple inheritance with another one that returns Yes one has to check for |
To put it more plainly, Django will never return an optional dict, and my forms will also never return optional dicts, so I don't see why this should be the return signature for the code. I'm open to being convinced otherwise though. Could you elaborate on why you think type hinting all potential returns types of inherited forms makes more sense than specifying precisely what the library does return? Is there a good reason why you would change the signature for your own forms' clean methods (other than for the fact that it's possible)? I'm not really sold on the fact that this is a valid return signature to begin with. If there's a documented standard/convention for this, I guess that would settle this pretty quickly. |
I don't think this is a bug and If you look at the documentation it states the following:
This is also backed up in where Django calls The problem is that there is no way to determine whether Instead, we have to do the following if we want to access the cleaned data: class Form(forms.Form):
...
def clean(self) -> Optional[Dict[str, Any]]:
cleaned_data = super().clean()
if cleaned_data is None:
cleaned_data = self.cleaned_data
...
return cleaned_data This narrows the type so that we can always expect It seems that it would be prudent to do this always given that class FormMixin:
def clean(self):
cleaned_data = super().clean()
if cleaned_data.get("field") != "value":
raise ValidationError("Invalid value.")
class Form(FormMixin, forms.Form):
def clean(self):
cleaned_data = super.clean()
cleaned_data.get("field") # AttributeError |
Indeed. Your mixin example shows the kind of pattern that type hints help us catch, which would not be possible without the correct hint. |
Bug report
What's wrong
The clean method for Django forms is currently defined as
Optional[Dict[str, Any]]
.How is that should be
Since the clean method just returns
cleaned_data
, it seems like it should match the type ofcleaned_data
(django source):I also think
cleaned_data
seems correctly typed (as Dict[str, Any]), though I haven't looked into it too in-depth.Additional context
The type for
clean
seems to have been changed in #862 on the basis that the clean method is described as "permitted to returnNone
" wrt. the frameworks constraints.I can see why the change was made, but all calls to
super().clean()
now require (unnecessary in almost all cases) checking for optionality:This code will generate
error: Value of type "Optional[Mapping[str, Any]]" is not indexable [index]
which seems wrong.It seems wrong because the type of
super().clean()
will always bedict
, unless the form inherits from another form instance that has changed the type signature (AFACIT).There might be a third option, but if our choices are:
Optional[dict]
to prevent mypy errors when changing the return type ofclean
in a form inheriting fromForm
dict
to prevent mypy errors when callingsuper().clean()
I personally think nr. 2 is the correct choice.
Does that make sense?
@adamchainz, do you see things differently?
The text was updated successfully, but these errors were encountered: