特定于方言的SQLAlchemy声明性列默认值

2024-05-15 16:22:12 发布

您现在位置:Python中文网/ 问答频道 /正文

短版

在SQLAlchemy的ORM列声明中,我如何在一种方言中使用server_default=sa.FetchedValue(),在另一种方言上使用default=somePythonFunction,这样我的真正的DBMS就可以用触发器填充东西,并且可以针对sqlite编写测试代码?在

背景

我正在使用SQLAlchemy的声明性ORM来处理Postgres数据库,但是试图针对sqlite:///:memory:编写单元测试,结果遇到了一个问题,这些列已经计算了主键的默认值。最简单的例子是:

CREATE TABLE test_table(
    id VARCHAR PRIMARY KEY NOT NULL
       DEFAULT (lower(hex(randomblob(16))))
)

SQLite本身对这个表定义(sqlfiddle)很满意,但是SQLAlchemy似乎无法计算出新创建的行的ID。在

^{pr2}$

像这样的定义在postgres中工作得很好,但是在sqlite中(正如您可以看到的on Ideone)在我调用^{}时使用FlushError

sqlalchemy.orm.exc.FlushError: Instance <TestTable at 0x7fc0e0254a10> has a NULL identity key. If this is an auto-generated value, check that the database table allows generation of new primary key values, and that the mapped Column object is configured to expect these generated values. Ensure also that this flush() is not occurring at an inappropriate time, such as within a load() event.

FetchedValuewarns us that this can happen on dialects that don't support the ^{} clause on ^{}的文档:

For special situations where triggers are used to generate primary key values, and the database in use does not support the RETURNING clause, it may be necessary to forego the usage of the trigger and instead apply the SQL expression or function as a “pre execute” expression:

t = Table('test', meta,
        Column('abc', MyType, default=func.generate_new_value(), 
               primary_key=True)
)

func.generate_new_valuenot defined anywhere else in SQLAlchemy,所以他们似乎希望我要么在Python中生成默认值,要么编写一个单独的函数来执行SQL查询,在DBMS中生成一个默认值。我可以这样做,但问题是,我只想为SQLite这样做,因为FetchedValue完全符合我在postgres上想要的功能。在

死胡同

  • 子类化Column可能行不通。我在源代码中找不到任何东西告诉专栏使用的是什么方言,default和{}字段的行为是defined outside the class

  • 编写一个python函数,在实际的DBMS上手工调用触发器会产生竞争条件。通过更改isolation level来避免竞争条件会导致死锁。

我当前的解决方法

不好,因为它破坏了连接到真正的postgre的集成测试。在

import sys
import sqlalchemy as sa

def trigger_column(*a, **kw):
    python_default = kw.pop('python_default')
    if 'unittest' in sys.modules:
        return sa.Column(*a, default=python_default, **kw)
    else
        return sa.Column(*a, server_default=sa.FetchedValue(), **kw)

Tags: thekeydefaultsqlitethatsqlalchemyison
1条回答
网友
1楼 · 发布于 2024-05-15 16:22:12

不是直接回答你的问题,但希望能对某人有所帮助

我的问题是想根据方言更改排序规则,这是我的解决方案:

from sqlalchemy import Unicode
from sqlalchemy.ext.compiler import compiles

@compiles(Unicode, 'sqlite')    
def compile_unicode(element, compiler, **kw):
    element.collation = None
    return compiler.visit_unicode(element, **kw)

这将只更改sqlite的所有Unicode列的排序规则。在

以下是一些文档:http://docs.sqlalchemy.org/en/latest/core/custom_types.html#overriding-type-compilation

相关问题 更多 >