Lewati ke isi

Mode Ketat

??? api "Dokumentasi API" [pydantic.types.Strict][pydantic.types.Ketat]

Secara default, Pydantic akan mencoba memaksakan nilai ke tipe yang diinginkan jika memungkinkan. Misalnya, Anda dapat meneruskan string "123" sebagai masukan ke bidang int , dan string tersebut akan dikonversi menjadi 123 . Perilaku pemaksaan ini berguna dalam banyak skenario — pikirkan: UUID, parameter URL, header HTTP, variabel lingkungan, masukan pengguna, dll.

Namun, ada juga situasi di mana hal ini tidak diinginkan, dan Anda ingin Pydantic melakukan kesalahan alih-alih memaksa data.

Untuk lebih mendukung kasus penggunaan ini, Pydantic menyediakan "mode ketat" yang dapat diaktifkan berdasarkan per model, per bidang, atau bahkan per panggilan validasi. Ketika mode ketat diaktifkan, Pydantic akan menjadi lebih lunak saat memaksa data, dan malah akan error jika tipe datanya tidak benar.

Berikut adalah contoh singkat yang menunjukkan perbedaan antara perilaku validasi dalam mode ketat dan mode default/"lax":

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
    x: int


print(MyModel.model_validate({'x': '123'}))  # lax mode
#> x=123

try:
    MyModel.model_validate({'x': '123'}, strict=True)  # strict mode
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyModel
    x
      Input should be a valid integer [type=int_type, input_value='123', input_type=str]
    """

Ada berbagai cara untuk mendapatkan validasi mode ketat saat menggunakan Pydantic, yang akan dibahas lebih detail di bawah ini:

Ketik paksaan dalam mode ketat

Untuk sebagian besar tipe, saat memvalidasi data dari python dalam mode ketat, hanya instance dari tipe persisnya yang diterima. Misalnya, saat memvalidasi bidang int , hanya contoh int yang diterima; melewatkan instance float atau str akan menghasilkan ValidationError .

Perhatikan bahwa kami lebih longgar saat memvalidasi data dari JSON dalam mode ketat. Misalnya, saat memvalidasi bidang UUID , instance str akan diterima saat memvalidasi dari JSON, namun tidak dari python:

import json
from uuid import UUID

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
    guid: UUID


data = {'guid': '12345678-1234-1234-1234-123456789012'}

print(MyModel.model_validate(data))  # OK: lax
#> guid=UUID('12345678-1234-1234-1234-123456789012')

print(
    MyModel.model_validate_json(json.dumps(data), strict=True)
)  # OK: strict, but from json
#> guid=UUID('12345678-1234-1234-1234-123456789012')

try:
    MyModel.model_validate(data, strict=True)  # Not OK: strict, from python
except ValidationError as exc:
    print(exc.errors(include_url=False))
    """
    [
        {
            'type': 'is_instance_of',
            'loc': ('guid',),
            'msg': 'Input should be an instance of UUID',
            'input': '12345678-1234-1234-1234-123456789012',
            'ctx': {'class': 'UUID'},
        }
    ]
    """

Untuk lebih jelasnya mengenai jenis apa saja yang diperbolehkan sebagai input pada mode ketat, Anda dapat meninjau Tabel Konversi .

Mode ketat dalam pemanggilan metode

Semua contoh yang disertakan sejauh ini mendapatkan validasi mode ketat melalui penggunaan strict=True sebagai argumen kata kunci pada metode validasi. Meskipun kami telah menunjukkan ini untuk BaseModel.model_validate , ini juga berfungsi dengan tipe arbitrer melalui penggunaan TypeAdapter :

from pydantic import TypeAdapter, ValidationError

print(TypeAdapter(bool).validate_python('yes'))  # OK: lax
#> True

try:
    TypeAdapter(bool).validate_python('yes', strict=True)  # Not OK: strict
except ValidationError as exc:
    print(exc)
    """
    1 validation error for bool
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

Perhatikan ini juga berfungsi bahkan ketika menggunakan tipe yang lebih "kompleks" di TypeAdapter :

from dataclasses import dataclass

from pydantic import TypeAdapter, ValidationError


@dataclass
class MyDataclass:
    x: int


try:
    TypeAdapter(MyDataclass).validate_python({'x': '123'}, strict=True)
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyDataclass
      Input should be an instance of MyDataclass [type=dataclass_exact_type, input_value={'x': '123'}, input_type=dict]
    """

Ini juga berfungsi dengan metode TypeAdapter.validate_json dan BaseModel.model_validate_json :

import json
from typing import List
from uuid import UUID

from pydantic import BaseModel, TypeAdapter, ValidationError

try:
    TypeAdapter(List[int]).validate_json('["1", 2, "3"]', strict=True)
except ValidationError as exc:
    print(exc)
    """
    2 validation errors for list[int]
    0
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    2
      Input should be a valid integer [type=int_type, input_value='3', input_type=str]
    """


class Model(BaseModel):
    x: int
    y: UUID


data = {'x': '1', 'y': '12345678-1234-1234-1234-123456789012'}
try:
    Model.model_validate(data, strict=True)
except ValidationError as exc:
    # Neither x nor y are valid in strict mode from python:
    print(exc)
    """
    2 validation errors for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    y
      Input should be an instance of UUID [type=is_instance_of, input_value='12345678-1234-1234-1234-123456789012', input_type=str]
    """

json_data = json.dumps(data)
try:
    Model.model_validate_json(json_data, strict=True)
except ValidationError as exc:
    # From JSON, x is still not valid in strict mode, but y is:
    print(exc)
    """
    1 validation error for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

Mode ketat dengan Field

Untuk masing-masing bidang pada model, Anda dapat mengatur strict=True pada bidang tersebut . Hal ini akan menyebabkan validasi mode ketat digunakan untuk bidang tersebut, meskipun metode validasi dipanggil tanpa strict=True .

Hanya kolom yang strict=True disetel yang akan terpengaruh:

from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
    name: str
    age: int
    n_pets: int


user = User(name='John', age='42', n_pets='1')
print(user)
#> name='John' age=42 n_pets=1


class AnotherUser(BaseModel):
    name: str
    age: int = Field(strict=True)
    n_pets: int


try:
    anotheruser = AnotherUser(name='John', age='42', n_pets='1')
except ValidationError as e:
    print(e)
    """
    1 validation error for AnotherUser
    age
      Input should be a valid integer [type=int_type, input_value='42', input_type=str]
    """

Perhatikan bahwa membuat bidang menjadi ketat juga akan memengaruhi validasi yang dilakukan saat membuat instance kelas model:

from pydantic import BaseModel, Field, ValidationError


class Model(BaseModel):
    x: int = Field(strict=True)
    y: int = Field(strict=False)


try:
    Model(x='1', y='2')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

Menggunakan Field sebagai anotasi

Perhatikan bahwa Field(strict=True) (atau dengan argumen kata kunci lainnya) dapat digunakan sebagai anotasi jika diperlukan, misalnya saat bekerja dengan TypedDict :

from typing_extensions import Annotated, TypedDict

from pydantic import Field, TypeAdapter, ValidationError


class MyDict(TypedDict):
    x: Annotated[int, Field(strict=True)]


try:
    TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for typed-dict
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

Mode ketat dengan Annotated[..., Strict()]

??? api "Dokumentasi API" [pydantic.types.Strict][pydantic.types.Ketat]

Pydantic juga menyediakan kelas Strict , yang dimaksudkan untuk digunakan sebagai metadata dengan kelas typing.Annotated; anotasi ini menunjukkan bahwa bidang yang diberi anotasi harus divalidasi dalam mode ketat:

from typing_extensions import Annotated

from pydantic import BaseModel, Strict, ValidationError


class User(BaseModel):
    name: str
    age: int
    is_active: Annotated[bool, Strict()]


User(name='David', age=33, is_active=True)
try:
    User(name='David', age=33, is_active='True')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for User
    is_active
      Input should be a valid boolean [type=bool_type, input_value='True', input_type=str]
    """

Faktanya, ini adalah metode yang digunakan untuk mengimplementasikan beberapa tipe strict-out-of-the-box yang disediakan oleh Pydantic, seperti StrictInt .

Mode ketat dengan ConfigDict

BaseModel

Jika Anda ingin mengaktifkan mode ketat untuk semua bidang pada tipe input kompleks, Anda dapat menggunakan ConfigDict(strict=True) di model_config :

from pydantic import BaseModel, ConfigDict, ValidationError


class User(BaseModel):
    model_config = ConfigDict(strict=True)

    name: str
    age: int
    is_active: bool


try:
    User(name='David', age='33', is_active='yes')
except ValidationError as exc:
    print(exc)
    """
    2 validation errors for User
    age
      Input should be a valid integer [type=int_type, input_value='33', input_type=str]
    is_active
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

!!! catatan Saat menggunakan strict=True melalui model_config model, Anda masih dapat mengganti ketatnya masing-masing bidang dengan menyetel strict=False pada masing-masing bidang:

```py
from pydantic import BaseModel, ConfigDict, Field


class User(BaseModel):
    model_config = ConfigDict(strict=True)

    name: str
    age: int = Field(strict=False)
```

Perhatikan bahwa mode ketat tidak diterapkan secara rekursif ke bidang model bertumpuk:

from pydantic import BaseModel, ConfigDict, ValidationError


class Inner(BaseModel):
    y: int


class Outer(BaseModel):
    model_config = ConfigDict(strict=True)

    x: int
    inner: Inner


print(Outer(x=1, inner=Inner(y='2')))
#> x=1 inner=Inner(y=2)

try:
    Outer(x='1', inner=Inner(y='2'))
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

(Ini juga berlaku untuk dataclasses dan TypedDict .)

Jika hal ini tidak diinginkan, Anda harus memastikan bahwa mode ketat diaktifkan untuk semua jenis yang terlibat. Misalnya, hal ini dapat dilakukan untuk kelas model dengan menggunakan kelas dasar bersama model_config = ConfigDict(strict=True) :

from pydantic import BaseModel, ConfigDict, ValidationError


class MyBaseModel(BaseModel):
    model_config = ConfigDict(strict=True)


class Inner(MyBaseModel):
    y: int


class Outer(MyBaseModel):
    x: int
    inner: Inner


try:
    Outer.model_validate({'x': 1, 'inner': {'y': '2'}})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    inner.y
      Input should be a valid integer [type=int_type, input_value='2', input_type=str]
    """

Kelas data dan TypedDict

Kelas data Pydantic berperilaku mirip dengan contoh yang ditunjukkan di atas dengan BaseModel , hanya saja alih-alih model_config Anda harus menggunakan argumen kata kunci config ke @pydantic.dataclasses.dataclass penghias.

Jika memungkinkan, Anda dapat mencapai mode ketat bersarang untuk kelas data vanilla atau subkelas TypedDict dengan memberi anotasi pada bidang dengan anotasi pydantic.types.Strict .

Namun, jika hal ini tidak memungkinkan (misalnya, ketika bekerja dengan tipe pihak ketiga), Anda dapat mengatur konfigurasi yang harus digunakan Pydantic untuk tipe tersebut dengan mengatur atribut __pydantic_config__ pada tipe:

from typing_extensions import TypedDict

from pydantic import ConfigDict, TypeAdapter, ValidationError


class Inner(TypedDict):
    y: int


Inner.__pydantic_config__ = ConfigDict(strict=True)


class Outer(TypedDict):
    x: int
    inner: Inner


adapter = TypeAdapter(Outer)
print(adapter.validate_python({'x': '1', 'inner': {'y': 2}}))
#> {'x': 1, 'inner': {'y': 2}}


try:
    adapter.validate_python({'x': '1', 'inner': {'y': '2'}})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for typed-dict
    inner.y
      Input should be a valid integer [type=int_type, input_value='2', input_type=str]
    """

TypeAdapter

Anda juga bisa mendapatkan mode ketat melalui penggunaan argumen kata kunci config ke kelas TypeAdapter :

from pydantic import ConfigDict, TypeAdapter, ValidationError

adapter = TypeAdapter(bool, config=ConfigDict(strict=True))

try:
    adapter.validate_python('yes')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for bool
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

@validate_call

Mode ketat juga dapat digunakan dengan dekorator @validate_call dengan meneruskan argumen kata kunci config :

from pydantic import ConfigDict, ValidationError, validate_call


@validate_call(config=ConfigDict(strict=True))
def foo(x: int) -> int:
    return x


try:
    foo('1')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for foo
    0
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

本文总阅读量