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

DRF自定义ChoiceField从数据库查询设置默认值的租户schema问题

问题根因

Django REST Framework 的序列化器字段会在项目启动、模块加载阶段完成初始化,你当前把数据库查询逻辑放在__init__方法中,代码会在服务启动时就执行,此时还没有收到用户请求,django_tenants 也还没切换到对应租户的 schema,只能访问到 public schema 的数据,这是问题的核心原因。

另外你原代码中OptionGroup.objects.filter(name__iexact=option_group)返回的是 QuerySet 对象,直接判断if group永远为真,属于需要修复的小问题。

可行实现方案

把默认值读取逻辑从__init__移到 DRF 字段自带的get_default()方法中,该方法会在实际处理请求、需要获取字段默认值时才执行,此时租户 schema 已经切换完成。

修改后的CustomChoiceField代码如下:

class CustomChoiceField(serializers.ChoiceField):

    def __init__(self, choices, **kwargs):
        # 仅存储参数,不执行数据库查询
        self.option_group = kwargs.pop('option_group', None)
        super().__init__(choices, **kwargs)

    def get_default(self):
        # 请求处理阶段才查询租户 schema 下的配置
        if self.option_group and not self.default:
            group = OptionGroup.objects.filter(name__iexact=self.option_group).first()
            if group and group.default_option:
                self.default = group.default_option
        return super().get_default()

序列化器的使用方式不需要做任何修改,原有逻辑保持不变即可。

可选优化:支持动态加载租户自定义选项

如果你的defined_in=option_table类型的选项也需要从租户 schema 读取,不需要在序列化器中写死 choices 参数,可以进一步扩展字段逻辑,实现选项和默认值完全从租户配置加载,保证前后端100%同步:

class CustomChoiceField(serializers.ChoiceField):
    def __init__(self, choices=None, **kwargs):
        self.option_group = kwargs.pop('option_group', None)
        # 初始化阶段先传空 choices,后续动态赋值
        super().__init__(choices or [], **kwargs)

    def _get_group(self):
        # 缓存查询结果,避免同一请求内重复查询数据库
        if not hasattr(self, '_group'):
            self._group = OptionGroup.objects.filter(name__iexact=self.option_group).first()
        return self._group

    def get_default(self):
        group = self._get_group()
        if group and group.default_option and not self.default:
            self.default = group.default_option
        return super().get_default()

    @property
    def choices(self):
        group = self._get_group()
        if not group:
            return super().choices
        # 按配置类型加载对应选项
        if group.defined_in == 'option_table':
            return [(opt.value, opt.name) for opt in group.options.all()]
        elif group.defined_in == 'class':
            # 从指定的 TextChoices 类加载选项
            from django.apps import apps
            choices_cls = apps.get_model(group.app_label, group.choices_class)
            return choices_cls.choices
        return super().choices

    @choices.setter
    def choices(self, value):
        # 保留原生 setter 逻辑,不影响初始化流程
        super().choices = value

内容的提问来源于stack exchange,提问作者ExTexan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 19:54:02