我正在使用MSSQL数据库,无法控制数据库设置或数据库中的(只读)数据。像这样在SQLAlchemy中表示一个表:
class pdAnlage(pdBase):
__tablename__ = "Anlage"
typ = Column(CHAR(4), primary_key=True)
nummer = Column(CHAR(4), primary_key=True)
在访问数据库时,我需要一个属性“名称”,该属性只是“ typ”和“ nummer”的连接,并且它们之间有一个点。所以我这样做了:
@hybrid_property
def name(self):
return self.typ + '.' + self.nummer
看起来简单,并且可以正常工作。不过,有两个警告,一个是一般性,另一个是特殊性。一般情况:表很大,我想对Anlage.name进行查询,如下所示:
db.query(Anlage).filter(Anlage.name.like('A%.B'))
db.query(Anlage).filter(Anlage.name == 'X.Y')
这有效,但是效率低下,因为SQL Server首先必须在进行测试之前将(大)表的所有“ typ”和“ nummer”列连接在一起。所以我定义了这样的类方法:
@classmethod
def name_like(self, pattern):
p = pattern.split('.', 2)
if len(p) == 1 or not p[1]:
return self.typ.like(p[0])
else:
return and_(self.typ.like(p[0]), self.nummer.like(p[1]))
这不是很优雅,但是可以很好地完成工作。重载“ ==”和“ like()”会更好,有没有办法做到这一点?
由于特殊情况:名称和类型列都可以在数据库中包含尾随空格。但是name属性不能有空格,尤其是点号之前不能有空格。所以我试图像这样重写名称混合属性:
@hybrid_property
def name(self):
return self.typ.rstrip() + '.' + self.nummer.rstrip()
这不起作用,因为SQLAlchemy不知道如何将rstrip()python方法转换为MSSQL RTRIM()函数。我该怎么办?
答案 0 :(得分:1)
您可以实现一个custom comparator来以特殊方式(以及其他必要方式)处理字符串操作数:
from sqlalchemy.ext.hybrid import Comparator
_sep = '.'
def _partition(s):
typ, sep, nummer = s.partition(_sep)
return typ, nummer
class NameComparator(Comparator):
def __init__(self, typ, nummer):
self.typ = typ
self.nummer = nummer
super().__init__(func.rtrim(typ) + _sep + func.rtrim(nummer))
def operate(self, op, other, **kwgs):
if isinstance(other, str):
typ, nummer = _partition(other)
expr = op(self.typ, typ, **kwgs)
if nummer:
expr = and_(expr, op(self.nummer, nummer, **kwgs))
return expr
else:
# Default to using the "slow" method of concatenating first that
# hides the columns from the index created for the primary key.
return op(self.__clause_element__(), other, **kwgs)
并与您的混合属性一起使用:
class pdAnlage(Base):
__tablename__ = "Anlage"
typ = Column(CHAR(4), primary_key=True)
nummer = Column(CHAR(4), primary_key=True)
@hybrid_property
def name(self):
return self.typ.rstrip() + _sep + self.nummer.rstrip()
@name.comparator
def name(cls):
return NameComparator(cls.typ, cls.nummer)