Как заставить объект Flask-SQLAlchemy загружать дочерние элементы отношений для шаблона Jinja?
-
21-12-2019 - |
Вопрос
У меня есть базовые модели для User и Post.В моей пользовательской модели у меня есть
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 %}
К сожалению, это не работает.Posts - это запрос, а не объект b/c из lazy='dynamic'
.Я могу сделать вышеописанное, если изменю lazy='joined'
, но тогда он загружал бы все сообщения для пользователей в любое время, когда я запрашиваю пользователя.
Я попробовал добавить .options(joinedload('posts'))
на мой запрос, но в нем говорилось, что
InvalidRequestError Недействительный запрос:'User.posts' не поддерживает заполнение объектов - быстрая загрузка не может быть применена.
Мы ценим любую помощь.
Решение
Просто снимите 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')
Другие советы
Это объект запроса да, но у этих объектов запроса есть методы, которые могли бы вам помочь.Специально для этого варианта использования вы можете сделать:
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.posts.count() }}</td>
</tr>
{% endfor %}
Вы должны выполнить .first() или .all() для вашего объекта запроса, чтобы получить фактический объект/ список.например,
users = User.query.all()