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

DRF操作含计算列的SQL Server表时遇修改错误求助

解决DRF操作SQL Server计算列时的"column cannot be modified"错误

这个问题我之前处理过类似场景,核心原因是:虽然你在序列化器里把description设为了read_only_fields,但时态表(TemporalModel)的历史记录创建逻辑(也就是delete_me方法)仍然会尝试将计算列Description写入旧记录,而SQL Server本身禁止修改计算列。

问题根源拆解

DRF的read_only_fields只负责过滤客户端请求里的字段,但你的TemporalModel在生成历史版本时,是直接从当前模型实例复制所有字段值——这时候计算列的内容会被一并带入,插入历史记录时就触发了SQL Server的限制。

具体解决方案

方案1:在delete_me方法中手动排除计算列

找到temporal.py里的delete_me方法,在复制字段时直接过滤掉计算列,比如针对你的BondMaster表:

def delete_me(self):
    # 定义需要排除的计算列列表
    excluded_computed_fields = ['description']
    
    # 仅复制非计算列的字段值
    old_record_data = {
        field.name: getattr(self, field.name)
        for field in self._meta.fields
        if field.name not in excluded_computed_fields
    }
    
    # 创建并保存历史记录
    old_record = self.__class__(**old_record_data)
    old_record.valid_to = timezone.now()
    old_record.is_current = False
    old_record.save()

方案2:给计算列加标记,让时态逻辑自动识别排除

如果有多个计算列,或者想让逻辑更通用,可以给模型里的计算列添加自定义属性,然后在temporal逻辑中自动过滤:
首先修改BondMaster模型的计算列定义:

class BondMaster(TemporalModel):
    ticker = models.CharField(max_length=50)
    maturity_date = models.DateField()
    coupon = models.DecimalField(max_digits=10, decimal_places=2)
    # 给计算列添加自定义标记属性
    description = models.CharField(max_length=200, computed=True)

    class Meta:
        managed = False
        db_table = 'BondMaster'

然后更新temporal.py的delete_me方法:

def delete_me(self):
    old_record_data = {}
    for field in self._meta.fields:
        # 跳过带有computed=True标记的字段
        if hasattr(field, 'computed') and field.computed:
            continue
        old_record_data[field.name] = getattr(self, field.name)
    
    old_record = self.__class__(**old_record_data)
    old_record.valid_to = timezone.now()
    old_record.is_current = False
    old_record.save()

方案3:复用序列化器的字段过滤规则

也可以直接用序列化器的字段规则来生成历史记录数据,确保和客户端请求的过滤逻辑一致:

def delete_me(self):
    # 用当前模型对应的序列化器来获取过滤后的字段数据
    serializer = self.serializer_class(instance=self)
    old_record_data = serializer.data
    
    # 注意:序列化器返回的是JSON格式数据,需要转换为模型可接受的类型(比如日期、小数)
    for key, value in old_record_data.items():
        field = self._meta.get_field(key)
        old_record_data[key] = field.to_python(value)
    
    old_record = self.__class__(**old_record_data)
    old_record.valid_to = timezone.now()
    old_record.is_current = False
    old_record.save()

验证步骤

  1. 修改代码后重启服务
  2. 执行POST/PUT操作,检查历史记录是否成功创建,且未包含Description列
  3. 确认SQL Server不再抛出"The column cannot be modified"错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:17:54