簡體   English   中英

特定於Dialect的SQLAlchemy聲明性列默認值

[英]Dialect-specific SQLAlchemy declarative Column defaults

簡潔版本

在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。

class TestTable(Base):
    __tablename__ = 'test_table'
    id = sa.Column(
              sa.VARCHAR,
              primary_key=True,
              server_default=sa.FetchedValue())

像這樣的定義在postgres中工作得很好,但是當我調用Session.commit時,死在sqlite中(正如你在Ideone上看到的那樣 )和FlushError

sqlalchemy.orm.exc.FlushError :實例<TestTable at 0x7fc0e0254a10>具有NULL身份密鑰。 如果這是自動生成的值,請檢查數據庫表是否允許生成新的主鍵值,以及映射的Column對象是否配置為期望這些生成的值。 還要確保此flush()沒有在不適當的時間發生,例如在load()事件中。

FetchedValue的文檔警告我們,這可能發生在不支持INSERT上的RETURNING子句的方言上

對於使用觸發器生成主鍵值的特殊情況,並且正在使用的數據庫不支持RETURNING子句,可能需要放棄使用觸發器,而是將SQL表達式或函數應用為“預執行”表達:

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

func.generate_new_value 沒有在SQLAlchemy中的任何其他地方定義 ,因此它們似乎打算在Python中生成默認值,或者編寫一個單獨的函數來執行SQL查詢以在DBMS中生成默認值。 我可以這樣做,但問題是,我只想為SQLite做這 FetchedValue ,因為FetchedValue完全符合我想要的postgres。

死胡同

  • 子類化Column可能不起作用。 我在源中找不到任何內容,告訴Column使用什么方言, defaultserver_default字段的行為是在類外定義的

  • 編寫一個在真實DBMS上手動調用觸發器的python函數會產生競爭條件。 通過更改隔離級別來避免競爭條件會導致死鎖。

我當前的解決方法

糟糕,因為它打破了連接到真正的postgres的集成測試。

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)

不是直接回答你的問題,但希望對某人有幫助

我的問題是想要根據方言改變整理,這是我的解決方案:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM