我一直在尝试使用 Flask-SQLAlchemy 创建一个博客。我有两个数据库对象:User 和 Post,它们彼此之间具有双向关系。每当我的主页尝试访问帖子的作者属性时,都会出现以下错误:
sqlalchemy.orm.exc.DetachedInstanceError:父实例
未绑定到会话;的延迟加载操作 属性“作者”无法继续(此错误的背景位于: https://sqlalche.me/e/20/bhk3)
这就是我设置数据库的方式:
db = SQLAlchemy(model_class=Base, session_options={
'expire_on_commit': False
})
db.init_app(app)
db.session.expire_on_commit = False
class Post(db.Model):
id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("user.id"))
author: Mapped["User"] = relationship('User', back_populates="posts")
date: Mapped[str] = mapped_column(String(250), nullable=False)
title: Mapped[str] = mapped_column(String(250), nullable=False)
subtitle: Mapped[str] = mapped_column(String(250), nullable=False)
body: Mapped[str] = mapped_column(Text, nullable=False)
image_url: Mapped[str] = mapped_column(String(250), nullable=False)
class User(UserMixin, db.Model):
id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False)
email: Mapped[str] = mapped_column(String(100), nullable=False)
password: Mapped[str] = mapped_column(String(100), nullable=False)
name: Mapped[str] = mapped_column(String(100), nullable=False)
posts: Mapped[List["Post"]] = relationship('Post', back_populates="author")
with app.app_context():
db.create_all()
我的家乡路线呈现最近的帖子:
@app.route("/")
def home():
logged_in = current_user.is_active
is_admin = False
if current_user.is_active:
is_admin = current_user.id == 1
with app.app_context():
posts = db.session.execute(db.select(Post).order_by(Post.id))
posts = list(posts.scalars())[::-1]
return render_template("index.html", recent_posts=posts[0:(recent_posts_size)], year=year, logged_in=logged_in, is_admin=is_admin)
这是我主页的 HTML:
{% extends 'components/template.html' %}
{% block web_title %}Rephy's Blog{% endblock %}
{% block style %}
header.masthead {
background-image: url('static/img/home-bg.jpg')
}
{% endblock %}
{% block title %}Rephy's Blog{% endblock %}
{% block subtitle %}Welcome! Dive into the world of recent computer science breakthroughs, new technologies, and so much more with my blog!{% endblock %}
{% block content %}
<div class="container px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-md-10 col-lg-8 col-xl-7">
<!-- Post preview-->
{% for post in recent_posts %}
<div class="post-preview">
<a href="{{ url_for('post', id=post.id) }}">
<h2 class="post-title">{{ post.title }}</h2>
<h3 class="post-subtitle">{{ post.subtitle }}</h3>
</a>
<p class="post-meta">
Posted by
<a href="#!">{{ post.author }}</a>
on {{ post.date }}
{% if is_admin %}
<a href="{{ url_for('delete_post', id=post.id) }}">✘</a>
{% endif %}
</p>
</div>
<!-- Divider-->
<hr class="my-4" />
{% endfor %}
{% if is_admin %}
<div class="d-flex justify-content-end mb-4"><a class="btn btn-primary text-uppercase" href="{{ url_for('new_post') }}">Create New Post</a></div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
我尝试将“expire-on-commit”设置为 False 以消除错误,但这仍然不起作用。另外,如果我尝试使用不存在的列,Jinja 会完全忽略它。使用除作者之外的任何现有列都可以完美地工作。
如果你能帮忙,那就太好了。
在模板中,您正在访问
Post.author
。但是,由于 author
是一种关系,因此当您查询 Post
对象时,不会自动获取。当模板中发生访问时,SQLAlchemy 会发出一个查询来获取与 User
对应的 Post.author
实例,但会话不再活动,因此您会得到 DetachedInstanceError
。
为了防止这种情况,可以在关系上设置lazy='joined',以便在获取
Post.author
时始终获取Post
author: Mapped["User"] = relationship('User', lazy='joined', back_populates="posts")
或将其指定为相关查询中的选项:
from sqlalchemy.orm import joinedload
...
posts = db.session.execute(db.select(Post).options(joinedload(Post.author)).order_by(Post.id))
上面的示例应该可以解决问题,但请注意,在 SQLAlchemy 中有很多关系加载方法 - 值得您熟悉它们,以确保您为手头的任务选择正确的方法。