首页 > 编程问答 >Python SQLAlchemy 2.0 使用 dataclass_transform 非必需字段类型

Python SQLAlchemy 2.0 使用 dataclass_transform 非必需字段类型

时间:2024-07-28 07:20:21浏览次数:7  
标签:python sqlalchemy python-typing python-dataclasses

我刚刚在一个新项目上安装了 SQLAlchemy 2.0,我正在尝试使我的模型尽可能类型安全。

通过使用 @typing_extensions.dataclass_transform ,我已经能够实现我想要实现的大部分目标类型检查,但是当前所有字段都被标记为不需要。

例如:


@typing_extensions.dataclass_transform(kw_only_default=True)
class Base(DeclarativeBase):
    pass

class TestModel(Base):
    __tablename__ = "test_table"
    name: Mapped[str]
    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    external_id: Mapped[int] = mapped_column(
        ForeignKey("external.id"), nullable=False
    )

def test_test_model(session: Session) -> None:
    TEST_NAME = "name"
    external = External()
    session.add(external)
    session.commit()
    model1 = TestModel() # Intellisense shows error because "name" is required

    model2 = TestModel(name=TEST_NAME, external_id=external.id). # no error
    session.add(model2)
    session.commit() # model commits successfully

    model3 = TestModel(name=TEST_NAME) # No intellisense error, despite "external_id" being required
    session.add(model3)
    session.commit(). # error when saving because of missing "external_id"

在上面的示例中,如何将 external_id 的类型设置为必需?


问题在于,当在 mapped_column() 中设置 nullable=False 时,这只会影响数据库级别的约束,而不会影响 Python 类型提示。

为了使 external_id 在类型提示中成为必需项,需要在 dataclass 定义中提供默认值,即使该默认值在数据库级别无关紧要。以下是一些方法:

1. 使用 Field default_factory :

from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
from typing import Optional
from sqlalchemy.orm import Session
from typing_extensions import dataclass_transform

@dataclass_transform(kw_only_default=True)
class Base(DeclarativeBase):
    pass

class External(Base):
    __tablename__ = "external"
    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)

class TestModel(Base):
    __tablename__ = "test_table"
    name: Mapped[str] 
    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    external_id: Mapped[int] = mapped_column(ForeignKey("external.id"), nullable=False)

def test_test_model(session: Session) -> None:
    TEST_NAME = "name"
    external = External()
    session.add(external)
    session.commit()

    # 使用默认工厂函数提供默认值
    model1 = TestModel(name=TEST_NAME, external_id=external.id) 
    session.add(model1)
    session.commit() 

2. 使用 __post_init__ :

from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
from typing import Optional
from sqlalchemy.orm import Session
from typing_extensions import dataclass_transform

@dataclass_transform(kw_only_default=True)
class Base(DeclarativeBase):
    pass

class External(Base):
    __tablename__ = "external"
    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)

class TestModel(Base):
    __tablename__ = "test_table"
    name: Mapped[str]
    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    external_id: Mapped[int] = mapped_column(ForeignKey("external.id"), nullable=False)

    def __post_init__(self):
        if self.external_id is None:
            raise ValueError("external_id is required")

def test_test_model(session: Session) -> None:
    TEST_NAME = "name"
    external = External()
    session.add(external)
    session.commit()

    model1 = TestModel(name=TEST_NAME, external_id=external.id)
    session.add(model1)
    session.commit() 

这两种方法都可以在类型提示中将 external_id 设置为必需项,同时仍然允许在数据库级别强制执行非空约束。选择哪种方法取决于的偏好和具体用例。

标签:python,sqlalchemy,python-typing,python-dataclasses
From: 75300029

相关文章

  • 当读取字节时,Python 将 \n 解释为 0xd0
    换行符被解释为Ð(0xd0)而不是(0x0a),我真的不知道如何解决这个问题。我也不知道从哪里开始。当尝试使用此代码时,它将0x0a读为0xd0。withopen(path,"rb")asroot:b_arr=root.read()+b""print("Processing...")buffer=""b_arr......
  • 在Python 3中获取范围的特定形式
    当获取range(1,100)时,我得到:[1,2,3,4,5...99]我需要像这个范围的zip之类的东西:[50,49,51,48,52,47,53...99]如何获取它?背景:这都是关于比特币谜题66的。首先我做了对过去已知的私钥进行线性回归预测,直到第65题为止。我......
  • Python:如何使用pyaudio或sounddevice等库进行自动录音?
    我想做一个项目,需要满足以下录音要求:程序启动后,会在后台不断检测麦克风的声音,当声音分贝大于一定值时打开录音流级别,当分贝低于一定级别时关闭录音流并保存为wav文件。我知道原理,但我无法使用这些库来实现。我想实现以上结果使用Python实现自动录音以下代......
  • 当我们创建一个在 https 服务器上获取和发送数据的 python 应用程序时,我们应该如何处
    python脚本使用Urllib3,我的服务器是在Node.js上编写的脚本。我担心(并且不清楚)证书:我是否需要将我的python应用程序上的证书另存为变量?例如我这样做了,http=urllib3.PoolManager(cert_reqs="CERT_REQUIRED",ca_certs='client-cert.pem')并且我不知道......
  • 使用 Python 进行 Web 抓取以获取数据 NoneType ERROR
    我正在努力为我的学校项目获取美元和价格。所以我决定为此使用网络抓取,但我有一个问题。当我尝试在服务器上使用我的代码时,它给我NoneType错误。它可以在googlecolab上使用,但我无法在我的电脑或服务器上使用。我该如何解决这个问题?网页抓取代码;defdolar():he......
  • Python 请求 - response.json() 未按预期工作
    我正在尝试从Python的requests模块调用API。在邮递员上,返回的响应标头中的Content-Type是application/json;charset=utf-8,响应json数据是我期望的样子。但是,在python上的API的get方法之后运行response.json()会抛出错误simplejson.errors......
  • Python 中的“样板”代码?
    Google有一个Python教程,他们将样板代码描述为“不幸的”,并提供了以下示例:#!/usr/bin/python#importmodulesusedhere--sysisaverystandardoneimportsys#Gatherourcodeinamain()functiondefmain():print'Hellothere',sys.argv[1]#Command......
  • Python 3.9.1 中的 collections.abc.Callable 是否有 bug?
    Python3.9包含PEP585并弃用typing模块中的许多类型,转而支持collections.abc中的类型,现在它们支持__class_getitem__例如Callable就是这种情况。对我来说,typing.Callable和collections.abc.Ca......
  • 列表子类的 Python 类型
    我希望能够定义列表子类的内容必须是什么。该类如下所示。classA(list):def__init__(self):list.__init__(self)我想包含键入内容,以便发生以下情况。importtypingclassA(list:typing.List[str]):#Maybesomethinglikethisdef__init__(self):......
  • Python 中类型友好的委托
    考虑以下代码示例defsum(a:int,b:int):returna+bdefwrap(*args,**kwargs):#delegatetosumreturnsum(*args,**kwargs)该代码运行良好,只是类型提示丢失了。在Python中使用*args,**kwargs来实现​​委托模式是很常见的。如果有一种方法可......