Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. BaseModel
- 2.2. Turlarni keltirish va qat'iy rejim
- 2.3. Field va Annotated
- 2.4. Validatorlar
- 2.5. ValidationError
- 2.6. Serializatsiya va sxema
- 2.7. Narxi va chegarasi
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — BaseModel asoslari
- Misol 2 — Field cheklovlari va validatorlar
- Misol 3 — Serializatsiya, alias va sxema
- Misol 4 — Amaliy: API chegarasidan domen modeliga
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
13.8-dars: Runtime validatsiya (pydantic bilan tanishuv)
13-QISM — TURLAR TIZIMI · 8-dars
1. Kirish va motivatsiya
13.7-darsda mypy sizning kodingizni tekshirdi. Lekin tashqaridan keladigan ma'lumot — JSON so'rov, forma, konfiguratsiya fayli, boshqa servisning javobi — kod emas: mypy uni ko'rmaydi.
def buyurtma_yarat(xom: dict) -> Buyurtma:
return Buyurtma(id=xom["id"], summa=Decimal(xom["summa"]))Tashqi tomon "summa": "ikki yuz ming" yuborsa, mypy jim; xato ish vaqtida, Decimal ichida chiqadi — chalkash xabar bilan va foydalanuvchiga tushunarsiz shaklda.
Real vaziyat. To'lov API si amount maydonini "250000.00" satr sifatida kutardi. Mijoz ilovasi yangilanishdan keyin 250000.0 (son) yubora boshladi — Python kodi Decimal(float) orqali 250000.00000000000582... hosil qildi va hisob-kitoblar tiyin darajasida buzildi. Xato uch hafta sezilmadi.
Yechim — chegarada ish vaqtida validatsiya: tashqi ma'lumotni ishonchli turlarga aylantirish va noto'g'ri bo'lsa aniq xato qaytarish. pydantic shu ish uchun standart vosita bo'lib qoldi: u annotatsiyalarni shartnoma sifatida ishlatadi.
Bu darsda:
-
BaseModel: annotatsiyalardan validatsiya - Turlarni keltirish (coercion) va qat'iy (strict) rejim
Fieldcheklovlari vaAnnotated- Validatorlar:
field_validator,model_validator -
ValidationErrortuzilishi va foydalanuvchiga xabar - Serializatsiya:
model_dump,model_dump_json, alias, JSON Schema - Narxi va chegarasi: qayerda
pydantic, qayerdadataclass - Amaliy: API chegarasidan domen modeliga
2. Nazariya — chuqur tushuntirish
2.1. BaseModel
from pydantic import BaseModel
class Manzil(BaseModel):
shahar: str
kocha: str | None = None
class Buyurtma(BaseModel):
id: int
mijoz: str
summa: Decimal
manzil: Manzil | None = None- Annotatsiyalar — ish vaqtida majburlanadigan shartnoma
- Ichma-ich modellar avtomatik validatsiya qilinadi
- Noto'g'ri ma'lumot —
ValidationError(hamma xatolar ro'yxati bilan) pydanticv2 yadrosi Rust'da yozilgan — validatsiya tez
2.2. Turlarni keltirish va qat'iy rejim
Sukut bo'yicha pydantic mos keladigan qiymatlarni o'giradi:
| Kiruvchi | int maydon |
Decimal maydon |
|---|---|---|
5 |
5 |
Decimal("5") |
"5" |
5 |
Decimal("5") |
5.0 |
5 |
Decimal("5.0") |
"besh" |
int_parsing |
decimal_parsing |
float dan Decimal ga o'girish aniqlikni yo'qotishi mumkin — pul uchun maydonni Decimal deb e'lon qiling va satr yuborishni talab qiling (strict=True yoki Field(strict=True)).
class Buyurtma(BaseModel):
model_config = ConfigDict(strict=True, extra="forbid")| Sozlama | Ma'nosi |
|---|---|
strict=True |
Turlarni o'girmaydi — aniq tur talab qiladi |
extra="forbid" |
Ortiqcha maydonlar — xato (sukut: "ignore") |
frozen=True |
O'zgarmas model |
populate_by_name=True |
Alias bilan birga asl nom ham qabul qilinadi |
2.3. Field va Annotated
class Foydalanuvchi(BaseModel):
ism: str = Field(min_length=2, max_length=50)
yosh: int = Field(ge=0, le=150)
email: Annotated[str, Field(pattern=r"^[^@]+@[^@]+\.[a-z]+$")]
teglar: list[str] = Field(default_factory=list)| Cheklov | Nima uchun |
|---|---|
gt, ge, lt, le |
Sonlar chegarasi |
min_length, max_length |
Satr va ro'yxat uzunligi |
pattern |
Regulyar ifoda |
default_factory |
O'zgaruvchan sukut qiymat (11.6-dars tuzog'idan himoya) |
alias |
Tashqi nom (order_id → id) |
description |
JSON Schema va hujjat uchun |
2.4. Validatorlar
class Buyurtma(BaseModel):
mijoz: str
holat: Literal["yangi", "yakunlangan"] = "yangi"
manzil: Manzil | None = None
@field_validator("mijoz")
@classmethod
def ismni_tozala(cls, qiymat: str) -> str:
return qiymat.strip().title()
@model_validator(mode="after")
def manzil_talab(self) -> "Buyurtma":
if self.holat == "yakunlangan" and self.manzil is None:
raise ValueError("yakunlangan buyurtmada manzil bo'lishi kerak")
return self| Validator | Qachon | Nima uchun |
|---|---|---|
field_validator(..., mode="before") |
Turga o'girishdan oldin | Xom ma'lumotni tozalash (" 12 000 " → 12000) |
field_validator(...) (after) |
O'girishdan keyin | Qiymatni normallashtirish, qo'shimcha shart |
model_validator(mode="after") |
Butun model tayyor bo'lgach | Maydonlararo qoidalar |
2.5. ValidationError
try:
Buyurtma.model_validate(xom)
except ValidationError as xato:
xato.error_count() # nechta muammo
xato.errors() # [{'loc': ('summa',), 'type': 'greater_than', 'msg': ..., 'input': ...}]- Hamma xato birdan qaytadi — foydalanuvchiga to'liq ro'yxat ko'rsatish mumkin
loc— maydon yo'li (ichma-ich modellarda('manzil', 'shahar'))type— mashina o'qiydigan kod (int_parsing,string_too_short,extra_forbidden)- HTTP API da odatda
422javobiga aylantiriladi (20-qism)
2.6. Serializatsiya va sxema
b.model_dump() # dict (Python obyektlari bilan)
b.model_dump(mode="json") # JSON ga mos turlar
b.model_dump_json() # JSON satri
b.model_dump(exclude={"manzil"}, by_alias=True)
Buyurtma.model_validate(xom) # dict dan
Buyurtma.model_validate_json(matn) # JSON satridan (tezroq)
Buyurtma.model_json_schema() # JSON Schema (hujjat, OpenAPI)
b.model_copy(update={"holat": "yakunlangan"})TypeAdapter — model bo'lmagan turlar uchun: TypeAdapter(list[Buyurtma]).validate_python(xom).
2.7. Narxi va chegarasi
dataclass |
pydantic.BaseModel |
|
|---|---|---|
| Yaratish tezligi | Juda tez | Sekinroq (validatsiya) |
| Xotira | Kamroq (slots=True) |
Ko'proq |
| Validatsiya | Yo'q | To'liq |
| JSON | Qo'lda | O'rnatilgan |
| Qachon | Ichki domen modeli | Chegara: API, konfiguratsiya, fayl |
Qoida: chegarada pydantic, ichkarida dataclass — 13.6-darsdagi naqshning davomi. Har ichki funksiya chaqiruvida qayta validatsiya qilish — behuda narx.
3. Tez ma'lumotnoma
from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_validator, model_validator
class Model(BaseModel):
model_config = ConfigDict(extra="forbid", strict=False, frozen=False)
id: int
nom: str = Field(min_length=2, alias="name")
teglar: list[str] = Field(default_factory=list)
@field_validator("nom")
@classmethod
def tozala(cls, v: str) -> str: return v.strip()
@model_validator(mode="after")
def tekshir(self) -> "Model": return selfModel.model_validate(dict) Model.model_validate_json(str)
m.model_dump() m.model_dump_json() m.model_copy(update=...)
Model.model_json_schema() TypeAdapter(list[Model])
xato.errors() → loc, type, msg, input4. Batafsil misollar
Misol 1 — BaseModel asoslari
"""Validatsiya va turlarni keltirish; ichma-ich model; xatolar; qat'iy rejim; ortiqcha maydonlar."""
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, ValidationError
class Manzil(BaseModel):
shahar: str
kocha: str | None = None
class Buyurtma(BaseModel):
id: int
mijoz: str
summa: Decimal
manzil: Manzil | None = None
print("=== 1. Oddiy validatsiya ===")
b = Buyurtma(id=1, mijoz="Aziz", summa="250000.50", manzil={"shahar": "Toshkent"})
print(f" model: {b}")
print(f" summa turi: {type(b.summa).__name__}, qiymati: {b.summa}")
print(f" ichma-ich model: {type(b.manzil).__name__}, shahar: {b.manzil.shahar}")
print("\n=== 2. Turlarni keltirish ===")
namunalar = [("5", "satr son"), (5, "butun son"), (5.0, "kasr son"), (True, "bool")]
for qiymat, tavsif in namunalar:
try:
m = Buyurtma(id=qiymat, mijoz="A", summa=1)
print(f" id={qiymat!r:6} ({tavsif:10}) → {m.id!r} ({type(m.id).__name__})")
except ValidationError as xato:
print(f" id={qiymat!r:6} ({tavsif:10}) → xato: {xato.errors()[0]['type']}")
print("\n=== 3. Noto'g'ri ma'lumot ===")
try:
Buyurtma(id="besh", mijoz="A", summa="ikki yuz ming")
except ValidationError as xato:
print(f" xatolar soni: {xato.error_count()}")
for e in xato.errors():
print(f" {'.'.join(map(str, e['loc'])):8} {e['type']:18} kiruvchi: {e['input']!r}")
print("\n=== 4. ⚠️ float dan Decimal ga ===")
kasr = Buyurtma(id=1, mijoz="A", summa=250000.1)
satr = Buyurtma(id=1, mijoz="A", summa="250000.1")
print(f" float dan: {kasr.summa}")
print(f" satrdan: {satr.summa}")
print(f" teng emas: {kasr.summa != satr.summa} — pul uchun satr yuboring yoki strict rejim")
print("\n=== 5. Qat'iy rejim va ortiqcha maydonlar ===")
class QatiyBuyurtma(BaseModel):
model_config = ConfigDict(strict=True, extra="forbid")
id: int
summa: Decimal
print(f" to'g'ri: {QatiyBuyurtma(id=1, summa=Decimal('10.5'))}")
for xom, tavsif in [({"id": "1", "summa": Decimal("10")}, "id satr sifatida"),
({"id": 1, "summa": 10.5}, "summa float sifatida"),
({"id": 1, "summa": Decimal("10"), "qoshimcha": True}, "ortiqcha maydon")]:
try:
QatiyBuyurtma(**xom)
print(f" {tavsif:24} → o'tdi")
except ValidationError as xato:
print(f" {tavsif:24} → {xato.errors()[0]['type']}")Natijaning muhim qismi:
=== 1. Oddiy validatsiya ===
model: id=1 mijoz='Aziz' summa=Decimal('250000.50') manzil=Manzil(shahar='Toshkent', kocha=None)
summa turi: Decimal, qiymati: 250000.50
ichma-ich model: Manzil, shahar: Toshkent
=== 2. Turlarni keltirish ===
id='5' (satr son ) → 5 (int)
id=5 (butun son ) → 5 (int)
id=5.0 (kasr son ) → 5 (int)
id=True (bool ) → 1 (int)
=== 3. Noto'g'ri ma'lumot ===
xatolar soni: 2
id int_parsing kiruvchi: 'besh'
summa decimal_parsing kiruvchi: 'ikki yuz ming'
=== 4. ⚠️ float dan Decimal ga ===
float dan: 250000.1
satrdan: 250000.1
teng emas: False — pul uchun satr yuboring yoki strict rejim
=== 5. Qat'iy rejim va ortiqcha maydonlar ===
to'g'ri: id=1 summa=Decimal('10.5')
id satr sifatida → int_type
summa float sifatida → is_instance_of
ortiqcha maydon → extra_forbiddenNima ko'rsatdi: 2.1, 2.2-bo'limlar — kirishdagi float muammosi aynan shunday yuz bergan.
Misol 2 — Field cheklovlari va validatorlar
"""Field bilan cheklovlar; before/after validatorlar; model_validator; default_factory; aniq xato xabarlari."""
import datetime as dt
from decimal import Decimal
from typing import Annotated, Literal
from pydantic import BaseModel, Field, ValidationError, field_validator, model_validator
class Mijoz(BaseModel):
ism: str = Field(min_length=2, max_length=50, description="To'liq ism")
yosh: int = Field(ge=18, le=120)
email: Annotated[str, Field(pattern=r"^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$")] # katta harflar ham, keyin validator kichraytiradi
teglar: list[str] = Field(default_factory=list)
@field_validator("ism")
@classmethod
def ismni_tozala(cls, qiymat: str) -> str:
return " ".join(qiymat.split()).title()
@field_validator("email")
@classmethod
def emailni_kichik(cls, qiymat: str) -> str:
return qiymat.lower()
class Buyurtma(BaseModel):
id: int = Field(gt=0)
summa: Decimal = Field(gt=0, le=Decimal("100000000"))
holat: Literal["yangi", "jarayonda", "yakunlangan"] = "yangi"
yetkazish_sanasi: dt.date | None = None
@field_validator("summa", mode="before")
@classmethod
def summani_tozala(cls, qiymat: object) -> object:
"""Xom ma'lumotni turga o'girishdan oldin tozalash: '250 000,50 so'm' → '250000.50'."""
if isinstance(qiymat, str):
tozalangan = qiymat.replace(" ", "").replace("so'm", "").replace(",", ".")
return tozalangan or qiymat
return qiymat
@model_validator(mode="after")
def sana_talab(self) -> "Buyurtma":
if self.holat == "yakunlangan" and self.yetkazish_sanasi is None:
raise ValueError("yakunlangan buyurtmada yetkazish sanasi bo'lishi shart")
return self
print("=== 1. Field cheklovlari ===")
print(f" to'g'ri: {Mijoz(ism=' aziz karimov ', yosh=30, email='Aziz@Wisar.UZ')}")
for xom, tavsif in [
({"ism": "A", "yosh": 30, "email": "a@b.uz"}, "ism juda qisqa"),
({"ism": "Aziz", "yosh": 15, "email": "a@b.uz"}, "yosh 18 dan kichik"),
({"ism": "Aziz", "yosh": 30, "email": "notogri"}, "email formati"),
]:
try:
Mijoz(**xom)
except ValidationError as xato:
e = xato.errors()[0]
print(f" {tavsif:22} → [{e['type']}] {e['msg']}")
print("\n=== 2. default_factory ===")
m1, m2 = Mijoz(ism="Aziz", yosh=30, email="a@b.uz"), Mijoz(ism="Vali", yosh=40, email="v@b.uz")
m1.teglar.append("vip")
print(f" m1 teglari: {m1.teglar}, m2 teglari: {m2.teglar} — umumiy ro'yxat emas (11.6-dars)")
print("\n=== 3. before validator: xom ma'lumotni tozalash ===")
for xom in ["250 000,50", "250000.50", " 1 000 so'm", "nomalum"]:
try:
b = Buyurtma(id=1, summa=xom)
print(f" {xom!r:16} → {b.summa}")
except ValidationError as xato:
print(f" {xom!r:16} → xato: {xato.errors()[0]['type']}")
print("\n=== 4. model_validator: maydonlararo qoida ===")
try:
Buyurtma(id=1, summa="1000", holat="yakunlangan")
except ValidationError as xato:
print(f" sanasiz yakunlangan: {xato.errors()[0]['msg']}")
tayyor = Buyurtma(id=1, summa="1000", holat="yakunlangan", yetkazish_sanasi="2026-09-20")
print(f" sana bilan: {tayyor.holat}, {tayyor.yetkazish_sanasi} ({type(tayyor.yetkazish_sanasi).__name__})")
print("\n=== 5. Barcha xatolar birdan ===")
try:
Mijoz(ism="A", yosh=200, email="yomon")
except ValidationError as xato:
print(f" {xato.error_count()} ta muammo bir vaqtda:")
for e in xato.errors():
print(f" {e['loc'][0]:6} {e['msg']}")Natijaning muhim qismi:
=== 1. Field cheklovlari ===
to'g'ri: ism='Aziz Karimov' yosh=30 email='aziz@wisar.uz' teglar=[]
ism juda qisqa → [string_too_short] String should have at least 2 characters
yosh 18 dan kichik → [greater_than_equal] Input should be greater than or equal to 18
email formati → [string_pattern_mismatch] String should match pattern '^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$'
=== 2. default_factory ===
m1 teglari: ['vip'], m2 teglari: [] — umumiy ro'yxat emas (11.6-dars)
=== 3. before validator: xom ma'lumotni tozalash ===
'250 000,50' → 250000.50
'250000.50' → 250000.50
" 1 000 so'm" → 1000
'nomalum' → xato: decimal_parsing
=== 4. model_validator: maydonlararo qoida ===
sanasiz yakunlangan: Value error, yakunlangan buyurtmada yetkazish sanasi bo'lishi shart
sana bilan: yakunlangan, 2026-09-20 (date)
=== 5. Barcha xatolar birdan ===
3 ta muammo bir vaqtda:
ism String should have at least 2 characters
yosh Input should be less than or equal to 120
email String should match pattern '^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$'Nima ko'rsatdi: 2.3, 2.4-bo'limlar.
Misol 3 — Serializatsiya, alias va sxema
"""JSON dan yuklash va JSON ga yozish; tashqi nomlar uchun alias; exclude; JSON Schema; TypeAdapter."""
import json
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, ValidationError
class BuyurtmaAPI(BaseModel):
"""Tashqi shakl: kalitlar snake_case va boshqa nomlar bilan (13.6-dars)."""
model_config = ConfigDict(populate_by_name=True)
id: int = Field(alias="order_id")
mijoz: str = Field(alias="customer_name")
summa: Decimal = Field(alias="total_amount")
ichki_izoh: str | None = Field(default=None, exclude=True)
XOM_JSON = '{"order_id": 101, "customer_name": "Aziz", "total_amount": "250000.00"}'
print("=== 1. JSON dan yuklash ===")
b = BuyurtmaAPI.model_validate_json(XOM_JSON)
print(f" model: {b}")
print(f" ichki nomlar bilan ishlaymiz: b.id={b.id}, b.mijoz={b.mijoz!r}")
print("\n=== 2. Serializatsiya ===")
print(f" model_dump(): {b.model_dump()}")
print(f" model_dump(mode='json'): {b.model_dump(mode='json')}")
print(f" by_alias=True: {b.model_dump(by_alias=True, mode='json')}")
b.ichki_izoh = "ichki eslatma"
print(f" exclude=True maydon chiqishda yo'q: {'ichki_izoh' in b.model_dump()}")
print(f" JSON satri: {b.model_dump_json(by_alias=True)}")
print("\n=== 3. Nusxa va o'zgartirish ===")
yangi = b.model_copy(update={"summa": Decimal("300000.00")})
print(f" asl: {b.summa}, nusxa: {yangi.summa}, bir xil obyektmi: {b is yangi}")
print("\n=== 4. JSON Schema ===")
sxema = BuyurtmaAPI.model_json_schema()
print(f" majburiy maydonlar: {sxema['required']}")
print(f" xususiyatlar: {sorted(sxema['properties'])}")
print(f" 'total_amount' turi: {sxema['properties']['total_amount'].get('anyOf', sxema['properties']['total_amount'])}")
print("\n=== 5. TypeAdapter: model bo'lmagan turlar ===")
royxat_adapter = TypeAdapter(list[BuyurtmaAPI])
xom_royxat = json.loads(f'[{XOM_JSON}, {{"order_id": 102, "customer_name": "Dilnoza", "total_amount": "99000"}}]')
buyurtmalar = royxat_adapter.validate_python(xom_royxat)
print(f" {len(buyurtmalar)} ta buyurtma: {[b.mijoz for b in buyurtmalar]}")
jami_adapter = TypeAdapter(dict[str, Decimal])
print(f" dict[str, Decimal]: {jami_adapter.validate_python({'jami': '349000.00'})}")
try:
royxat_adapter.validate_python([{"order_id": "x", "customer_name": "A", "total_amount": "1"}])
except ValidationError as xato:
e = xato.errors()[0]
print(f" ro'yxat ichidagi xato joyi: {e['loc']} → {e['type']}")Natijaning muhim qismi:
=== 1. JSON dan yuklash ===
model: id=101 mijoz='Aziz' summa=Decimal('250000.00') ichki_izoh=None
ichki nomlar bilan ishlaymiz: b.id=101, b.mijoz='Aziz'
=== 2. Serializatsiya ===
model_dump(): {'id': 101, 'mijoz': 'Aziz', 'summa': Decimal('250000.00')}
model_dump(mode='json'): {'id': 101, 'mijoz': 'Aziz', 'summa': '250000.00'}
by_alias=True: {'order_id': 101, 'customer_name': 'Aziz', 'total_amount': '250000.00'}
exclude=True maydon chiqishda yo'q: False
JSON satri: {"order_id":101,"customer_name":"Aziz","total_amount":"250000.00"}
=== 3. Nusxa va o'zgartirish ===
asl: 250000.00, nusxa: 300000.00, bir xil obyektmi: False
=== 4. JSON Schema ===
majburiy maydonlar: ['order_id', 'customer_name', 'total_amount']
xususiyatlar: ['customer_name', 'ichki_izoh', 'order_id', 'total_amount']
'total_amount' turi: [{'type': 'number'}, {'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$', 'type': 'string'}]
=== 5. TypeAdapter: model bo'lmagan turlar ===
2 ta buyurtma: ['Aziz', 'Dilnoza']
dict[str, Decimal]: {'jami': Decimal('349000.00')}
ro'yxat ichidagi xato joyi: (0, 'order_id') → int_parsingNima ko'rsatdi: 2.6-bo'lim.
Misol 4 — Amaliy: API chegarasidan domen modeliga
To'liq zanjir: xom JSON → pydantic validatsiyasi → domen dataclass → biznes mantiq. Xatolar API javobiga (422) aylanadi. Oxirida — narx o'lchovi va "chegarada validatsiya, ichkarida dataclass" qoidasining asosi.
"""Xom JSON → pydantic → domen dataclass; xatolarni API javobiga aylantirish; narx o'lchovi."""
import dataclasses
import json
import time
from decimal import Decimal
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class BuyurtmaSorovi(BaseModel):
"""Chegara modeli: tashqi shakl va barcha tekshiruvlar shu yerda."""
model_config = ConfigDict(extra="forbid", populate_by_name=True)
id: int = Field(gt=0, alias="order_id")
mijoz: str = Field(min_length=2, max_length=60, alias="customer_name")
summa: Decimal = Field(gt=0, alias="total_amount")
holat: Literal["yangi", "yakunlangan"] = "yangi"
@dataclasses.dataclass(frozen=True, slots=True)
class Buyurtma:
"""Domen modeli: validatsiya qilingan, tez, o'zgarmas."""
id: int
mijoz: str
summa: Decimal
holat: str
def komissiya(self, foiz: Decimal = Decimal("0.02")) -> Decimal:
return (self.summa * foiz).quantize(Decimal("0.01"))
def domenga(sorov: BuyurtmaSorovi) -> Buyurtma:
return Buyurtma(sorov.id, sorov.mijoz, sorov.summa, sorov.holat)
def api_javob(xom: object) -> tuple[int, dict[str, object]]:
"""HTTP ishlovchisining soddalashtirilgan modeli."""
try:
sorov = BuyurtmaSorovi.model_validate(xom)
except ValidationError as xato:
return 422, {"xatolar": [{"maydon": ".".join(map(str, e["loc"])), "sabab": e["type"], "xabar": e["msg"]}
for e in xato.errors()]}
buyurtma = domenga(sorov)
return 200, {"id": buyurtma.id, "komissiya": str(buyurtma.komissiya())}
XOM = json.loads('''[
{"order_id": 101, "customer_name": "Aziz", "total_amount": "250000.00"},
{"order_id": 102, "customer_name": "Dilnoza", "total_amount": "99000", "holat": "yakunlangan"},
{"order_id": -1, "customer_name": "X", "total_amount": "0"},
{"order_id": 104, "customer_name": "Jasur", "total_amount": "1000", "qoshimcha": true},
{"customer_name": "Kalitsiz", "total_amount": "5000"}
]''')
print("=== 1. API javoblari ===")
qabul: list[Buyurtma] = []
for xom in XOM:
kod, javob = api_javob(xom)
if kod == 200:
qabul.append(domenga(BuyurtmaSorovi.model_validate(xom)))
print(f" 200 → {javob}")
else:
sabablar = ", ".join(f"{x['maydon']}:{x['sabab']}" for x in javob["xatolar"])
print(f" 422 → {sabablar}")
print("\n=== 2. Ichkarida — domen modeli ===")
for b in qabul:
print(f" {b.id}: {b.mijoz:8} {b.summa:>10} komissiya {b.komissiya()}")
print(f" domen modeli o'zgarmas: {Buyurtma.__dataclass_params__.frozen}, slots: {Buyurtma.__slots__}")
print("\n=== 3. Narx: validatsiya va oddiy yaratish ===")
XOM_BITTA = {"order_id": 101, "customer_name": "Aziz", "total_amount": "250000.00"}
N = 20_000
def olch(f, *args) -> float:
eng = float("inf")
for _ in range(3):
bosh = time.perf_counter()
for _ in range(N):
f(*args)
eng = min(eng, time.perf_counter() - bosh)
return eng
t_pydantic = olch(BuyurtmaSorovi.model_validate, XOM_BITTA)
t_dataclass = olch(Buyurtma, 101, "Aziz", Decimal("250000.00"), "yangi")
print(f" {N:,} marta validatsiya dataclass yaratishdan sekinroq: {t_pydantic > t_dataclass}")
print(f" kamida 3 barobar: {t_pydantic / t_dataclass >= 3}")
print(" ⭐ shuning uchun: validatsiya faqat chegarada, ichki chaqiruvlarda takrorlanmaydi")
print("\n=== 4. Qoidalar ===")
print(" chegara (API, fayl, konfiguratsiya) → pydantic: tekshiradi, o'giradi, xato beradi")
print(" ichkarida (biznes mantiq) → dataclass: tez, o'zgarmas, xulq bilan")
print(" statik tekshiruv (mypy) → ikkala qatlamda ham ishlaydi")Natijaning muhim qismi:
=== 1. API javoblari ===
200 → {'id': 101, 'komissiya': '5000.00'}
200 → {'id': 102, 'komissiya': '1980.00'}
422 → order_id:greater_than, customer_name:string_too_short, total_amount:greater_than
422 → qoshimcha:extra_forbidden
422 → order_id:missing
=== 2. Ichkarida — domen modeli ===
101: Aziz 250000.00 komissiya 5000.00
102: Dilnoza 99000 komissiya 1980.00
domen modeli o'zgarmas: True, slots: ('id', 'mijoz', 'summa', 'holat')
=== 3. Narx: validatsiya va oddiy yaratish ===
20,000 marta validatsiya dataclass yaratishdan sekinroq: True
kamida 3 barobar: True
⭐ shuning uchun: validatsiya faqat chegarada, ichki chaqiruvlarda takrorlanmaydi
=== 4. Qoidalar ===
chegara (API, fayl, konfiguratsiya) → pydantic: tekshiradi, o'giradi, xato beradi
ichkarida (biznes mantiq) → dataclass: tez, o'zgarmas, xulq bilan
statik tekshiruv (mypy) → ikkala qatlamda ham ishlaydiNima ko'rsatdi: 2.5, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"mypy bo'lsa, validatsiya kerak emas" |
mypy tashqi ma'lumotni ko'rmaydi |
"pydantic — dataclass ning yaxshiroq varianti" |
Boshqa maqsad: validatsiya; narxi ham bor |
"Har joyda BaseModel ishlatish kerak" |
Chegarada; ichkarida dataclass tez |
| "Validatsiya turlarni o'girmaydi" | Sukut bo'yicha o'giradi (strict=True bilan yo'q) |
"float bilan Decimal xavfsiz" |
Aniqlik yo'qoladi — satr yuboring |
| "Xato bitta bo'lib qaytadi" | ValidationError hammasini birdan beradi |
| "Ortiqcha maydonlar xato beradi" | Sukut bo'yicha e'tiborsiz qoldiriladi (extra="forbid" kerak) |
"pydantic sekin" |
v2 yadrosi Rust'da; lekin dataclass dan baribir qimmat |
6. Keng tarqalgan xatolar va yechimlari
1. Tashqi ma'lumotni tekshirmasdan ishlatish
buyurtma = Buyurtma(**json.loads(tana)) # ❌ har qanday narsa kelishi mumkin
sorov = BuyurtmaSorovi.model_validate_json(tana) # ✅2. Pul uchun float
summa: float # ❌ aniqlik yo'qoladi
summa: Decimal # ✅ va satr sifatida yuboring3. Ortiqcha maydonlarga ruxsat berish
class Sorov(BaseModel): ... # ⚠️ noto'g'ri yozilgan maydon jim o'tadi
model_config = ConfigDict(extra="forbid") # ✅4. O'zgaruvchan sukut qiymat
teglar: list[str] = [] # ❌ (pydantic ogohlantiradi)
teglar: list[str] = Field(default_factory=list) # ✅5. Ichki qatlamda qayta validatsiya
def hisobla(sorov: BuyurtmaSorovi) -> Decimal:
yangi = BuyurtmaSorovi.model_validate(sorov.model_dump()) # ⚠️ behuda narx6. Xatolarni foydalanuvchiga xom holda ko'rsatish
return {"error": str(xato)} # ⚠️ ichki tafsilotlar va texnik matn
return {"xatolar": [...]} # ✅ maydon + tushunarli xabar (Misol 4)7. Domen mantiqini modelga tiqish
class Buyurtma(BaseModel):
def bazaga_yoz(self) -> None: ... # ⚠️ chegara modeli — faqat shakl va tekshiruv8. alias bilan populate_by_name ni unutish
id: int = Field(alias="order_id") # ⚠️ endi Buyurtma(id=1) ishlamaydi
model_config = ConfigDict(populate_by_name=True) # ✅ ikkalasi ham qabul qilinadi7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 9-qism (o'tilgan): xatolar va foydalanuvchiga xabar
- 13.1-dars (o'tilgan): annotatsiyalar ish vaqtida tekshirilmaydi —
pydanticshuni o'zgartiradi - 13.6-dars (o'tilgan):
TypedDictbilan shakl;pydantic— validatsiya bilan - 13.7-dars (o'tilgan): statik tekshiruv bilan birga ishlatish
- 13.9-dars: qatlamlar va turlar bilan dizayn
- 16-qism: JSON, YAML, konfiguratsiya fayllari
- 20-qism: FastAPI —
pydanticustiga qurilgan - 23-qism: baza yozuvlari va DTO lar
8. Eng yaxshi amaliyotlar
Chegarada validatsiya qiling — API, fayl, konfiguratsiya, navbat xabarlari.
Ichkarida
dataclass— validatsiyani takrorlamang.extra="forbid"— noto'g'ri maydon nomlari jim o'tmasin.Pul uchun
Decimalva satr ko'rinishidagi kirish.Xatolarni tushunarli javobga aylantiring (
loc,type, xabar).Maydonlararo qoidalar —
model_validator(mode="after").Tashqi nomlarni
aliasbilan chegarada qoldiring.mypybilan birga ishlating — statik va dinamik tekshiruv bir-birini to'ldiradi.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class M(BaseModel):
id: int
nom: str = "yoq"
1. print(M(id=5))
2. print(M(id="5").id, type(M(id="5").id).__name__)
3. try:
M(id="besh")
except ValidationError as e:
print(e.errors()[0]["type"])
4. print(M(id=1, qoshimcha=2).model_dump())
5. class S(BaseModel):
model_config = ConfigDict(extra="forbid")
id: int
try:
S(id=1, qoshimcha=2)
except ValidationError as e:
print(e.errors()[0]["type"])
6. print(M(id=1).model_dump_json())
7. print(M.model_validate({"id": 3}).id)
8. print(M.model_validate_json('{"id": 4}').nom)
9. class D(BaseModel):
summa: Decimal
print(D(summa="10.50").summa, type(D(summa="10.50").summa).__name__)
10. class F(BaseModel):
yosh: int = Field(ge=18)
try:
F(yosh=10)
except ValidationError as e:
print(e.errors()[0]["type"])
11. print(M(id=1).model_copy(update={"nom": "yangi"}).nom)
12. print(sorted(M.model_json_schema()["properties"]))Javoblar
id=5 nom='yoq'5 int— satr butun songa o'girildiint_parsing{'id': 1, 'nom': 'yoq'}— ortiqcha maydon sukut bo'yicha e'tiborsizextra_forbidden{"id":1,"nom":"yoq"}3yoq10.50 Decimalgreater_than_equalyangi['id', 'nom']
Vazifa 2: Xatolarni tuzating
1. class Tolov(BaseModel):
summa: float
2. class Sozlama(BaseModel):
teglar: list[str] = []
3. def ishlov(tana: str) -> Buyurtma:
malumot = json.loads(tana)
return Buyurtma(**malumot)
4. class Sorov(BaseModel):
id: int = Field(alias="order_id")
Sorov(id=1)
5. try:
Sorov.model_validate(xom)
except ValidationError as e:
return {"xato": str(e)}Javoblar
1. class Tolov(BaseModel):
summa: Decimal = Field(gt=0) # pul — Decimal, satr sifatida yuboriladi
2. class Sozlama(BaseModel):
teglar: list[str] = Field(default_factory=list)
3. def ishlov(tana: str) -> Buyurtma:
sorov = BuyurtmaSorovi.model_validate_json(tana) # validatsiya bilan
return domenga(sorov)
4. class Sorov(BaseModel):
model_config = ConfigDict(populate_by_name=True) # ikkala nom ham ishlaydi
id: int = Field(alias="order_id")
5. except ValidationError as e:
return {"xatolar": [{"maydon": ".".join(map(str, x["loc"])), "xabar": x["msg"]} for x in e.errors()]}Vazifa 3: Konfiguratsiya modeli
Ilova konfiguratsiyasi uchun pydantic model yozing:
- Baza URL, port (1–65535), log darajasi (
Literal), ishchi jarayonlar soni, xususiyat bayroqlari (dict[str, bool]) - Muhit o'zgaruvchilaridan va YAML/JSON fayldan yuklash
- Noto'g'ri konfiguratsiyada ilovani ishga tushirmasdan aniq xato chiqarish
- Sirlarni (
parol,token)model_dumpdan chiqarib tashlash - Testlar: to'g'ri, noto'g'ri va qisman konfiguratsiyalar
Vazifa 4: Tashqi API mijozi
Tanlagan ochiq API uchun:
- Javob modellarini yozing (ichma-ich tuzilmalar bilan)
aliasbilan tashqi nomlarni ichki nomlarga bog'lang- Kutilmagan maydonlar kelganda nima bo'lishini sinab ko'ring (
extrasozlamasi bilan) - Domen modellariga o'giruvchi funksiya yozing
- Yozib olingan (fixture) javoblar bilan testlar yozing
Vazifa 5: Xatolar tarjimasi
ValidationErrorni foydalanuvchiga tushunarli o'zbekcha xabarlarga aylantiruvchi funksiya yozingtypekodlari bo'yicha xabarlar lug'atini tuzing (int_parsing,string_too_short,missing, ...)- Ichma-ich maydonlar uchun yo'lni o'qiladigan ko'rinishga keltiring (
manzil.shahar) - Formadagi maydonlarga bog'lash uchun JSON javob formatini loyihalang
- 10 xil xato holati uchun natijani ko'rsating
Vazifa 6: Narx o'lchovi
- 100 000 yozuvni uch usulda yarating:
dataclass,pydantic(validatsiya bilan),pydanticmodel_construct(validatsiyasiz) - Vaqt va xotirani o'lchang
model_validate_jsonvajson.loads+model_validateni solishtiring- Natijalar asosida "qayerda validatsiya qilish kerak" qoidasini asoslang
Vazifa 7: O'ylash
Go'da encoding/json teglari va qo'lda tekshiruv, Java'da Bean Validation (@NotNull, @Size), TypeScript'da zod yoki io-ts, Rust'da serde + validator. Python'da pydantic shu vazifani annotatsiyalar orqali bajaradi. Annotatsiyalarni ham statik tekshiruv, ham ish vaqtidagi validatsiya uchun ishlatish qanday afzallik va xavflarga ega?
Javob
Qisqa javob: bitta manba (annotatsiya) ikki maqsadga xizmat qiladi — bu takrorlanishni yo'qotadi va model bilan tekshiruv doim mos bo'ladi. Xavf esa shundaki, statik va dinamik semantika har doim ham bir xil emas: pydantic turlarni o'giradi, mypy esa o'girmaydi; natijada kod "tekshiruvdan o'tgan", lekin ish vaqtida boshqacha xulq qilishi mumkin.
1. Boshqa ekotizimlar
| Platforma | Validatsiya | Manba |
|---|---|---|
| Go | Qo'lda yoki validator teglari |
Struct teglari (alohida til) |
| Java | Bean Validation annotatsiyalari | Annotatsiyalar (turlardan alohida) |
| TypeScript | zod, io-ts |
Sxemadan tur chiqariladi (teskari yo'nalish) |
| Rust | serde + validator |
Atributlar (makroslar) |
| Python | pydantic |
Turlar annotatsiyasining o'zi |
TypeScript'dagi zod yondashuvi qiziq: u yerda sxema birlamchi, tur esa undan chiqariladi (z.infer). Python'da esa aksincha — tur birlamchi.
2. Afzalliklari
- Bitta manba: model o'zgarsa, validatsiya ham o'zgaradi
- Hujjat tekin: JSON Schema/OpenAPI avtomatik (FastAPI, 20-qism)
- IDE qo'llab-quvvatlashi: maydonlar avtoto'ldiriladi
- O'rganish oson: yangi sintaksis yo'q — oddiy annotatsiyalar
3. Xavflari
| Xavf | Misol |
|---|---|
| Statik va dinamik semantika farqi | mypy: summa: Decimal ga float bermaysiz; pydantic: o'giradi |
| Import paytidagi narx | Modellar yaratish yig'ish vaqtini oshiradi |
| Ish vaqtidagi narx | Har validatsiya — hisob; issiq yo'lda qimmat |
| "Hamma joyda model" vasvasasi | Domen mantiqi chegara modeliga yopishib qoladi |
| Kutubxonaga bog'lanish | Model ta'riflari pydantic ga bog'liq bo'lib qoladi |
4. Amaliy qoidalar
- Chegarada
pydantic, ichkaridadataclass— semantika farqi bitta joyda qoladi strict=Truebilan o'girishni cheklang — statik va dinamik xulq yaqinlashadi- Modelni shakl va tekshiruv uchun ishlating, biznes mantiqni alohida qatlamga chiqaring
mypyvapydanticni birga ishlating: biri kodni, ikkinchisi ma'lumotni tekshiradi
Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.
Xulosa
Bu darsda ish vaqtidagi validatsiyani o'rgandik.
Eng muhim uch fikr:
Statik tekshiruv tashqi ma'lumotni ko'rmaydi.
mypysizning kodingizdagi shartnomalarni tekshiradi; JSON so'rov, forma yoki konfiguratsiya fayli esa ish vaqtida keladi.pydanticannotatsiyalarni ish vaqtida majburlanadigan shartnomaga aylantiradi: turlarni tekshiradi, keltiradi va noto'g'ri bo'lsaValidationErrorberadi — barcha muammolar ro'yxati bilan.Sozlamalar xulqni belgilaydi. Sukut bo'yicha
pydanticmos qiymatlarni o'giradi ("5"→5) va ortiqcha maydonlarni e'tiborsiz qoldiradi — shuning uchunextra="forbid"va kerak bo'lsastrict=Trueqo'shiladi. Pul uchunDecimalva satr ko'rinishidagi kirish:floatorqali o'tgan qiymat aniqlikni yo'qotadi.Fieldcheklovlari,field_validatorvamodel_validatorqolgan qoidalarni beradi.Chegarada validatsiya, ichkarida
dataclass. Har ichki chaqiruvda qayta validatsiya — behuda narx (o'lchov bo'yicha bir necha barobar). To'g'ri zanjir: xom JSON →pydanticmodeli (tekshiruv, alias, o'girish) → domendataclassi (tez, o'zgarmas, xulq bilan) → biznes mantiq; xatolar esa foydalanuvchiga tushunarli javobga aylanadi.
Keyingi darsda 13-qismni yakunlaymiz: turlar bilan loyiha dizayni — qatlamlar, portlar va adapterlar, "noto'g'ri holatni ifodalab bo'lmaydigan" modellar va turlar madaniyatini jamoada joriy qilish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!