Python 带PostgreSQL和全文搜索的SQLAlchemy

Python 带PostgreSQL和全文搜索的SQLAlchemy,python,postgresql,sqlalchemy,flask,Python,Postgresql,Sqlalchemy,Flask,我用的是烧瓶炼金术和烧瓶炼金术。我想用gin和to_向量在postgres中创建一个完整的测试搜索索引。目前,我正在尝试以下方法。我想这是我最接近我想要表达的东西,但是没有用 from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.schema import Index from sqlalchemy.sql.expression import func from app import db class Po

我用的是烧瓶炼金术和烧瓶炼金术。我想用gin和to_向量在postgres中创建一个完整的测试搜索索引。目前,我正在尝试以下方法。我想这是我最接近我想要表达的东西,但是没有用

from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.schema import Index
from sqlalchemy.sql.expression import func

from app import db


class Post(db.Model):

    id = db.Column(db.Integer, primary_key=True)
    added = db.Column(db.DateTime, nullable=False)
    pub_date = db.Column(db.DateTime, nullable=True)
    content = db.Column(db.Text)

    @declared_attr
    def __table_args__(cls):
        return (Index('idx_content', func.to_tsvector("english", "content"), postgresql_using="gin"), )
这会引发以下错误

Traceback (most recent call last):
  File "./manage.py", line 5, in <module>
    from app import app, db
  File "/vagrant/app/__init__.py", line 36, in <module>
    from pep.models import *
  File "/vagrant/pep/models.py", line 8, in <module>
    class Post(db.Model):
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/flask_sqlalchemy.py", line 477, in __init__
    DeclarativeMeta.__init__(self, name, bases, d)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/api.py", line 48, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 222, in _as_declarative
    **table_kw)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/schema.py", line 326, in __new__
    table._init(name, metadata, *args, **kw)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/schema.py", line 393, in _init
    self._init_items(*args)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/schema.py", line 63, in _init_items
    item._set_parent_with_dispatch(self)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/events.py", line 235, in _set_parent_with_dispatch
    self._set_parent(parent)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/schema.py", line 2321, in _set_parent
    ColumnCollectionMixin._set_parent(self, table)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/schema.py", line 1978, in _set_parent
    self.columns.add(col)
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/sql/expression.py", line 2391, in add
    self[column.key] = column
  File "/home/vagrant/.virtualenvs/pep/local/lib/python2.7/site-packages/sqlalchemy/sql/expression.py", line 2211, in __getattr__
    key)
AttributeError: Neither 'Function' object nor 'Comparator' object has an attribute 'key'
然而,它不能像postgres(至少是9.1,因为我运行的是9.1)所期望的那样被调用。这一行创建SQL

CREATE INDEX content_index ON post USING gin (content)
而不是我想要的

CREATE INDEX content_index ON post USING gin(to_tsvector('english', content))

我打开了一张罚单,因为我认为这可能是一个缺陷/限制

现在,我已经添加了以下几行来手动执行,但是如果有“正确的”SQLAlchemy方法,我更愿意使用它

create_index = DDL("CREATE INDEX idx_content ON pep USING gin(to_tsvector('english', content));")
event.listen(Pep.__table__, 'after_create', create_index.execute_if(dialect='postgresql'))

关于SQLAlchemy bug跟踪器有一些有趣的讨论。看起来这是当前索引定义的一个限制。基本上,我的要求是允许索引成为表达式,而不仅仅是列名,但这目前不受支持。此票证正在跟踪此功能请求:。但是,这需要开发人员继续工作(已经有一段时间了)。

因此在sqlalchemy 0.9及更高版本中:

class Content(Base, ):
    __tablename__ = 'content'

    id = sa.Column(sa.Integer, primary_key=True)

    description = sa.Column(sa.UnicodeText, nullable=False, server_default='')
    @declared_attr
    def __table_args__(cls):
        return (sa.Index('idx_content',
                     sa.sql.func.to_tsvector("english", cls.description),
                     postgresql_using="gin"), )

值得注意的是,与第一个示例不同的是直接引用了列名,而不是在引号中提供的列名,因为这不起作用。

在我创建一些单列和多列索引时遇到了这个老问题。对于那些正在寻找使用列名的字符串表示创建这些索引的简单方法的人,这里有一种使用SQLAlchemy构造的方法

from sqlalchemy import Column, Index, Integer, String, text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func


Base = declarative_base()

def to_tsvector_ix(*columns):
    s = " || ' ' || ".join(columns)
    return func.to_tsvector('english', text(s))

class Example(Base):
    __tablename__ = 'examples'

    id = Column(Integer, primary_key=True)
    atext = Column(String)
    btext = Column(String)

    __table_args__ = (
        Index(
            'ix_examples_tsv',
            to_tsvector_ix('atext', 'btext'),
            postgresql_using='gin'
            ),
        )

那是什么SQLAlchemy版本?0.8.0测试版。不过,我也尝试了0.7.2(我认为是最新的稳定版本)。有一个与
column.key
vs
column.name
用法相关的修复程序,在0.8.0betath之后应用,这很好,谢谢-我会留意它的下一个beta/最终版本。我正在0.9.8上尝试,但没有。中断为:
sqlalchemy.exc.ArgumentError:无法将未命名列添加到列集合
2013年关闭/修复。
from sqlalchemy import Column, Index, Integer, String, text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func


Base = declarative_base()

def to_tsvector_ix(*columns):
    s = " || ' ' || ".join(columns)
    return func.to_tsvector('english', text(s))

class Example(Base):
    __tablename__ = 'examples'

    id = Column(Integer, primary_key=True)
    atext = Column(String)
    btext = Column(String)

    __table_args__ = (
        Index(
            'ix_examples_tsv',
            to_tsvector_ix('atext', 'btext'),
            postgresql_using='gin'
            ),
        )