4

假设我有以下结构(使用 Flask-SqlAlchemy):

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False, index=True)
    # The following line throws an error at runtime.
    variant = db.Column(db.Integer, nullable=False, index=True,
                        default=select(func.count(User.id)).where(User.name == self.name))

    def __init__(self, name):
        super(User, self).__init__()
        self.name = name

    @property
    def clause(self):
        return '/'.join([str(self.variant), self.name])

问题是“未定义用户”。我想用用户建模一个系统,这些用户可以选择相同的名称,但添加一个字段以系统方式区分用户,而不使用(从而公开)“id”字段。

任何人都知道如何进行自引用查询以填充默认值?

4

1 回答 1

5

一旦用户可用,只需将“默认”分配给列,即可解决此处默认不指代用户的问题。但是,这并不能解决这里的问题,因为“self”也没有任何意义,没有调用 User 方法,因此您不能只引用“self”。此语句的挑战在于您希望将其呈现为内联子 SELECT,但它仍需要知道“.name”的内存值。因此,您必须以某种方式为每个对象分配该子 SELECT。

人们处理 ORM 级别 INSERT 默认值的常用方法通常是使用before_insert处理程序。

值得指出的另一种方法是创建 SQL 级别的 INSERT 触发器。这总体上是最“传统”的方法,因为在这里您需要访问正在插入的行;触发器定义了一种获取正在插入的行值的方法。

至于在列级别使用默认值,您需要使用可调用函数作为默认值,该函数可以查看正在插入的行的当前值,但目前这意味着您的 SELECT 语句将不会被呈现与 INSERT 语句内联,您需要预先执行 SELECT,这并不是我们真正想要的。

无论如何,将 SQL 表达式呈现到 INSERT 中,同时让该 SQL 表达式引用某个本地的每个对象状态的基本任务是通过将该表达式分配给属性来实现的,ORM 在刷新时接受这一点。下面我们在构造函数中执行此操作,但这也可能发生在 before_insert() 内部:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False, index=True)
    variant = Column(Integer, nullable=False, index=True)

    def __init__(self, name):
        self.name = name
        self.variant = select([func.count(User.id)]).where(User.name == self.name)

e = create_engine("sqlite://", echo=True)

Base.metadata.create_all(e)

s = Session(e)

s.add(User(name='n1'))
s.commit()

s.add(User(name='n1'))
s.commit()

print s.query(User.variant).all()
于 2013-10-27T17:22:23.030 回答