SqlAlchemy TypeDecorator, column_expression и агрегатные функции - PullRequest
1 голос
/ 28 апреля 2020

Я нашел решение моей проблемы на SqlAlchemy docs

from sqlalchemy.dialects.postgresql import BYTEA

class PGPString(BYTEA):
    def __init__(self, passphrase, length=None):
        super(PGPString, self).__init__(length)
        self.passphrase = passphrase

    def bind_expression(self, bindvalue):
        # convert the bind's type from PGPString to
        # String, so that it's passed to psycopg2 as is without
        # a dbapi.Binary wrapper
        bindvalue = type_coerce(bindvalue, String)
        return func.pgp_sym_encrypt(bindvalue, self.passphrase)

    def column_expression(self, col):
        return func.pgp_sym_decrypt(col, self.passphrase)

Он выдает запрос, как и ожидалось:

 SELECT pgp_sym_decrypt(employees.salary, $1) AS salary_crypt FROM employees

Но, чем если бы я попытался обернуть столбец PGPString любым агрегированным выражением

 session.query(func.sum(Employee.salary))

запрос ниже будет произведен с помощью выражения выше:

 SELECT pgp_sym_decrypt(sum(salary_rate_employee_link.salary_crypt), $1) AS sum_1 

и что я ожидал

SELECT sum(pgp_sym_decrypt(salary_rate_employee_link.salary_crypt, $1)) AS sum_1 

Как вы можете видеть порядок функций БД это не то, что я ожидал. Итак, я хотел бы найти простой способ заставить его работать правильно

1 Ответ

0 голосов
/ 30 апреля 2020

Ну, единственное решение, к которому я пришел сейчас, это своего рода обходной путь

class PGPString(BYTEA):
    def __init__(self, passphrase, length=None):
        super(PGPString, self).__init__(length)
        self.passphrase = passphrase

    def bind_expression(self, bindvalue):
        # convert the bind's type from PGPString to
        # String, so that it's passed to psycopg2 as is without
        # a dbapi.Binary wrapper
        bindvalue = type_coerce(bindvalue, String)
        return func.pgp_sym_encrypt(bindvalue, self.passphrase)

    def column_expression(self, col):
        clauses = getattr(col, 'clauses', [col])
        columns = [
            db.func.pgp_sym_decrypt(clause, self.passphrase)
            for clause in clauses
        ]

        if isinstance(col, FunctionElement):
            if col.base_columns:
                base_column, *base_columns = col.base_columns
                columns = type(base_column)(*columns)
                for base_column in base_columns:
                    columns = type(base_column)(columns)
        else:
            columns = columns[0]

        return columns
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...