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
相关产品推荐
相关产品推荐

