Как отсортировать по гибридному свойству в отношении один-к-одному в SQLAlchemy со ссылкой на одну и ту же таблицу дважды? - PullRequest
6 голосов
/ 05 мая 2020

У меня есть таблица с именами User и Document. Таблица User содержит:

first_name
…

Document содержит:

id
created_by_id
deleted_by_id
…

Я хочу отсортировать таблицу документа по first_name. Поэтому я создал отношения creation_user и deletion_user. Оба внешних ключа created_by_id и deleted_by_id ссылаются на одну и ту же таблицу. Гибридное свойство created_by должно быть отсортировано по:

creation_user = relationship("User", foreign_keys=[created_by_id], lazy="joined")
deletion_user = relationship("User", foreign_keys=[deleted_by_id], lazy="joined")

@hybrid_property
def created_by(self):
    if self.creation_user:
        return self.creation_user.first_name
    else:
        return None

@created_by.expression
    def created_by(cls):
        return User.first_name

К сожалению, SQLAlchemy не соответствует правильному пользователю, и результирующий запрос SQL выглядит следующим образом:

SELECT … 
FROM   document 
       LEFT OUTER JOIN [user] AS user_1 
                    ON user_1.id = document.created_by_id 
       LEFT OUTER JOIN [user] AS user_2 
                    ON user_2.id = document.deleted_by_id 
WHERE  document.id = ? 
ORDER  BY [user].first_name ASC 

Is есть способ использовать гибридное свойство или гибридное выражение (см. sqlalchemy docs ), чтобы оператор ORDER BY разрешался в ORDER BY user_2.first_name ASC?

Ответы [ 2 ]

1 голос
/ 18 июня 2020

Решение, которое является «полу-SQLAlchemy», заключается в использовании text следующего вида:

def sort_docs(ids, order)
   if order.lower() not in ['asc', 'desc']:
      return None
   statement = """
   SELECT documents.id AS documents_id, documents.created_by_id AS 
    documents_created_by_id, documents.deleted_by_id AS documents_deleted_by_id, 
    users_1.id AS users_1_id, users_1.first_name AS users_1_first_name, users_2.id AS 
    users_2_id, users_2.first_name AS users_2_first_name 
   FROM documents 
   LEFT OUTER JOIN users AS users_1 ON users_1.id = documents.created_by_id 
   LEFT OUTER JOIN users AS users_2 ON users_2.id = documents.deleted_by_id
   WHERE documents_id in :ids
   ORDER BY users_1.first_name {}
   """.format(order)
   sql_statement = text(statement)
   result = db.session.query(Document).from_statement(sql_statement.bindparams(ids=ids))
   return result       

Таким образом вы получите необходимые документы как объект модели Document, отсортированный первым пользователем имя в запрошенном порядке.

1 голос
/ 14 мая 2020

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

@created_by.expression
def created_by(cls):
    return sql.select([User.first_name]).where(User.id == cls.created_by_id)

Хотя сгенерированный запрос добавляет дополнительный подзапрос, а не ссылается на таблицу с псевдонимом:

SELECT documents.id AS documents_id, documents.created_by_id AS documents_created_by_id, documents.deleted_by_id AS documents_deleted_by_id, users_1.id AS users_1_id, users_1.first_name AS users_1_first_name, users_2.id AS users_2_id, users_2.first_name AS users_2_first_name 
FROM documents 
LEFT OUTER JOIN users AS users_1 ON users_1.id = documents.created_by_id 
LEFT OUTER JOIN users AS users_2 ON users_2.id = documents.deleted_by_id 
ORDER BY (SELECT users.first_name 
          FROM users 
          WHERE users.id = documents.created_by_id)

Другой подход заключался бы в определении порядка в отношении:

creation_user = relationship("User", foreign_keys=[created_by_id], lazy="joined", order_by="User.first_name")

Это произвело SQL, указанное в вопросе, и устраняет необходимость в гибридных атрибутах:

SELECT documents.id AS documents_id, documents.created_by_id AS documents_created_by_id, documents.deleted_by_id AS documents_deleted_by_id, users_1.id AS users_1_id, users_1.first_name AS users_1_first_name, users_2.id AS users_2_id, users_2.first_name AS users_2_first_name 
FROM documents 
LEFT OUTER JOIN users AS users_1 ON users_1.id = documents.created_by_id 
LEFT OUTER JOIN users AS users_2 ON users_2.id = documents.deleted_by_id 
ORDER BY users_1.first_name

Однако если вам когда-нибудь понадобится другой порядок, вам нужно будет переопределить стратегию загрузки - см. этот ответ .

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