2014-03-03 41 views
2

我想在SQLAlchemy中創建一個不區分大小寫的唯一約束,它可以同時用於Postgres和SQLite。SQLAlchemy中的編譯擴展:索引中使用的函數

在Postgres裏,可以實現:

  • SQL:CREATE UNIQUE INDEX my_index ON my_table (lower(my_field));
  • SQLAlchemy的:Index('my_index', func.lower(my_field), unique=True)

在SQLite的它正在使用來實現:

  • SQL:CREATE UNIQUE INDEX my_index ON my_table (my_field COLLATE NOCASE);
  • SQLAlchemy的:Index('my_index', collate(my_field, 'nocase'), unique=True)

所以我想子類FunctionElement爲了創建自己的功能樣結構,這將彙編要麼my_field COLLATE NOCASElower(my_field)根據的DBMS。我試圖按照the guide to compilation extension,但我得到一個錯誤(請參閱下面的回溯),我可以通過SQLAlchemy源代碼遵循,但我不明白。我嘗試了子類別的其他東西,如ColumnElement,ColumnClause,ClauseElement,但我收到類似的錯誤。

這裏是重現錯誤的代碼。我使用SQLAlchemy的0.8.2版本。

from sqlalchemy import create_engine, Column, String, Integer 
from sqlalchemy.types import TypeDecorator, VARCHAR 
from sqlalchemy import func, Index, collate 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.ext.compiler import compiles 
from sqlalchemy.sql.expression import (
    # Not sure which one to subclass 
    FunctionElement, ColumnElement, ColumnClause, ClauseElement 
) 


engine = create_engine('sqlite:///:memory:', echo=True) 
Base = declarative_base() 


class CaseInsensitive(FunctionElement): 
    name = 'CaseInsensitive' 
    type = VARCHAR() 


@compiles(CaseInsensitive, 'sqlite') 
def case_insensitive_sqlite(element, compiler, **kw): 
    arg1, = list(element.clauses) 
    return collate(compiler.process(arg1), 'nocase') 


@compiles(CaseInsensitive, 'postgresql') 
def case_insensitive_postgresql(element, compiler, **kw): 
    arg1, = list(element.clauses) 
    return func.lower(compiler.process(arg1)) 


class MyTable(Base): 

    __tablename__ = 'my_table' 

    id = Column(Integer, primary_key=True) 

    my_field = Column(String) 

    __table_args__ = (
     Index('idx', CaseInsensitive(my_field), unique=True), 
    ) 

Traceback (most recent call last): 
    File "tmp.py", line 33, in <module> 
    class MyTable(Base): 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/ext/declarative/api.py", line 50, in __init__ 
    _as_declarative(cls, classname, cls.__dict__) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 222, in _as_declarative 
    **table_kw) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/schema.py", line 332, in __new__ 
    table._init(name, metadata, *args, **kw) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/schema.py", line 400, in _init 
    self._init_items(*args) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/schema.py", line 65, in _init_items 
    item._set_parent_with_dispatch(self) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/events.py", line 236, in _set_parent_with_dispatch 
    self._set_parent(parent) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/schema.py", line 2421, in _set_parent 
    ColumnCollectionMixin._set_parent(self, table) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/schema.py", line 2035, in _set_parent 
    self.columns.add(col) 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/sql/expression.py", line 2477, in add 
    self[column.key] = column 
    File "/Users/vladimir/git/wb/lib/python2.7/site-packages/sqlalchemy/sql/expression.py", line 2296, in __getattr__ 
    key) 
AttributeError: Neither 'CaseInsensitive' object nor 'Comparator' object has an attribute 'key' 
+0

雖然這並不關心的問題,直接,我還沒有看到「表ARGS」之前建造。對於其他人的幫助,如果您想知道[「__table args__」](http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#declarative-table-args),請點擊此處鏈接。 – Soferio

回答

4

的@compiles方法必須返回一個字符串。此外,還有在指數如何在這裏穿越functionelement一些小故障,所以我們需要一個小的解決方法:

class CaseInsensitive(FunctionElement): 
    __visit_name__ = 'notacolumn' 
    name = 'CaseInsensitive' 
    type = VARCHAR() 


@compiles(CaseInsensitive, 'sqlite') 
def case_insensitive_sqlite(element, compiler, **kw): 
    arg1, = list(element.clauses) 
    return compiler.process(collate(arg1, 'nocase'), **kw) 


@compiles(CaseInsensitive, 'postgresql') 
def case_insensitive_postgresql(element, compiler, **kw): 
    arg1, = list(element.clauses) 
    return compiler.process(func.lower(arg1), **kw) 
+0

我正在使用它,它自己的工作很好,但alembic現在變得困惑,似乎無法爲創建索引創建升級腳本。 Alembic給出了一個遷移線,例如:「op.create_index('idxUsername','users',['CaseInsensitive'],unique = True)」。當我運行「alembic upgrade head」時,出現以「sqlalchemy.exc.ProgrammingError:(ProgrammingError)列」CaseInsensitive「不存在的錯誤追蹤 'CREATE UNIQUE INDEX」idxUsername「ON users(」CaseInsensitive「 )'{}「。我不確定如何微調遷移腳本。 – Soferio