From 69042783b1186a8f2786cebac451e1504ee039cb Mon Sep 17 00:00:00 2001 From: Ceesjan Luiten Date: Fri, 12 Jun 2020 20:12:56 +0200 Subject: [PATCH] Allow form.save() in CreateView / UpdateView (#374) --- django-stubs/views/generic/edit.pyi | 15 +++++++++++---- test-data/typecheck/test_forms.yml | 12 ++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/django-stubs/views/generic/edit.pyi b/django-stubs/views/generic/edit.pyi index 219ebed..2a2f478 100644 --- a/django-stubs/views/generic/edit.pyi +++ b/django-stubs/views/generic/edit.pyi @@ -1,28 +1,35 @@ from typing import Any, Callable, Dict, Optional, Sequence, Type, Union from django.forms.forms import BaseForm +from django.forms.models import BaseModelForm from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.detail import BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin from typing_extensions import Literal from django.http import HttpRequest, HttpResponse -class FormMixin(ContextMixin): +class AbstractFormMixin(ContextMixin): initial: Dict[str, Any] = ... form_class: Optional[Type[BaseForm]] = ... success_url: Optional[Union[str, Callable[..., Any]]] = ... prefix: Optional[str] = ... def get_initial(self) -> Dict[str, Any]: ... def get_prefix(self) -> Optional[str]: ... - def get_form_class(self) -> Type[BaseForm]: ... - def get_form(self, form_class: Optional[Type[BaseForm]] = ...) -> BaseForm: ... def get_form_kwargs(self) -> Dict[str, Any]: ... def get_success_url(self) -> str: ... + +class FormMixin(AbstractFormMixin): + def get_form_class(self) -> Type[BaseForm]: ... + def get_form(self, form_class: Optional[Type[BaseForm]] = ...) -> BaseForm: ... def form_valid(self, form: BaseForm) -> HttpResponse: ... def form_invalid(self, form: BaseForm) -> HttpResponse: ... -class ModelFormMixin(FormMixin, SingleObjectMixin): +class ModelFormMixin(AbstractFormMixin, SingleObjectMixin): fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ... + def get_form_class(self) -> Type[BaseModelForm]: ... + def get_form(self, form_class: Optional[Type[BaseModelForm]] = ...) -> BaseModelForm: ... + def form_valid(self, form: BaseModelForm) -> HttpResponse: ... + def form_invalid(self, form: BaseModelForm) -> HttpResponse: ... class ProcessFormView(View): def get(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ... diff --git a/test-data/typecheck/test_forms.yml b/test-data/typecheck/test_forms.yml index ed200a0..1416406 100644 --- a/test-data/typecheck/test_forms.yml +++ b/test-data/typecheck/test_forms.yml @@ -46,6 +46,18 @@ reveal_type(self.get_form(form_class)) # N: Revealed type is 'main.MyForm' reveal_type(self.get_form(MyForm2)) # N: Revealed type is 'main.MyForm2' +- case: updateview_form_valid_has_form_save + main: | + from django import forms + from django.views.generic.edit import UpdateView + + class MyForm(forms.ModelForm): + pass + class MyView(UpdateView): + form_class = MyForm + def form_valid(self, form: forms.BaseModelForm): + reveal_type(form.save) # N: Revealed type is 'def (commit: builtins.bool =) -> Any' + - case: successmessagemixin_compatible_with_formmixin main: | from django.views.generic.edit import FormMixin