Skip to content

Commit f08b428

Browse files
author
proxy
authored
make FormMixin generic to allow proper typing for LoginView (#515)
closes #514
1 parent 44151c4 commit f08b428

File tree

3 files changed

+24
-9
lines changed

3 files changed

+24
-9
lines changed

django-stubs/contrib/auth/views.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Any, Optional, Set
22

33
from django.contrib.auth.base_user import AbstractBaseUser
4+
from django.contrib.auth.forms import AuthenticationForm
45
from django.core.handlers.wsgi import WSGIRequest
56
from django.http.request import HttpRequest
67
from django.http.response import HttpResponseRedirect
@@ -14,7 +15,7 @@ class SuccessURLAllowedHostsMixin:
1415
success_url_allowed_hosts: Any = ...
1516
def get_success_url_allowed_hosts(self) -> Set[str]: ...
1617

17-
class LoginView(SuccessURLAllowedHostsMixin, FormView):
18+
class LoginView(SuccessURLAllowedHostsMixin, FormView[AuthenticationForm]):
1819
authentication_form: Any = ...
1920
redirect_field_name: Any = ...
2021
redirect_authenticated_user: bool = ...

django-stubs/views/generic/edit.pyi

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Callable, Dict, Optional, Sequence, Type, Union
1+
from typing import Any, Callable, Dict, Generic, Optional, Sequence, Type, TypeVar, Union
22

33
from django.forms.forms import BaseForm
44
from django.forms.models import BaseModelForm
@@ -8,6 +8,8 @@ from typing_extensions import Literal
88

99
from django.http import HttpRequest, HttpResponse
1010

11+
_FormT = TypeVar('_FormT', bound=BaseForm)
12+
1113
class AbstractFormMixin(ContextMixin):
1214
initial: Dict[str, Any] = ...
1315
form_class: Optional[Type[BaseForm]] = ...
@@ -18,11 +20,11 @@ class AbstractFormMixin(ContextMixin):
1820
def get_form_kwargs(self) -> Dict[str, Any]: ...
1921
def get_success_url(self) -> str: ...
2022

21-
class FormMixin(AbstractFormMixin):
22-
def get_form_class(self) -> Type[BaseForm]: ...
23-
def get_form(self, form_class: Optional[Type[BaseForm]] = ...) -> BaseForm: ...
24-
def form_valid(self, form: BaseForm) -> HttpResponse: ...
25-
def form_invalid(self, form: BaseForm) -> HttpResponse: ...
23+
class FormMixin(Generic[_FormT], AbstractFormMixin):
24+
def get_form_class(self) -> Type[_FormT]: ...
25+
def get_form(self, form_class: Optional[Type[_FormT]] = ...) -> BaseForm: ...
26+
def form_valid(self, form: _FormT) -> HttpResponse: ...
27+
def form_invalid(self, form: _FormT) -> HttpResponse: ...
2628

2729
class ModelFormMixin(AbstractFormMixin, SingleObjectMixin):
2830
fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...
@@ -36,8 +38,8 @@ class ProcessFormView(View):
3638
def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
3739
def put(self, *args: str, **kwargs: Any) -> HttpResponse: ...
3840

39-
class BaseFormView(FormMixin, ProcessFormView): ...
40-
class FormView(TemplateResponseMixin, BaseFormView): ...
41+
class BaseFormView(FormMixin[_FormT], ProcessFormView): ...
42+
class FormView(TemplateResponseMixin, BaseFormView[_FormT]): ...
4143
class BaseCreateView(ModelFormMixin, ProcessFormView): ...
4244
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): ...
4345
class BaseUpdateView(ModelFormMixin, ProcessFormView): ...

tests/typecheck/test_views.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
- case: login_form_form_valid_typechecks
2+
main: |
3+
from django.contrib.auth.views import LoginView
4+
from django.contrib.auth import login as auth_login
5+
from django.http import HttpResponseRedirect
6+
from django.contrib.auth.forms import AuthenticationForm
7+
8+
class MyLoginView(LoginView):
9+
def form_valid(self, form: AuthenticationForm) -> HttpResponseRedirect:
10+
"""Ensure that form can have type AuthenticationForm."""
11+
form.get_user()
12+
return HttpResponseRedirect(self.get_success_url())

0 commit comments

Comments
 (0)