【问题标题】:SQLAlchemy mismatches column with orm querySQLAlchemy 不匹配列与 orm 查询
【发布时间】:2020-12-22 21:08:59
【问题描述】:

我在开发过程中使用 SQLAlchemy 在本地查询 SQLite 数据库的 Flask 应用程序中有订阅和订阅者表/模型的简单关系。

订阅通过如下定义的subscriber_id 字段与订阅者建立关系:

subscriber_id = Column(db.Integer, ForeignKey('subscriber.id'))

这两个对象都有一个定义为String(36) 的字段,当它们在数据库中创建时,我将在其中写入一个 UUID4 字符串。它们分别是subscription_idsubscriber_id,像这样:

@dataclass
class Subscription(db.Model):
    __tablename__ = 'subscription'

    subscription: str
    subscriber: str

    id = Column(db.Integer, primary_key=True)
    subscription_id = Column(
        db.String(36),
        default=generate_uuid,  # a function that returns a uuid4
        unique=True
    )
    subscriber_id = Column(db.Integer, ForeignKey('subscriber.id'))

在使用 PyTest 执行测试期间,我遇到了一个奇怪的行为,其中 Subscription 实例的 subscriber_id 返回的不是类中定义的整数,而是一个 UUID。

测试运行一个查询订阅者的方法,然后尝试获取其订阅:

subscriber = Subscriber.query.filter(
    Subscriber.subscriber_id == subscriber_id
).first()

assert subscriber is not None

subscriptions = Subscription.query.filter(
    Subscription.subscriber_id == subscriber_id
).all()

subscriber_id在执行期间是1,从数据库中检索订阅者。

然而,第二个查询不起作用,即使 subscriber_id 是订阅者的 id 的外键。

如果我用另一个不带过滤器的查询替换第二个查询,它可以工作。但是后来我看到了这个问题,返回的subscriber_id 不是整数,而是 UUID:

raise Exception(Subscription.query.first().subscriber_id)
        subscriptions = Subscription.query.filter(
            Subscription.subscriber_id == subscriber_id
        ).all()
    
>       raise Exception(Subscription.query.first().subscriber_id)
E       Exception: 59ebfdd6-1cbc-4748-94e1-955ef55c380c

我在这里没有考虑到 SQLAlchemy 的特殊行为吗?可能是表/字段的命名约定?

【问题讨论】:

  • Subscription.id 已经是主键。候选键Subscription.subscription_id的用途是什么?
  • 另外,检查你的工作。 subscriber_id 不会返回 uuid,但 subscription_id 会。这里没有命名约定。
  • @GordThompson 目的不是在请求 URI 或正文中显示实际 ID,因为它是一个 API。这是一种替代ID。我计划在内部调用中使用实际的id。我会用更多信息更新原始帖子。
  • 哦等等,这是 SQLite。你有explicitly enabled foreign key support吗?
  • @GordThompson 这真的很奇怪。当显式启用外键支持时,我得到一个 IntegrityException。

标签: python unit-testing sqlalchemy pytest


【解决方案1】:

虽然 SQLite 很出色,但它有两个特性使其与大多数其他 SQL 实现完全“不同”:

  1. SQLite 不严格强制列类型,它只识别列"affinities"。因此,可以将列声明为INT,在其中插入一个字符串(如 UUID 的字符串表示),SQLite 不一定会引发错误。

  2. SQLite 接受外键约束声明,但它ignores them by default。在使用 SQLite 并期望外键“正常工作”时,这可能会导致混淆。

这两种行为都对这个问题起作用。将 (UUID) 字符串插入到本来应该是整数的列中时出现编码错误,并且没有强制执行外键约束导致该错误被忽视。

道德:使用 SQLite 并欣赏它所能提供的东西,但也要注意它的特殊“个性”(就像任何其他 SQL 方言一样)。

【讨论】:

    猜你喜欢
    • 2020-05-02
    • 2017-04-03
    • 1970-01-01
    • 1970-01-01
    • 2021-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多