SqlAlchemy:自引用默认值作为查询

时间:2013-10-26 17:46:23

标签: python sqlalchemy

假设我有以下结构(使用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”字段。

任何人都知道如何使用自引用查询来填充默认值?

1 个答案:

答案 0 :(得分:5)

一旦用户可用,只需在列中指定“default”即可解决默认情况下不引用User的问题。但是,这并不能解决问题,因为“self”也没有任何意义,没有调用User方法,所以你不能只提到“self”。这个陈述的挑战是你希望它被渲染为内联子SELECT,但它仍然需要知道“.name”的内存中值。因此,您必须以某种方式为每个对象分配该子SELECT。

人们通常使用before_insert处理程序来处理ORM级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()