Description
Enhancement
What's wrong
The current type stubs define the authenticate
method of the ModelBackend
(django.contrib.auth.backends.ModelBackend
) as such:
def authenticate(
self,
request: HttpRequest | None,
username: str | None = ...,
password: str | None = ...,
**kwargs: Any,
) -> _UserModel | None: ...
The parent class, BaseBackend
defines it as such:
def authenticate(self, request: HttpRequest | None, **kwargs: Any) -> _UserModel | None: ...
When Django looks for which authentication backend to use, it'll always call said backend using the parameters as kwargs
, until it finds the first backend that accepts the parameters and returns a valid user (or None
if they all fail). The documentation itself instructs the user to define it's own kwargs
when defining a custom authentication backend (relevant docs).
However, a very common use case here is to subclass ModelBackend
instead of BaseBackend
, in cases where we want to make simple adjustments to how authentication is done, but not how it's handled once you have the specific user; in this cases, you'll subclass the ModelBackend
, which already has all the code to handle permissions that BaseBackend
does not, and simply overwrite the authenticate
method so that instead of, for example, a username and password, it uses a token or a code or something of the sorts. For example:
class MyCustomBackend(ModelBackend):
def authenticate(self, code: Optional[str] = None, **kwargs: Any) -> Optional[User]:
return User.get_user_for_code(code)
In this simple example Mypy will give an "override" error: Signature of "authenticate" incompatible with supertype "ModelBackend"
, even though this backend does not use the username / password at all.
Alternatives
There are already alternatives to getting around this of course; simply #type: ignore[override]
will do, or adding the typed arguments to the definition even though they are not used at all, as such:
class MyCustomBackend(ModelBackend):
def authenticate(
self,
username: Optional[str] = None,
password: Optional[str] = None,
code: Optional[str] = None,
**kwargs: Any
) -> Optional[User]:
return User.get_user_for_code(code)
How is that should be
The stubs for the ModelBackend
should not override the parent class' method definition at all (it should not define it at all and simply inherit). This will be inline to how all authentication backends are used, and allow users to override with their own type definitions without extra work.
System information
- OS: -
python
version: 3.13.2django
version: 5.1.7mypy
version: 1.15.0django-stubs
version: 5.1.3django-stubs-ext
version: 5.1.3