You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:41:04