首页 > 编程问答 >赋值时的 Pydantic 类型转换

赋值时的 Pydantic 类型转换

时间:2024-07-21 05:30:08浏览次数:10  
标签:python python-3.x validation pydantic-v2

我想使用 Pydantic 使用特定的哈希函数将明文字符串密码转换为分配时字节类型的哈希值。

这是一个最小的示例,显示了我当前(不起作用)的方法。不过我对Pydantic还没有很深入的了解。

import bcrypt
from pydantic import BaseModel, field_validator

def hash_password(password: str) -> bytes:
    return bcrypt.hashpw(password.encode('utf-8'), salt = bcrypt.gensalt())

class Password(BaseModel):
    hash_value: bytes
    
    @field_validator("hash_value")
    @classmethod
    def set_password(cls, plain_password: str) -> bytes:
        return hash_password(plain_password)
    
class Settings:
    DEFAULT_PASSWORD = "my_plain_password"
    
settings = Settings()
    
password_doc = Password(
    hash_value = settings.DEFAULT_PASSWORD
)

一开始我不小心将hash_values声明为str,没有意识到 hashpw 的返回值是bytes类型。这以某种方式起作用了, hash_password 函数在分配时被调用。然而,发生的所有隐式类型转换都使我的散列密码无效。

现在的问题是 Pydantic 在赋值时需要一个字节值,并在将字符串传递给 settings.DEFAULT_PASSWORD 方法之前将字符串 set_password 隐式转换为字节值,即使这个需要字符串类型。

我的错误消息:

Traceback (most recent call last):
  File "xxx", line 20, in <module>
    password_doc = Password(
                   ^^^^^^^^^
  File "xxx", line 176, in __init__
    self.__pydantic_validator__.validate_python(data, self_instance=self)
  File "xxx", line 13, in set_password
    return hash_password(plain_password)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "xxx", line 5, in hash_password
    return bcrypt.hashpw(password.encode('utf-8'), salt = bcrypt.gensalt())
                         ^^^^^^^^^^^^^^^
AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'?

问题在于正在尝试在已经为字节类型的值上使用 field_validator 。Pydantic 首先将 settings.DEFAULT_PASSWORD (一个字符串)转换为 hash_value 字段定义的类型(字节)。然后,它尝试对已转换的值应用验证器,从而导致错误,因为正在尝试对字节字符串进行编码。

有两种主要方法可以解决此问题:

1. 使用根验证器:

可以使用根验证器来对整个模型数据进行操作,并在创建实例后设置 hash_value

import bcrypt
from pydantic import BaseModel, root_validator

def hash_password(password: str) -> bytes:
    return bcrypt.hashpw(password.encode('utf-8'), salt=bcrypt.gensalt())

class Password(BaseModel):
    hash_value: bytes

    @root_validator(pre=True)
    def hash_password(cls, values: dict) -> dict:
        if "hash_value" in values and isinstance(values["hash_value"], str):
            values["hash_value"] = hash_password(values["hash_value"])
        return values

class Settings:
    DEFAULT_PASSWORD = "my_plain_password"


settings = Settings()

password_doc = Password(hash_value=settings.DEFAULT_PASSWORD)
print(password_doc)

这种方法使用 root_validator 并在 pre=True 中指定,以便在对字段进行任何其他验证之前运行。验证器检查 hash_value 是否是字符串;如果是,则对其进行哈希处理,然后更新模型中的值。

2. 使用专用字段进行明文密码:

创建单独的字段来处理明文密码,并在模型内部进行哈希处理。

import bcrypt
from pydantic import BaseModel, validator

def hash_password(password: str) -> bytes:
    return bcrypt.hashpw(password.encode('utf-8'), salt=bcrypt.gensalt())

class Password(BaseModel):
    plain_password: str = None  # 可选字段,用于存储明文密码
    hash_value: bytes

    @validator("hash_value", pre=True, always=True)
    def set_hash_from_password(cls, value, values: dict) -> bytes:
        if "plain_password" in values and values["plain_password"]:
            return hash_password(values["plain_password"])
        elif isinstance(value, str):
            return hash_password(value)
        return value

class Settings:
    DEFAULT_PASSWORD = "my_plain_password"


settings = Settings()

password_doc = Password(plain_password=settings.DEFAULT_PASSWORD)
print(password_doc)

此方法使用 plain_password 字段来接收明文密码。 @validator 装饰的 hash_value 字段确保始终使用 plain_password 或直接传递的值生成哈希值。

选择哪种方法取决于的具体需求和偏好。使用根验证器可以提供更大的灵活性来处理更复杂的逻辑,而专用字段提供了一种更清晰的方式来分离明文和哈希密码。

标签:python,python-3.x,validation,pydantic-v2
From: 78773899

相关文章

  • Python:对很高维的矩阵进行对角化?
    目前我正在研究一个涉及对角化矩阵以获得特征值和特征向量的问题。但现在我想将问题扩展到200,000x200,000的尺寸。我查找了如何将矩阵存储在numpy中,有人建议使用PyTables。看起来很有希望。但我想知道哪里有工具可以帮助对PyTables中的矩阵存储进行对角化。......
  • 除了curses之外,是否有一个python包可以轻松控制终端的输出?
    我现在正在处理一些小项目,我对GUI的偏好是终端中漂亮的文本界面。我宁愿不强迫用户处理Windowscurses二进制文件,所以我正在寻找不同的选项。我已经发现了asciimatics,但我想考虑所有可能的选择。如果有人有任何经验或知道解决此用例的包,我将不胜感激。谢谢你说的没错......
  • 当值来自函数 python unittest 时,如何模拟全局变量
    我必须在python中模拟全局变量,但变量值来自另一个函数。当我导入文件时,这个函数正在运行,但我想要那里的模拟值。secrets.pyimporttracebackimportloggingimportboto3importosimportjsonlogger=logging.getLogger()logger.setLevel(logging.INFO)secret_......
  • 使用 python print 和 gdb 时出现 BrokenPipeError
    我正在尝试在Linux中运行应用程序并使用Python生成输入:python3-c'print(".....")'|./someapp但出现下一个错误:Exceptionignoredin:<_io.TextIOWrappername='<stdout>'mode='w'encoding='utf-8'>BrokenPipeError:......
  • python 舰队容器
    我正在尝试使用容器在flet中制作一个菜单,它应该是半透明的,但其中的项目不是。我尝试将opacity=1分配给元素,但没有成功-它们与容器一样透明感谢任何帮助我的代码:nickname=ft.TextField(label="xxx",hint_text="xxx")column=ft.Column(controls=[nickname......
  • Python应用程序跨子包共享的配置文件
    我正在构建一个应用程序来控制一些硬件。我在包中实现了不同类型的硬件:电机和测量设备。我的文件结构如下:name_of_my_app/__init__.pymain.pyconfig.iniCONFIG.pymotors/__init__.pyone_kind_of_motor.pymeasurement_devices/......
  • python中时间序列数据的梯度计算
    我正在尝试编写一个函数,它可以从最适合下面的线返回梯度dataframe在浏览了谷歌的几个资源之后,我仍然不确定这是如何完成的。我明白最佳拟合线的计算公式为:y=mx+b将因变量(y)设置为foos,将自变量(x)设置为DateTimeDatafram......
  • 调试用 C 编写的 Python 扩展
    我非常熟悉编写C代码,并且很擅长编写Python代码。我正在尝试学习如何用C编写可以从OSX10.15.7上的Python-3.9.X调用的模块。我已经得到了几个“helloworld”类型的示例,但是对于复杂的示例,我正在努力弄清楚如何调试我编写的C扩展。MWE:src/add.c//......
  • 具有块大小选项的 Python pandas read_sas 因索引不匹配而失败并出现值错误
    我有一个非常大的SAS文件,无法容纳在我的服务器内存中。我只需要转换为镶木地板格式的文件。为此,我使用pandas中chunksize方法的read_sas选项分块读取它。它主要是在工作/做它的工作。除此之外,一段时间后它会失败并出现以下错误。此特定SAS文件有794......
  • 使用 requests 包 python 时打开文件太多
    我正在使用Pythonrequests包向API发出大量请求。然而,在某些时候,我的程序由于“打开的文件太多”而崩溃。当我明确关闭我的会话时,我真的不知道这是怎么回事。我使用以下代码:importrequestsimportmultiprocessingimportnumpyasnps=requests.session()s.keep......