Package: django-types
django-typesType stubs forDjango.Note: this project was forked fromhttps://github.com/typeddjango/django-stubswith the goal of removing themypyplugin dependency so thatmypycan'tcrash due to Django
config, and that
non-mypytype checkers likepyrightwill work better with
Django.installpipinstalldjango-typesYou can get aTypeError: 'type' object is not subscriptablewhen you will try to useQuerySet[MyModel],Manager[MyModel]or some other Django-based Generic types.This happens because these Django classes do not support__class_getitem__magic method in runtime.You can go withdjango_stubs_exthelper, that patches all the types we use as Generic in django.Install it:pipinstalldjango-stubs-ext# as a production dependencyAnd then place in your top-level settings:importdjango_stubs_extdjango_stubs_ext.monkeypatch()You can add extra types to patch withdjango_stubs_ext.monkeypatch(extra_classes=[YourDesiredType])You can use strings instead:'QuerySet[MyModel]'and'Manager[MyModel]', this way it will work as a type for type-checking and as a regularstrin runtime.usageForeignKey ids and related names as properties in ORM modelsWhen defining a Django ORM model with a foreign key, like so:classUser(models.Model):team=models.ForeignKey("Team",null=True,on_delete=models.SET_NULL,)role=models.ForeignKey("Role",null=True,on_delete=models.SET_NULL,related_name="users",)two properties are created,teamas expected, andteam_id. Also, a related
manager calleduser_setis created onTeamfor the reverse access.In order to properly add typing to the foreign key itself and also for the created ids you can do
something like this:fromtypingimportTYPE_CHECKINGfromsomeapp.modelsimportTeamifTYPE_CHECKING:# In this example Role cannot be imported due to circular import issues,# but doing so inside TYPE_CHECKING will make sure that the typing below# knows what "Role" meansfromanotherapp.modelsimportRoleclassUser(models.Model):team_id:Optional[int]team=models.ForeignKey(Team,null=True,on_delete=models.SET_NULL,)role_id:introle=models.ForeignKey["Role"]("Role",null=False,on_delete=models.SET_NULL,related_name="users",)reveal_type(User().team)# note: Revealed type is 'Optional[Team]'reveal_type(User().role)# note: Revealed type is 'Role'This will make sure thatteam_idandrole_idcan be accessed. Also,teamandrolewill be typed to their right objects.To be able to access the related managerTeamandRoleyou could do:fromtypingimportTYPE_CHECKINGifTYPE_CHECKING:# This doesn't really exists on django so it always need to be imported this wayfromdjango.db.models.managerimportRelatedManagerfromuser.modelsimportUserclassTeam(models.Model):ifTYPE_CHECKING:user_set=RelatedManager["User"]()classRole(models.Model):ifTYPE_CHECKING:users=RelatedManager["User"]()reveal_type(Team().user_set)# note: Revealed type is 'RelatedManager[User]'reveal_type(Role().users)# note: Revealed type is 'RelatedManager[User]'An alternative is using annotations:from__future__importannotations# or just be in python 3.11fromtypingimportTYPE_CHECKINGifTYPE_CHECKING:fromdjango.db.modelsimportManagerfromuser.modelsimportUserclassTeam(models.Model):user_set:Manager[User]classRole(models.Model):users:Manager[User]reveal_type(Team().user_set)# note: Revealed type is 'Manager[User]'reveal_type(Role().users)# note: Revealed type is 'Manager[User]'id FieldBy default Django will create anAutoFieldfor you if one doesn't exist.For type checkers to know about theidfield you'll need to declare the
field explicitly.# beforeclassPost(models.Model):...# afterclassPost(models.Model):id=models.AutoField(primary_key=True)# ORid:intHttpRequest'suserpropertyTheHttpRequest'suserproperty has a type ofUnion[AbstractBaseUser, AnonymousUser],
but for most of your views you'll probably want either an authed user or anAnonymousUser.So we can define a subclass for each case:classAuthedHttpRequest(HttpRequest):user:User# type: ignore [assignment]And then you can use it in your views:@auth.login_requireddefactivity(request:AuthedHttpRequest,team_id:str)->HttpResponse:...You can also get more strict with yourlogin_requireddecorator so that the
first argument of the function it is decorating isAuthedHttpRequest:fromtypingimportAny,Union,TypeVar,castfromdjango.httpimportHttpRequest,HttpResponsefromtyping_extensionsimportProtocolfromfunctoolsimportwrapsclassRequestHandler1(Protocol):def__call__(self,request:AuthedHttpRequest)->HttpResponse:...classRequestHandler2(Protocol):def__call__(self,request:AuthedHttpRequest,__arg1:Any)->HttpResponse:...RequestHandler=Union[RequestHandler1,RequestHandler2]# Verbose bound arg due to limitations of Python typing.# see: https://github.com/python/mypy/issues/5876_F=TypeVar("_F",bound=RequestHandler)deflogin_required(view_func:_F)->_F:@wraps(view_func)defwrapped_view(request:AuthedHttpRequest,*args:object,**kwargs:object)->HttpResponse:ifrequest.user.is_authenticated:returnview_func(request,*args,**kwargs)# type: ignore [call-arg]raiseAuthenticationRequiredreturncast(_F,wrapped_view)Then the following will type error:@auth.login_requireddefactivity(request:HttpRequest,team_id:str)->HttpResponse:...relatedhttps://github.com/sbdchd/djangorestframework-typeshttps://github.com/sbdchd/celery-typeshttps://github.com/sbdchd/mongo-typeshttps://github.com/sbdchd/msgpack-types