Почему в SQLAlchemy мой load_only не фильтрует указанные мной столбцы? - PullRequest
0 голосов
/ 13 мая 2018

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

db.session.query(User).all()

Сгенерированные запросы:

<User(email='howard@howard.com', fullname='Howard', company='howard', address='None', password='howard')>,  <User(email='emailhoward', fullname='None', company='None', address='None', password='passwordhoward')>

Это логично, так как я извлекаю все из таблицы. Однако, когда я пытаюсь использовать load_only, чтобы специально выбрать один столбец, в данном случае, столбец электронной почты. Код, который я использовал:

db.session.query(User).options(load_only(User.address)).all()
db.session.query(User).options(load_only('email')).all()

Обе команды дают одинаковые результаты:

<User(email='howard@howard.com', fullname='Howard', company='howard', address='None', password='howard')>,<User(email='emailhoward', fullname='None', company='None', address='None', password='passwordhoward')>

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

db.session.query(User.email).select_from(User).filter_by(email=email).first()[0]

он волшебным образом возвращает только один столбец для меня. Мне нужно было использовать load_only, так как у меня есть динамические таблицы, в которых я хочу повторно использовать одну и ту же функцию, а не поддерживать множество наборов функций. Кто-нибудь может посоветовать, в чем проблема с командой load_only, или я что-то не так делаю?

Спасибо.

1 Ответ

0 голосов
/ 14 мая 2018

Нет проблем, просто недоразумение;

<User(email='howard@howard.com', fullname='Howard', company='howard', address='None', password='howard')>

- это строковое представление вашего User объекта модели, и это метод User.__repr__(), который вытягивает отложенные столбцы при обращении к ним.

Используя load_only(), вы определяете набор столбцов для первоначальной загрузки для сущности, одновременно откладывая все остальные. Но откладывание столбца не означает, что он каким-то образом непригоден или содержит какой-то маркер «нет значения» (на самом деле это так, под капотом). При первом обращении к каждому отложенному атрибуту SQLAlchemy выдаст SELECT, чтобы получить его значение. Это должно быть видно из журналов:

In [7]: u = session.query(User).options(load_only(User.email)).first()
2018-05-14 16:04:49,218 INFO sqlalchemy.engine.base.Engine SELECT user.id AS user_id, user.email AS user_email 
FROM user
 LIMIT ? OFFSET ?
2018-05-14 16:04:49,218 INFO sqlalchemy.engine.base.Engine (1, 0)

In [8]: u.fullname
2018-05-14 16:04:53,773 INFO sqlalchemy.engine.base.Engine SELECT user.fullname AS user_fullname 
FROM user 
WHERE user.id = ?
2018-05-14 16:04:53,773 INFO sqlalchemy.engine.base.Engine (2,)
Out[8]: 'Bar'

Вы можете проверить, был ли столбец отложен, используя API проверки. InstanceState.unloaded содержит набор ключей, которые не имеют загруженного значения. Используя это, вы можете изменить User.__repr__ на что-то вроде:

class User(Base):
    ...

    def __repr__(self):
        state = inspect(self)
        def ga(attr):
            return (repr(getattr(self, attr))
                    if attr not in state.unloaded
                    else "<deferred>")

        attrs = " ".join([f"{attr.key}={ga(attr.key)}"
                          for attr in state.attrs])
        return f"<User {attrs}>"

В качестве альтернативы вы можете выполнить итерацию по InstanceState.attrs с отображением AttributeState.loaded_value, что соответствует символу NO_VALUE , если значение не было загружено:

class User(Base):
    ...

    def __repr__(self):
        state = inspect(self)    
        attrs = " ".join([f"{attr.key}={attr.loaded_value!r}"
                          for attr in state.attrs])
        return f"<User {attrs}>"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...