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()
代码说明
- 在
__init__中添加判断,仅当kwargs中没有default时才设置值,避免克隆时重复覆盖。 - 重写
_copy方法,手动复制自定义的comment属性,确保克隆后的列实例保留该属性。 - 这种方式既适配了SQLAlchemy的克隆逻辑,又保证了自定义列的属性在继承过程中正确传递。
内容的提问来源于stack exchange,提问作者Vdutk
相关产品推荐
相关产品推荐

