Django多租户项目mypy类型检查提示HttpRequest无tenant属性的解决咨询
解决Django多租户中Mypy无法识别Request的Tenant属性问题
问题根源
你已经自定义了TenantHttpRequest继承自原生HttpRequest并添加了tenant属性,但视图中self.request的默认类型仍是Django原生的HttpRequest,Mypy无法感知中间件附加的tenant属性,因此抛出类型错误。
解决方案
方案1:视图内直接添加类型断言
在视图中访问self.request.tenant前,将self.request断言为自定义的TenantHttpRequest,明确告知Mypy其类型:
修改views.py:
from typing import Any, Dict from django.views.generic import TemplateView from apps.tenants.models import Tenant as Realm from apps_tenants.ticket_system.models import Ticket # 导入自定义请求类 from apps.tenants.custom_request import TenantHttpRequest class StaffDashboardView(TemplateView): template_name = "dashboard/dash-staff/dash.html" def get_context_data(self, **kwargs: Dict[str, Any]) -> Dict[str, Any]: context = super().get_context_data(**kwargs) # 类型断言,指定request为自定义类型 request = self.request # type: TenantHttpRequest context["logo_url"] = Realm.objects.get( schema_name=request.tenant.schema_name ).logo_url context["profile_image_url"] = "" context["tickets"] = Ticket.objects.all() return context class CustomerDashboardView(TemplateView): template_name = "dashboard/dash-customer/dash.html" def get_context_data(self, **kwargs: Dict[str, Any]) -> Dict[str, Any]: context = super().get_context_data(**kwargs) # 类型断言 request = self.request # type: TenantHttpRequest context["logo_url"] = Realm.objects.get( schema_name=request.tenant.schema_name ).logo_url context["profile_image_url"] = "" context["tickets"] = Ticket.objects.all() return context
方案2:自定义视图基类统一处理类型
创建一个基类视图,将request属性的类型固定为TenantHttpRequest,业务视图继承该基类即可避免重复断言:
# views/base.py from django.views.generic import TemplateView from apps.tenants.custom_request import TenantHttpRequest from typing import TypeVar, Generic T = TypeVar('T', bound=TenantHttpRequest) class TenantBaseView(TemplateView, Generic[T]): request: T
修改业务视图:
from typing import Any, Dict from apps.tenants.models import Tenant as Realm from apps_tenants.ticket_system.models import Ticket from .base import TenantBaseView from apps.tenants.custom_request import TenantHttpRequest class StaffDashboardView(TenantBaseView[TenantHttpRequest]): template_name = "dashboard/dash-staff/dash.html" def get_context_data(self, **kwargs: Dict[str, Any]) -> Dict[str, Any]: context = super().get_context_data(**kwargs) context["logo_url"] = Realm.objects.get( schema_name=self.request.tenant.schema_name ).logo_url context["profile_image_url"] = "" context["tickets"] = Ticket.objects.all() return context
方案3:用Mypy Stub扩展原生HttpRequest类型
在项目根目录创建mypy_stubs/django/http/__init__.pyi目录及文件(不存在则新建),扩展原生HttpRequest的类型定义:
# mypy_stubs/django/http/__init__.pyi from django.http import HttpRequest as OriginalHttpRequest from typing import Union, TYPE_CHECKING if TYPE_CHECKING: from apps.tenants.models import Tenant class HttpRequest(OriginalHttpRequest): tenant: Union["Tenant", None]
然后在mypy.ini或pyproject.toml中配置Stub目录:
# mypy.ini [mypy] mypy_path = ./mypy_stubs
这样Mypy会自动识别原生HttpRequest包含tenant属性,无需修改视图代码。
验证
完成任意一种方案后,运行mypy执行类型检查,tenant属性不存在的错误将不再出现。
内容的提问来源于stack exchange,提问作者Adam Birds
相关产品推荐
相关产品推荐

