字典键和值的 Pydantic 可重用验证

Pydantic reusable Validation for Dictionary's keys and values

如何验证输入以获得以下 Dict 通过!

d = dict()
d['en'] = 'English content'
d['it'] = 'Italian content'
d['es'] = 'Spanish content'
print(d)
# {'en': 'English content', 'it': 'Italian content', 'es': 'Spanish content'}

在此示例中,密钥是 ISO 639-1 codes 使用 pycountry python 包。

code = 'en'
pycountry.languages.get(alpha_2=code.upper()).alpha_2 # = 'en'

重点是如何使用 pydantic 可重用验证器或任何其他方法来验证 keys? 并验证 valuesstr 还是 int? Pydantic 模型 schema 应该与此示例 相似 :

# products/model.py
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from custom_field import Translatable
Base = declarative_base()
class Model(Base):
    __tablename__ = "products"

    id = Column(Integer, unique=True, index=True)
    name = Column(Translatable())
    price = Column(Integer)

# products/pydantic.py
from pydantic import BaseModel
import custom_pydantic_field

class BaseSchema(BaseModel):
    id: int

class CreateSchema(BaseSchema):
    name: custom_pydantic_field.translatable
    price: int

记住在其他 models/schemas.

中的可重用性

创建华丽的定制class

# validators/translated_field.py
from typing import Dict
from pydantic import ValidationError
from pydantic.error_wrappers import ErrorWrapper
import pycountry

class Translatable(Dict):
    """
    Validate Translation Dict Field (Json) where Language is Key and Translation as Value
        Languages : ISO 639-1 code
        Translation : Int, str, None

    ref:
    - https://pydantic-docs.helpmanual.io/usage/types/#classes-with-__get_validators__

    By: Khalid Murad
    """

    @property
    def __translation_interface__(self):
        return self.dict()

    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, base_dictionary):
        result = dict()
        dictionary = dict()
        errors = []

        dictionary = base_dictionary

        for key in dictionary:
            try:
                parsed_language = pycountry.languages.get(alpha_2=key.upper())
            except ValueError as exc:
                errors.append(ErrorWrapper(Exception(f"Invalid language: {key}."), loc="language"))
            if not parsed_language:
                errors.append(ErrorWrapper(Exception(f"Invalid language: {key}."), loc="language"))
            if isinstance(dictionary[key], int | str | None):
                result[key] = dictionary[key]
            else:
                errors.append(ErrorWrapper(Exception(f"Invalid content for language: {key}."), loc=("language","content")))

        if errors:
            raise ValidationError(
            errors,
            cls,
            )

        return cls(result)

然后在您的 schema/pydantic 模型中使用它,例如:

# products/pydantic.py
from pydantic import BaseModel
from validators.translated_field import Translatable

class BaseSchema(BaseModel):
    id: int

class CreateSchema(BaseSchema):
    name: Translatable
    ...your code

并在 SQLALchemy 模型中使用正常的 JSON 字段!

# products/model.py
...
from sqlalchemy import Column, JSON
...
class Model(Base):
    name = Column(JSON, nullable=True)
    ...