SQLAlchemy: как расширить гибридные атрибуты? - PullRequest
0 голосов
/ 05 октября 2018

Я работаю с базой данных MSSQL без контроля над настройкой БД или данными (только для чтения) в ней.Одна таблица представлена ​​в SQLAlchemy следующим образом:

class pdAnlage(pdBase):
    __tablename__ = "Anlage"
    typ = Column(CHAR(4), primary_key=True)
    nummer = Column(CHAR(4), primary_key=True)

При доступе к базе данных мне нужно свойство "name", которое представляет собой просто сочетание "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-сервер должен объединить всестолбцы «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 и typ могут содержать конечные пробелы в БД.Но у свойства name не должно быть пробелов, особенно перед точкой.Поэтому я попытался переписать гибридное свойство name следующим образом:

    @hybrid_property
    def name(self):
        return self.typ.rstrip() + '.' + self.nummer.rstrip()

Это не работает, потому что SQLAlchemy не знает, как преобразовать метод python rstrip () в функцию MSSQL RTRIM ().Как мне это сделать?

1 Ответ

0 голосов
/ 05 октября 2018

Вы можете реализовать пользовательский компаратор , который обрабатывает строковые операнды особым образом (и другие при необходимости):

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...