← Semua pembelajaran / Python untuk AI Engineer
Fase 2 · Type Hints & Pydantic

Pydantic — Models

Cara kerja model Pydantic secara mendalam: konstruksi, serialisasi, konfigurasi, dan pola yang sering dipakai.

Sumber asli pydantic.dev Resmi Rangkuman ~7 menit baca

Intisari

  • Field() menambahkan batasan (panjang, rentang, pola) sekaligus deskripsi yang ikut ke JSON Schema.
  • model_config = ConfigDict(...) mengatur perilaku seluruh model — mode strict, larangan field asing, alias.
  • model_copy(update={...}) membuat salinan dengan perubahan — jangan mutasi objek yang sudah divalidasi.
  • Alias memisahkan nama field JSON dari nama atribut Python (camelCase ↔ snake_case).
  • extra="forbid" menolak field yang tidak dikenal — penangkap salah ketik yang sangat efektif.

Field() — batasan dan metadata

from pydantic import BaseModel, Field

class Tiket(BaseModel):
    judul: str = Field(min_length=1, max_length=200)
    prioritas: int = Field(default=3, ge=1, le=5)
    email: str = Field(pattern=r"^[^@]+@[^@]+\.[^@]+$")
    tag: list[str] = Field(default_factory=list, max_length=10)
    catatan: str | None = Field(default=None, description="Catatan internal")
UntukParameterArti
Angkage / le≥ dan ≤
gt / lt> dan <
multiple_ofKelipatan
allow_inf_nanIzinkan inf / NaN
Stringmin_length / max_lengthPanjang
patternRegex
strip_whitespacePangkas spasi otomatis
Koleksimin_length / max_lengthJumlah elemen
MetadatadescriptionIkut ke JSON Schema
examplesContoh nilai
aliasNama alternatif di JSON

description bukan sekadar komentar. Ia masuk ke JSON Schema, yang berarti: muncul di dokumentasi /docs FastAPI, dan dibaca LLM saat model dipakai sebagai definisi tool. Deskripsi yang jelas di sini langsung meningkatkan akurasi pemanggilan tool.

model_config

from pydantic import BaseModel, ConfigDict

class Ketat(BaseModel):
    model_config = ConfigDict(
        strict=True,                # jangan konversi tipe otomatis
        extra="forbid",             # tolak field yang tidak dikenal
        frozen=True,                # immutable setelah dibuat
        str_strip_whitespace=True,  # pangkas spasi semua field str
        validate_assignment=True,   # validasi ulang saat atribut diubah
        populate_by_name=True,      # terima nama field ASLI meski ada alias
    )

    nama: str
    umur: int

Perilaku extra

NilaiField asingKapan dipakai
"ignore" (default)Dibuang diam-diamMembaca API pihak ketiga yang sering menambah field
"forbid"Error validasiKonfigurasi & request internal — menangkap salah ketik
"allow"Disimpan sebagai atributJarang; data yang bentuknya benar-benar dinamis
class Settings(BaseModel):
    model_config = ConfigDict(extra="forbid")
    max_token: int = 4096

Settings(max_tokens=8192)     # ← salah ketik ('tokens') langsung ketahuan

Serialisasi lanjutan

t.model_dump()
t.model_dump(mode="json")            # datetime → string ISO, dst — siap json.dumps
t.model_dump(exclude_none=True)
t.model_dump(exclude_unset=True)     # hanya field yang benar-benar diisi pemanggil
t.model_dump(exclude_defaults=True)  # hanya yang berbeda dari default
t.model_dump(include={"judul", "prioritas"})
t.model_dump(by_alias=True)          # pakai nama alias, bukan nama Python

Bedanya exclude_unset dan exclude_defaults:

t = Tiket(judul="A", prioritas=3)     # 3 kebetulan sama dengan default

t.model_dump(exclude_unset=True)      # {'judul': 'A', 'prioritas': 3}  ← diisi eksplisit
t.model_dump(exclude_defaults=True)   # {'judul': 'A'}                  ← nilainya = default

exclude_unset=True adalah pola yang tepat untuk endpoint PATCH.

Menyalin, bukan memutasi

t2 = t.model_copy(update={"prioritas": 5})
t3 = t.model_copy(deep=True)           # salin juga objek bersarangnya

Mengubah atribut secara langsung (t.prioritas = 99) tidak divalidasi kecuali kamu menyalakan validate_assignment=True. Kebiasaan yang lebih aman: perlakukan objek yang sudah tervalidasi sebagai read-only, dan buat salinan saat perlu berubah.

Alias

from pydantic import BaseModel, Field

class Respons(BaseModel):
    id_pengguna: int = Field(alias="userId")
    dibuat_pada: str = Field(alias="createdAt")

r = Respons.model_validate({"userId": 1, "createdAt": "2026-01-01"})
r.id_pengguna              # 1 — nama Python tetap snake_case
r.model_dump(by_alias=True)  # {'userId': 1, 'createdAt': '...'}

Untuk mengubah semua field sekaligus tanpa menulis alias satu per satu:

from pydantic.alias_generators import to_camel

class Respons(BaseModel):
    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)

    id_pengguna: int
    dibuat_pada: str

Tipe khusus bawaan

from pydantic import BaseModel, EmailStr, HttpUrl, SecretStr, PositiveInt
from datetime import datetime
from uuid import UUID

class Akun(BaseModel):
    id: UUID
    email: EmailStr           # butuh: uv add "pydantic[email]"
    situs: HttpUrl
    api_key: SecretStr        # tidak muncul saat di-print / log
    kuota: PositiveInt
    dibuat: datetime          # menerima string ISO-8601, mengeluarkan datetime

SecretStr layak jadi kebiasaan. Saat di-print atau masuk log, isinya tampil sebagai **********. Ambil nilai aslinya secara eksplisit dengan .get_secret_value() — jadi kebocoran ke log tidak bisa terjadi tanpa sengaja.

Union yang dibedakan (discriminated union)

from typing import Literal
from pydantic import BaseModel, Field

class BlokTeks(BaseModel):
    type: Literal["text"]
    text: str

class BlokTool(BaseModel):
    type: Literal["tool_use"]
    name: str
    input: dict

class Respons(BaseModel):
    content: list[BlokTeks | BlokTool] = Field(discriminator="type")

Pydantic memakai field type untuk memilih model yang tepat — lebih cepat dan pesan errornya jauh lebih jelas dibanding mencoba semua kemungkinan satu per satu. Bentuk ini persis seperti struktur content pada respons API Claude.

Memvalidasi tanpa membuat class

from pydantic import TypeAdapter

adapter = TypeAdapter(list[Tiket])
tiket = adapter.validate_python(data_json)     # validasi list langsung

TypeAdapter(dict[str, int]).validate_python({"a": "1"})   # {'a': 1}

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.