gpt4 book ai didi

python - SQLAlchemy CRUD 操作与结果分配不一致或没有结果分配

转载 作者:行者123 更新时间:2023-12-01 06:38:50 28 4
gpt4 key购买 nike

我有一个应用程序,我希望用户能够为博客添加书签/取消添加书签,但取消添加书签后,我不想删除该书签记录。因此,我的 Bookmark 模型上有一个 is_bookmarked 属性来确定书签是否处于事件/非事件状态。

在我的测试文件中,我有

def test_unbookmark_a_blog_do_assign(session):
blog = create_blog(session)

bookmark = toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 1

toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 0

该测试通过。然而,以下不会。 (唯一的区别是我没有为 toggle_bookmark 的结果分配变量。)

def test_unbookmark_a_blog_no_assign(session):
blog = create_blog(session)

toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 1

toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 0

第二个断言失败 assert len(blog.bookmarks) == 0。原因是 blog._bookmarks[0].is_bookmarked 不会在 toggle_bookmark 函数之外更新,并且仍然是 True,使其在 中可用>博客.书签。 (定义见下文)

对于上下文,我使用经典映射:

@dataclass
class Bookmark:
is_bookmarked: bool = True
blog_id: Optional[int] = None

@dataclass
class Blog:
_bookmarks: List[Bookmark] = field(default_factory=list)

def add_bookmark(self, bookmark):
self._bookmarks.append(bookmark)

@property
def bookmarks(self):
return [bookmark for bookmark in self._bookmarks if bookmark.is_bookmarked]

...

blog_table = Table(
"blog",
metadata,
Column("id", Integer, primary_key=True, index=True))

bookmark_table = Table(
"bookmark",
metadata,
Column("id", Integer, primary_key=True, index=True),
Column("is_bookmarked", Boolean, default=True),
Column("blog_id", ForeignKey("blog.id"), nullable=True),
)

...

mapper(
Blog,
blog_table,
properties={
"_bookmarks": relationship(Bookmark, back_populates="blog"),
},
)

mapper(
Bookmark,
bookmark_table,
properties={
"blog": relationship(Blog, back_populates="_bookmarks"),
},
)

toggle_bookmark 函数:

def toggle_bookmark(db_session, *, blog_id):
blog = db_session.query(Blog).get(blog_id)
bookmark = db_session.query(Bookmark).filter(
Bookmark.blog_id == blog_id
).one_or_none()

if bookmark is None:
bookmark = Bookmark()
blog.add_bookmark(bookmark)
db_session.add(blog)
db_session.commit()
return bookmark

bookmark.is_bookmarked = not bookmark.is_bookmarked
db_session.add(bookmark)
db_session.commit()
return bookmark

我真的很困惑......我的直觉告诉我,当查询被评估时它有一些事情要做,但我还没有找到任何证据来支持它。任何帮助表示赞赏。提前致谢!

完整示例:

from dataclasses import dataclass, field
from typing import Optional, List
from sqlalchemy import (
create_engine, MetaData, Table, Column, Integer, Boolean, ForeignKey)
from sqlalchemy.orm import mapper, relationship, sessionmaker

@dataclass
class Bookmark:
is_bookmarked: bool = True
blog_id: Optional[int] = None

@dataclass
class Blog:
_bookmarks: List[Bookmark] = field(default_factory=list)

def add_bookmark(self, bookmark):
self._bookmarks.append(bookmark)

@property
def bookmarks(self):
return [bookmark for bookmark in self._bookmarks if bookmark.is_bookmarked]

engine = create_engine("sqlite:///")
metadata = MetaData(bind=engine)

blog_table = Table(
"blog",
metadata,
Column("id", Integer, primary_key=True, index=True))

bookmark_table = Table(
"bookmark",
metadata,
Column("id", Integer, primary_key=True, index=True),
Column("is_bookmarked", Boolean, default=True),
Column("blog_id", ForeignKey("blog.id"), nullable=True),
)

metadata.create_all()

mapper(
Blog,
blog_table,
properties={
"_bookmarks": relationship(Bookmark, back_populates="blog"),
},
)

mapper(
Bookmark,
bookmark_table,
properties={
"blog": relationship(Blog, back_populates="_bookmarks"),
},
)

def toggle_bookmark(db_session, *, blog_id):
blog = db_session.query(Blog).get(blog_id)
bookmark = db_session.query(Bookmark).filter(
Bookmark.blog_id == blog_id
).one_or_none()

if bookmark is None:
bookmark = Bookmark()
blog.add_bookmark(bookmark)
db_session.add(blog)
db_session.commit()
return bookmark

bookmark.is_bookmarked = not bookmark.is_bookmarked
db_session.add(bookmark)
db_session.commit()
return bookmark

def create_blog(session):
blog = Blog()
session.add(blog)
session.commit()
return blog

def test_unbookmark_a_blog_do_assign(session):
blog = create_blog(session)

bookmark = toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 1

toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 0

def test_unbookmark_a_blog_no_assign(session):
blog = create_blog(session)

toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 1

toggle_bookmark(session, blog_id=blog.id)
assert len(blog.bookmarks) == 0

Session = sessionmaker()

test_unbookmark_a_blog_do_assign(Session())
test_unbookmark_a_blog_no_assign(Session())

最佳答案

核心问题是:

class Bookmark:
is_bookmarked: bool = True # <-- This here

经典映射不会在现有类属性上安装检测,因此对实例的 is_bookmarked 的任何更改都不会保留。从这里开始,如果没有分配,测试就会从数据库中读取状态,其中保存其默认值True。通过分配,实例保留在测试范围内,因此保留在 Session 中,后面的查询将返回现有的修改后的实例。

如果使用 default=,您会在 SQLAlchemy、数据类和 field() 中遇到类似的问题:

>>> from dataclasses import dataclass, field
>>> @dataclass
... class C:
... f: bool = field(default=True)
...
>>> C.f
True

克服这种情况的解决方案是使用 field()default_factory= 来表示 is_bookmarked:

@dataclass
class Bookmark:
is_bookmarked: bool = field(default_factory=lambda: True)
...

因为在最近的 Python 中,field() 在类中作为属性不可见,并且映射可以安装检测。

关于python - SQLAlchemy CRUD 操作与结果分配不一致或没有结果分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59550560/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com