Flask SQLAlchemy получает столбцы из двух соединенных сопоставленных объектов в результате запроса - PullRequest
0 голосов
/ 30 октября 2018

У меня есть таблица, MenuOptions, которая представляет любую опцию, найденную в раскрывающемся списке в моем приложении. Каждый параметр можно определить по меню, частью которого он является (например, MenuOptions.menu_name), и по конкретному значению этого параметра (MenuOptions.option_value).

Эта таблица имеет отношения по всей моей БД и не использует внешние ключи, поэтому у меня возникли проблемы с ее преобразованием в SQLAlchemy.

В SQL это будет так же просто, как:

SELECT 
    *
FROM
    document
        JOIN
    menu_options ON menu_options.option_menu_name = 'document_type'
        AND menu_options.option_value = document.document_type_id

чтобы определить это отношение. Однако, когда я делаю это в SQLAlchemy, у меня возникают проблемы, потому что я не могу четко отобразить это отношение без внешних ключей. В SQLAlchemy лучшее, что я сделал на данный момент:

the_doc = db.session.query(Document, MenuOptions).filter(
    Document.id == document_id
).join(
    MenuOptions,
    and_(
        MenuOptions.menu_name == text('"document_type"'),
        MenuOptions.value == Document.type_id
    )
).first()

, который работает и возвращает правильные значения, но возвращает их в виде списка двух отдельных объектов модели, так что я должен ссылаться на свойства сопоставленного документа через the_doc[0], а свойства сопоставленного меню - через the_doc[1]

Можно ли как-нибудь вернуть это отношение как один объект запроса со всеми его свойствами без использования внешних ключей или любых ForeignKeyConstraint в моей модели? Я пробовал add_columns и add_entity, но по сути получаю тот же результат.

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

В итоге я использовал немного другой подход, используя association_proxy, но если вы попали сюда из Google, то это должно вам помочь. В следующем примере я сохраняю document_type_id в таблице document и храню соответствующие значения для этого идентификатора в таблице с именем menu_options. Обычно для этого вы используете внешние ключи, но наша menu_options по сути является внутренней таблицей поиска и содержит связи с несколькими другими таблицами, поэтому внешние ключи не являются чистым решением.

Сначала установив отношение через свойство primaryjoin, затем используя associationproxy, я могу сразу загрузить document_type на основе document_type_id с помощью следующего кода:

from sqlalchemy import and_
from sqlalchemy.ext.associationproxy import association_proxy

class Document(db.Model):
    __tablename__ = "document"
    document_type_id = db.Column(db.Integer)
    document_type_proxy = db.relationship(
        "MenuOptions",
        primaryjoin=(
            and_(
                MenuOptions.menu_name=='document_type', 
                foreign('Document.document_type_id')==MenuOptions.value
            )
        ),
        lazy="immediate"
        viewonly=True
    )

Если все, что вам нужно, это сопоставленные отношения без использования внешних ключей в вашей базе данных, то это будет хорошо. Однако, если вы хотите иметь доступ к удаленному атрибуту (в данном случае document_type) напрямую как атрибуту исходного класса (в данном случае Document), тогда вы можете использовать association_proxy, чтобы сделать это путем просто передавая имя сопоставленного отношения и имя удаленного свойства:

    document_type = association_proxy("document_type_proxy", "document_type")
0 голосов
/ 30 октября 2018

вы можете использовать with_entities

entities = [getattr(Document, c) for c in Document.__table__.columns.keys()] + \
           [getattr(MenuOptions, c) for c in MenuOptions.__table__.columns.keys()]

session.query(Document, MenuOptions).filter(
    Document.id == document_id
).join(
    MenuOptions,
    and_(
        MenuOptions.menu_name == text('"document_type"'),
        MenuOptions.value == Document.type_id
    )
).with_entities(*entities)
...