是否可以在pydantic中更改输出别名?

2024-10-02 10:21:17 发布

您现在位置:Python中文网/ 问答频道 /正文

设置:

# Pydantic Models

class TMDB_Category(BaseModel):
    name: str = Field(alias="strCategory")
    description: str = Field(alias="strCategoryDescription")


class TMDB_GetCategoriesResponse(BaseModel):
    categories: list[TMDB_Category]


@router.get(path="category", response_model=TMDB_GetCategoriesResponse)
async def get_all_categories():
    async with httpx.AsyncClient() as client:
        response = await client.get(Endpoint.GET_CATEGORIES)
        return TMDB_GetCategoriesResponse.parse_obj(response.json())

问题:
创建响应时正在使用别名,我希望避免使用它。我只需要这个别名来正确映射传入的数据,但在返回响应时,我希望使用实际的字段名

实际响应:

{
  "categories": [
    {
      "strCategory": "Beef",
      "strCategoryDescription": "Beef is ..."
    },
    {
      "strCategory": "Chicken",
      "strCategoryDescription": "Chicken is ..."
    }
}

预期响应:

{
  "categories": [
    {
      "name": "Beef",
      "description": "Beef is ..."
    },
    {
      "name": "Chicken",
      "description": "Chicken is ..."
    }
}

Tags: namegetisresponsedescriptiontmdbclassbasemodel
2条回答

切换别名和字段名,并使用allow_population_by_field_namemodel config选项:

class TMDB_Category(BaseModel):
    strCategory: str = Field(alias="name")
    strCategoryDescription: str = Field(alias="description")

    class Config:
        allow_population_by_field_name = True

让别名配置要返回的字段的名称,但允许allow_population_by_field_name能够解析字段使用不同名称的数据

另一种选择(可能不会像流行的那样)是使用除pydantic之外的反序列化库。例如,Dataclass向导库就是支持此特定用例的库。如果需要Field(alias=...)提供的相同往返行为,可以将all参数传递给json_field函数。请注意,有了这样一个库,您确实失去了执行完整类型验证的能力,这可以说是pydantic最大的优势之一;但是,它会以类似于pydantic的方式执行类型转换。我觉得验证没有那么重要还有一些原因,我在下面列出了这些原因

为什么我会认为数据验证是一个好的 一般功能:

  • 如果您自己构建和传递输入,那么您很可能相信您知道自己在做什么,并且正在传递正确的数据类型
  • 如果您从另一个API获得输入,那么假设该API有合适的文档,您可以从它们的文档中获取一个示例响应,并使用该响应来建模您的类结构。如果API清楚地记录了其响应结构,则通常不需要任何验证
  • 数据验证需要时间,因此与只执行类型转换并捕获可能发生的任何错误而不事先验证输入类型相比,数据验证会稍微减慢过程

为了证明这一点,这里有一个使用dataclass-wizard库(依赖于使用dataclasses而不是pydantic模型)的上述用例的简单示例:

from dataclasses import dataclass

from dataclass_wizard import JSONWizard, json_field


@dataclass
class TMDB_Category:
    name: str = json_field('strCategory')
    description: str = json_field('strCategoryDescription')


@dataclass
class TMDB_GetCategoriesResponse(JSONWizard):
    categories: list[TMDB_Category] 

运行它的代码如下所示:

input_dict = {
  "categories": [
    {
      "strCategory": "Beef",
      "strCategoryDescription": "Beef is ..."
    },
    {
      "strCategory": "Chicken",
      "strCategoryDescription": "Chicken is ..."
    }
  ]
}

c = TMDB_GetCategoriesResponse.from_dict(input_dict)
print(repr(c))
# TMDB_GetCategoriesResponse(categories=[TMDB_Category(name='Beef', description='Beef is ...'), TMDB_Category(name='Chicken', description='Chicken is ...')])

print(c.to_dict())
# {'categories': [{'name': 'Beef', 'description': 'Beef is ...'}, {'name': 'Chicken', 'description': 'Chicken is ...'}]}
测量性能

如果有人好奇的话,我已经设置了一个快速的基准测试来比较pydantic与DataClass的反序列化和序列化时间:

from dataclasses import dataclass
from timeit import timeit

from pydantic import BaseModel, Field

from dataclass_wizard import JSONWizard, json_field


# Pydantic Models
class Pydantic_TMDB_Category(BaseModel):
    name: str = Field(alias="strCategory")
    description: str = Field(alias="strCategoryDescription")


class Pydantic_TMDB_GetCategoriesResponse(BaseModel):
    categories: list[Pydantic_TMDB_Category]


# Dataclasses
@dataclass
class TMDB_Category:
    name: str = json_field('strCategory', all=True)
    description: str = json_field('strCategoryDescription', all=True)


@dataclass
class TMDB_GetCategoriesResponse(JSONWizard):
    categories: list[TMDB_Category]


# Input dict which contains sufficient data for testing (100 categories)
input_dict = {
  "categories": [
    {
      "strCategory": f"Beef {i * 2}",
      "strCategoryDescription": "Beef is ..." * i
    }
    for i in range(100)
  ]
}

n = 10_000

print('=== LOAD (deserialize)')
print('dataclass-wizard: ',
      timeit('c = TMDB_GetCategoriesResponse.from_dict(input_dict)',
             globals=globals(), number=n))
print('pydantic:         ',
      timeit('c = Pydantic_TMDB_GetCategoriesResponse.parse_obj(input_dict)',
             globals=globals(), number=n))

c = TMDB_GetCategoriesResponse.from_dict(input_dict)
pydantic_c = Pydantic_TMDB_GetCategoriesResponse.parse_obj(input_dict)

print('=== DUMP (serialize)')
print('dataclass-wizard: ',
      timeit('c.to_dict()',
             globals=globals(), number=n))
print('pydantic:         ',
      timeit('pydantic_c.dict()',
             globals=globals(), number=n))

以及基准测试结果(在Mac OS Big Sur、Python 3.9.0上测试):

=== LOAD (deserialize)
dataclass-wizard:  1.742989194
pydantic:          5.31538175
=== DUMP (serialize)
dataclass-wizard:  2.300118940
pydantic:          5.582638598

在他们的文档中,pydantic声称总体上是最快的库,但要证明不是这样很简单。如您所见,对于上述数据集,pydantic在反序列化和序列化过程中大约慢2倍。但值得注意的是pydantic已经相当快了

相关问题 更多 >

    热门问题