Как заставить объект Flask-SQLAlchemy загружать дочерние отношения для шаблона Jinja?

У меня есть базовые модели для пользователей и сообщений. В моей модели пользователя у меня есть

posts = db.relationship('Post', backref='user', lazy='dynamic')

Однако, когда я делаю что-то вроде

return render_template('user.html', users=users)

Я хочу делать такие вещи, как

{% for user in users %}
    <tr>
        <td>{{ user.id }}</td>
        <td>{{ user.posts|length }}</td>
    </tr>
{% endfor %}

К сожалению, это не работает. Сообщения - это запрос, а не объект b/c lazy='dynamic'. Я могу сделать это, если изменю lazy='joined', но тогда будут загружаться все сообщения для пользователей каждый раз, когда я запрашиваю пользователя.

Я попытался добавить .options(joinedload('posts')) к моему запросу, но он сказал, что

InvalidRequestError: 'User.posts' не поддерживает заполнение объектов — нельзя применять активную загрузку.

Любая помощь приветствуется.


person Patrick Yan    schedule 25.02.2014    source источник


Ответы (3)


Просто удалите аргумент lazy="dynamic", и вы сможете без проблем использовать joinedload. (Помните, когда вы делаете это, вы открываете себя для трудно диагностируемых проблем с запросом N+1. Если вам всегда будут нужны сообщения, используйте вместо этого joined).

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

class User(db.Model):
    posts = db.relationship('Post', backref='user')

class Post(db.Model):
    # etc.

User.posts_query = db.relationship(Post, lazy='dynamic')
person Sean Vieira    schedule 25.02.2014

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

{% for user in users %}
    <tr>
        <td>{{ user.id }}</td>
        <td>{{ user.posts.count() }}</td>
    </tr>
{% endfor %}
person DazWorrall    schedule 25.02.2014
comment
Это приведет к N+1 запросам (где 1 получает пользователей, а N — количество возвращаемых пользователей), потому что count будет идти и извлекать общее количество сообщений для этого пользователя из базы данных для каждого пользователя. - person Sean Vieira; 25.02.2014
comment
@SeanVieira Если мне просто нужно подсчитать элементы, мне лучше использовать ленивый по умолчанию (то есть select), а затем использовать length Jinja, чтобы получить количество в шаблоне, вместо использования lzay='dynamic' с count()? - person Grey Li; 30.07.2017
comment
На самом деле вам гораздо лучше полностью использовать отдельный запрос, если все, что вам нужно, это User, post count - если вы собираетесь использовать все сообщения при рендеринге шаблона, тогда да, | length - это то, что вам нужно, вместо того, чтобы выдавать count для каждого пользователя. . - person Sean Vieira; 03.08.2017

Вы должны выполнить .first() или .all() для вашего объекта запроса, чтобы получить фактический объект/список. например
users = User.query.all()

person Eric Dong    schedule 10.12.2017