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

Flask+SQLAlchemy中CustomColumn的__init__被调用两次的问题

问题分析与解决

为什么CustomColumn.__init__被调用两次?

这是SQLAlchemy处理抽象模型继承的固有逻辑:

  • 第一次调用:定义BaseModel时执行my_column = CustomColumn(),此时无额外参数传入,kwargs为空,符合预期。
  • 第二次调用:当MyModel继承BaseModel时,SQLAlchemy会克隆抽象基类中的列对象——目的是让子类拥有独立的列定义(防止基类列的修改影响子类)。克隆过程中,SQLAlchemy会把原列已设置的属性(比如你添加的default=0、comment="My comment")打包成kwargs传入__init__,所以第二次调用时kwargs不为空。

解决方案

如果希望自定义列的行为在初始化和克隆时保持一致,或者避免重复设置属性,应该重写Column的_copy方法(SQLAlchemy克隆列的核心方法),而不是仅依赖__init__。

修改后的代码示例:

from flask import Flask
from sqlalchemy import Column, INTEGER
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////tmp/test.db"
db = SQLAlchemy(app)


class CustomColumn(Column):
    def __init__(self, *args, **kwargs):
        # 仅在首次初始化时设置default(避免克隆时重复覆盖)
        if "default" not in kwargs:
            kwargs["default"] = 0
        super().__init__(*args, **kwargs)
        # 仅在首次初始化时设置comment
        if not hasattr(self, "comment"):
            self.comment = "My comment"
    
    def _copy(self, **kwargs):
        # 克隆时复制自定义属性到新列实例
        copied_column = super()._copy(**kwargs)
        copied_column.comment = self.comment
        return copied_column


class BaseModel(db.Model):
    __abstract__ = True

    my_column = CustomColumn()


class MyModel(BaseModel):
    id = Column("id", INTEGER(), primary_key=True)


app.run()

代码说明

  1. 在__init__中添加判断,仅当kwargs中没有default时才设置值,避免克隆时重复覆盖。
  2. 重写_copy方法,手动复制自定义的comment属性,确保克隆后的列实例保留该属性。
  3. 这种方式既适配了SQLAlchemy的克隆逻辑,又保证了自定义列的属性在继承过程中正确传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:22:07