Bilingual Tech Book · English + বাংলা
Pydantic Mastery
Data validation made simple — for Backend, AI, ML & LLM engineers.
সহজ ভাষায়, প্রচুর উদাহরণসহ — একদম শূন্য থেকে প্রফেশনাল পর্যন্ত।
1
What is Pydantic?
Pydantic কী? — একদম সহজ ভাষায়
EN
Think of Pydantic as a security guard for your data. You tell the guard: "only let in a name (text) and an age (number)". If someone sends wrong data — the guard stops it at the gate and tells you exactly what was wrong.
বাং
Pydantic-কে ভাবুন আপনার ডেটার দারোয়ান হিসেবে। আপনি দারোয়ানকে বলে দিলেন: "শুধু নাম (text) আর বয়স (number) ঢুকতে দেবে।" কেউ ভুল ডেটা পাঠালে — দারোয়ান গেটেই আটকে দেবে, আর বলে দেবে ঠিক কী ভুল ছিল।
The problem — without Pydantic / সমস্যাটা — Pydantic ছাড়া
# Data comes from outside — you can't trust it
# ডেটা আসে বাইরে থেকে — বিশ্বাস করা যায় না
def create_user(data: dict):
age = data["age"] # might be "25" (a string!) / string হতে পারে!
return age + 1 # 💥 CRASH — but much later, somewhere confusing
The solution — with Pydantic / সমাধান — Pydantic সহ
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
u = User(name="Rahim", age="25") # "25" → 25 ✅ auto-converted / নিজেই convert
u = User(name="Rahim", age="abc") # ❌ instant clear error / সাথে সাথে পরিষ্কার error
Where will you use it? / কোথায় কোথায় লাগবে?
| Area | English | বাংলা |
|---|---|---|
| Backend API | Check every request & response (FastAPI is built on it) | প্রতিটা request/response চেক (FastAPI এর ওপরেই তৈরি) |
| ML | Make sure features are complete & in range | Feature সম্পূর্ণ ও সীমার মধ্যে কিনা |
| LLM | Check prompts, parse AI's JSON reply | Prompt চেক, AI-এর JSON উত্তর parse |
| Config | Load .env safely | .env নিরাপদে load |
💡
One line to remember / এক লাইনে মনে রাখুন: Pydantic changes "I hope the data is right" into "the data is guaranteed right." — "আশা করি ডেটা ঠিক আছে" বদলে হয়ে যায় "ডেটা নিশ্চিত ঠিক আছে।"
2
Your First Model
প্রথম Model — BaseModel দিয়ে শুরু
EN
Rule is simple: make a class, inherit
BaseModel, write each field with a type. No default = required. Has default = optional. That's 80% of Pydantic.
বাং
নিয়ম খুব সহজ: একটা class বানান,
BaseModel থেকে inherit করুন, প্রতিটা field-এর type লিখুন। Default নেই = দিতেই হবে। Default আছে = না দিলেও চলবে। এটুকুই Pydantic-এর ৮০%।
from pydantic import BaseModel
class Student(BaseModel):
id: int # required / আবশ্যক
name: str # required / আবশ্যক
is_active: bool = True # optional (default True)
bio: str | None = None # optional, null চলবে
3 ways to create / তৈরির ৩টা উপায়
# 1) Directly / সরাসরি
s = Student(id=1, name="Karim")
# 2) From a dict (like parsed JSON) / dict থেকে
s = Student.model_validate({"id": 2, "name": "Fatima"})
# 3) From a raw JSON string / সরাসরি JSON string থেকে
s = Student.model_validate_json('{"id": 3, "name": "Ayan"}')
print(s.name) # normal access / সাধারণভাবে ব্যবহার
print(s.is_active) # True — default মান
Errors are your friend / Error আপনার বন্ধু
from pydantic import ValidationError
try:
Student(id="abc", name=123)
except ValidationError as e:
print(e)
# id → Input should be a valid integer [input_value='abc']
# ↑ WHICH field, WHAT problem, WHAT bad value — all at once!
# ↑ কোন field, কী সমস্যা, ভুল মান কী — সব একসাথে!
✅
EN: Pydantic reports all errors together, not just the first — fix everything in one go.
বাং: Pydantic সব error একসাথে দেখায়, শুধু প্রথমটা না — এক দফায় সব ঠিক করে ফেলুন।
বাং: Pydantic সব error একসাথে দেখায়, শুধু প্রথমটা না — এক দফায় সব ঠিক করে ফেলুন।
3
Fields & Rules
Field ও নিয়ম — সীমা বেঁধে দিন
EN
A type says what kind of value ("a number").
Field() says which values are okay ("a number between 1 and 5"). Example: a rating must be 1–5, a price must be above 0.
বাং
Type বলে মানটা কী ধরনের ("একটা সংখ্যা")। আর
Field() বলে কোন মানগুলো ঠিক আছে ("১ থেকে ৫-এর মধ্যে সংখ্যা")। যেমন: rating হতে হবে ১–৫, দাম হতে হবে ০-এর বেশি।
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0) # gt = greater than / এর বেশি
rating: int = Field(default=5, ge=1, le=5) # 1 থেকে 5
sku: str = Field(pattern=r"^[A-Z]{3}-\d{4}$") # e.g. ABC-1234
| Rule | Meaning | অর্থ | For |
|---|---|---|---|
gt / ge | greater / greater-or-equal | বেশি / সমান বা বেশি | numbers |
lt / le | less / less-or-equal | কম / সমান বা কম | numbers |
min_length / max_length | length limits | দৈর্ঘ্যের সীমা | str, list |
pattern | must match regex | regex-এর সাথে মিলতে হবে | str |
default_factory | fresh default each time | প্রতিবার নতুন default | list, dict |
Free ready-made types / ফ্রি তৈরি-করা type
from pydantic import BaseModel, EmailStr, HttpUrl, SecretStr
class Account(BaseModel):
email: EmailStr # checks REAL email format / আসল email কিনা যাচাই
website: HttpUrl # must be a valid URL / বৈধ URL হতে হবে
api_key: SecretStr # hidden when printed! / print করলে লুকানো!
acc = Account(email="dev@x.com", website="https://x.com", api_key="sk-123")
print(acc.api_key) # SecretStr('**********') 🔒 — safe in logs / log-এ নিরাপদ
🔒
EN: Always use
বাং: API key ও password-এ সবসময়
SecretStr for API keys & passwords — they will never leak into your logs.বাং: API key ও password-এ সবসময়
SecretStr — ভুলেও log-এ ফাঁস হবে না।
4
Nested Models
Model-এর ভেতরে Model
EN
Real JSON is rarely flat — an order has an address inside it, and a list of items. Just use one model as the type of another field. Pydantic checks the whole tree, every level.
বাং
বাস্তবের JSON খুব কমই সরল হয় — একটা order-এর ভেতরে address থাকে, item-এর list থাকে। শুধু একটা model-কে আরেক field-এর type বানিয়ে দিন। Pydantic পুরো গাছটাই চেক করবে, প্রতিটা স্তর।
class Address(BaseModel):
city: str
postal_code: str = Field(pattern=r"^\d{4}$") # e.g. 1207
class Item(BaseModel):
product: str
quantity: int = Field(ge=1)
class Order(BaseModel):
customer: str
address: Address # ← one model inside / ভেতরে একটা model
items: list[Item] # ← a LIST of models / model-এর list
order = Order.model_validate({
"customer": "Nadia",
"address": {"city": "Dhaka", "postal_code": "1207"},
"items": [{"product": "Mouse", "quantity": 2}],
})
print(order.address.city) # Dhaka
print(order.items[0].quantity) # 2
🎯
EN: Error paths point into the tree:
বাং: Error-এর ঠিকানা গাছের ভেতর পর্যন্ত যায়:
items.1.quantity = "2nd item's quantity is wrong". In a 500-row payload this saves hours.বাং: Error-এর ঠিকানা গাছের ভেতর পর্যন্ত যায়:
items.1.quantity মানে "২য় item-এর quantity ভুল"। ৫০০ row-এর ডেটায় এটা ঘণ্টার পর ঘণ্টা বাঁচায়।
5
Custom Validators
নিজের Validator — নিজের নিয়ম নিজে লিখুন
EN
Built-in rules cover ranges and lengths. But what about "username can't have spaces" or "check-out date must be after check-in"? For those, write your own rule — a small function attached to the model.
বাং
Built-in নিয়মে সীমা আর দৈর্ঘ্য হয়ে যায়। কিন্তু "username-এ স্পেস চলবে না" বা "check-out অবশ্যই check-in-এর পরে" — এগুলোর জন্য নিজের নিয়ম লিখুন — model-এর সাথে জোড়া ছোট্ট একটা ফাংশন।
One field → @field_validator / এক field-এর জন্য
from pydantic import BaseModel, field_validator
class SignUp(BaseModel):
username: str
password: str
@field_validator("username")
@classmethod
def no_spaces(cls, v):
if " " in v:
raise ValueError("No spaces allowed / স্পেস চলবে না")
return v.lower() # can also TRANSFORM! / বদলেও দিতে পারে!
u = SignUp(username="RAHIM", password="secret123")
print(u.username) # "rahim" — lowercase হয়ে গেছে ✨
Multiple fields together → @model_validator / একাধিক field একসাথে
from datetime import date
from pydantic import BaseModel, model_validator
class Booking(BaseModel):
check_in: date
check_out: date
@model_validator(mode="after")
def dates_ok(self):
if self.check_out <= self.check_in:
raise ValueError("check_out must be after check_in / পরে হতে হবে")
return self
Clean messy input first → mode="before" / আগে ময়লা পরিষ্কার
class Post(BaseModel):
tags: list[str]
@field_validator("tags", mode="before")
@classmethod
def split_string(cls, v):
# Accept "a,b,c" OR ["a","b","c"] — both! / দুটোই নিন!
if isinstance(v, str):
return [t.strip() for t in v.split(",")]
return v
Post(tags="ml, ai, backend").tags # ['ml', 'ai', 'backend'] ✅
🤖
LLM tip / LLM টিপ: AI models often send
"5" instead of 5, or a comma-string instead of a list. mode="before" cleans it before validation. — AI প্রায়ই 5-এর বদলে "5" পাঠায়। mode="before" সেটা validation-এর আগেই ঠিক করে দেয়।
6
Model → JSON
Serialization — ডেটা বাইরে পাঠানো
EN
Validation = data coming in. Serialization = data going out (API response, database, logs). Two methods do it all:
model_dump() → dict, model_dump_json() → JSON string.
বাং
Validation = ডেটা ভেতরে আসা। Serialization = ডেটা বাইরে যাওয়া (API response, database, log)। দুটো method-ই সব করে:
model_dump() → dict, model_dump_json() → JSON string।
from datetime import datetime
class Event(BaseModel):
name: str
when: datetime
e = Event(name="Deploy", when="2026-07-03T10:30:00")
e.model_dump() # {'name': 'Deploy', 'when': datetime(...)} ← dict
e.model_dump_json() # '{"name":"Deploy","when":"2026-07-03T10:30:00"}'
# datetime → ISO string automatically! / নিজে নিজেই!
# Control the output / output নিয়ন্ত্রণ:
e.model_dump(include={"name"}) # only these / শুধু এগুলো
e.model_dump(exclude_none=True) # drop None fields / None বাদ
e.model_dump(exclude_unset=True) # only fields user actually sent ⭐
The PATCH trick / PATCH-এর কৌশল
class UserUpdate(BaseModel):
name: str | None = None
email: str | None = None
@app.patch("/users/{user_id}")
def update_user(user_id: int, patch: UserUpdate):
changes = patch.model_dump(exclude_unset=True)
# User sent only {"name": "New"} → changes has ONLY name!
# email untouched / email অক্ষত থাকল — এটাই আসল PATCH ✅
db.update(user_id, **changes)
⭐
EN:
বাং:
exclude_unset=True = "give me only what the client actually sent." The secret of every clean PATCH endpoint.বাং:
exclude_unset=True মানে "client যা পাঠিয়েছে শুধু তাই দাও।" প্রতিটা পরিষ্কার PATCH endpoint-এর গোপন রহস্য।
7
Settings & Config
Backend Config — .env থেকে নিরাপদে
EN
Every backend needs config: database URL, API keys, model paths. Hardcoding = dangerous. Scattered
os.getenv() = messy. pydantic-settings reads your .env into one validated class — and checks everything at startup.
বাং
প্রতিটা backend-এ config লাগে: database URL, API key, model path। Hardcode করা = বিপজ্জনক। জায়গায় জায়গায়
os.getenv() = অগোছালো। pydantic-settings আপনার .env পড়ে একটা validated class বানায় — আর startup-এই সব চেক করে ফেলে।
pip install pydantic-settings
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
app_name: str = "my-api"
debug: bool = False
database_url: str # required — no default!
anthropic_api_key: SecretStr # hidden in logs 🔒
max_batch_size: int = Field(default=32, ge=1, le=256)
settings = Settings() # reads env + .env, validates ALL / সব validate
# .env file / .env ফাইল
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
ANTHROPIC_API_KEY=sk-ant-xxxx
DEBUG=true
MAX_BATCH_SIZE=64
🌙
EN: If
বাং:
DATABASE_URL is missing, the app fails at startup with a clear message — not at 3 AM when the first user hits a broken connection.বাং:
DATABASE_URL না থাকলে অ্যাপ startup-এই fail করবে স্পষ্ট বার্তা দিয়ে — রাত ৩টায় প্রথম user এসে ভাঙা connection-এ পড়ার সময় নয়।
8
Pydantic + FastAPI
দুজনে মিলে API তৈরি
EN
FastAPI is built on top of Pydantic. Every request body, every 422 error, every schema in
/docs — that's Pydantic working. Here Pydantic does 3 jobs: validate input, filter output, generate docs.
বাং
FastAPI তৈরিই হয়েছে Pydantic-এর উপরে। প্রতিটা request body, প্রতিটা 422 error,
/docs-এর প্রতিটা schema — সবই Pydantic-এর কাজ। এখানে Pydantic ৩টা কাজ করে: input যাচাই, output ছাঁকা, docs তৈরি।
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
app = FastAPI()
# INPUT model — has password / password আছে
class UserCreate(BaseModel):
name: str = Field(min_length=1)
email: EmailStr
password: str = Field(min_length=8)
# OUTPUT model — NO password! / password নেই!
class UserOut(BaseModel):
id: int
name: str
email: EmailStr
db = {}
@app.post("/users/", response_model=UserOut, status_code=201)
def create_user(user: UserCreate):
new_id = len(db) + 1
db[new_id] = {"id": new_id, **user.model_dump()}
return db[new_id]
# ↑ this dict HAS the password...
# ...but response_model FILTERS it out! 🛡️
# ↑ dict-এ password আছে... কিন্তু response_model ছেঁকে ফেলে দেয়!
| # | Job | কাজ |
|---|---|---|
| 1 | Validates input — bad JSON → automatic 422 | Input যাচাই — খারাপ JSON → স্বয়ংক্রিয় 422 |
| 2 | Filters output — response_model drops extra fields (like password) | Output ছাঁকে — বাড়তি field (যেমন password) বাদ |
| 3 | Makes docs — open /docs, everything is documented | Docs বানায় — /docs খুলুন, সব লেখা আছে |
Path + Query + Body together / তিনটা একসাথে
class ReviewIn(BaseModel):
rating: int = Field(ge=1, le=5)
comment: str = Field(max_length=500)
@app.post("/products/{product_id}/reviews")
def add_review(
product_id: int, # in the URL path → path param
notify: bool = False, # simple + not in path → query param
review: ReviewIn = ..., # Pydantic model → request body
):
return {"ok": True}
🧠
How FastAPI decides / FastAPI যেভাবে ঠিক করে: in the path → path param · Pydantic model → body · everything else → query param. — path-এ আছে → path param · Pydantic model → body · বাকি সব → query param। একটাই নিয়ম, শূন্য config।
9
Pydantic for ML
ML Model Serve করা
EN
An ML model is the pickiest data user you'll ever have. Wrong order, a missing value, or a number outside the training range — and it quietly gives garbage predictions. Pydantic is the guard at your
/predict door.
বাং
ML model হলো ডেটার সবচেয়ে খুঁতখুঁতে ব্যবহারকারী। ক্রম ভুল, মান missing, বা training range-এর বাইরের সংখ্যা — model চুপচাপ আবর্জনা prediction দেবে। Pydantic হলো আপনার
/predict দরজার প্রহরী।
from enum import Enum
from pydantic import BaseModel, Field
import joblib
model = joblib.load("iris_model.pkl")
# INPUT — exactly what the model NEEDS / model-এর যা লাগবেই
class IrisFeatures(BaseModel):
sepal_length: float = Field(gt=0, le=10)
sepal_width: float = Field(gt=0, le=10)
petal_length: float = Field(gt=0, le=10)
petal_width: float = Field(gt=0, le=10)
def to_vector(self):
# GUARANTEED correct order / নিশ্চিত সঠিক ক্রম
return [self.sepal_length, self.sepal_width,
self.petal_length, self.petal_width]
# OUTPUT — typed prediction / typed prediction
class Prediction(BaseModel):
species: str
confidence: float = Field(ge=0, le=1)
@app.post("/predict", response_model=Prediction)
def predict(features: IrisFeatures):
# Here, data is COMPLETE, TYPED, IN-RANGE — guaranteed ✅
# এই লাইনে ডেটা সম্পূর্ণ, typed, সীমার মধ্যে — নিশ্চিত
proba = model.predict_proba([features.to_vector()])[0]
idx = proba.argmax()
return Prediction(species=str(idx), confidence=round(float(proba[idx]), 4))
Batch prediction — protect your server / সার্ভার বাঁচান
class BatchRequest(BaseModel):
rows: list[IrisFeatures] = Field(min_length=1, max_length=256)
# max_length stops a 1-million-row request from killing your CPU!
# max_length ১০ লাখ row-এর request থেকে CPU বাঁচায়!
📉
Catch data drift at the door / দরজাতেই drift ধরুন: Set
le=/ge= to your training data's range. If callers send values far outside it, they get a clear 422 — and the failed requests become a signal that reality has drifted. — le=/ge= দিন training range অনুযায়ী। বাইরের মান এলে পরিষ্কার 422 — আর fail হওয়া request-ই সংকেত দেবে যে বাস্তব বদলে গেছে।
🎯
The golden pattern / সোনালি প্যাটার্ন:
Features (in) + Prediction (out) + to_vector() = a complete contract for any sklearn / XGBoost / PyTorch model. — যেকোনো ML model-এর জন্য এটাই সম্পূর্ণ চুক্তি।
10
Pydantic for LLM
Chat ও RAG অ্যাপে
EN
LLM apps have two risky doors: (1) user input going to the model, (2) the model's reply coming back. Pydantic guards both. Start with the request going in.
বাং
LLM অ্যাপে দুটো ঝুঁকির দরজা: (১) user input যা model-এর দিকে যায়, (২) model-এর উত্তর যা ফিরে আসে। Pydantic দুটোই পাহারা দেয়। শুরু করি ভেতরে যাওয়া request দিয়ে।
from typing import Literal
from pydantic import BaseModel, Field
class Message(BaseModel):
role: Literal["system", "user", "assistant"] # ONLY these 3 / শুধু ৩টা
content: str = Field(min_length=1, max_length=32_000)
class ChatRequest(BaseModel):
messages: list[Message] = Field(min_length=1, max_length=100)
temperature: float = Field(default=0.7, ge=0, le=2)
max_tokens: int = Field(default=1024, ge=1, le=8192)
| Rule | What it buys you | কী দেয় |
|---|---|---|
Literal[...] on role | a typo like "users" fails instantly | "users"-এর মতো typo সাথে সাথে আটকায় |
max_length on content | protects your token budget & wallet | token budget ও পকেট বাঁচায় |
max_length on messages | caps chat history size | history-র আকার সীমিত রাখে |
ge/le on temperature | enforces valid range before the API | API-র আগেই বৈধ সীমা প্রয়োগ |
RAG query model / RAG-এর request model
class RAGQuery(BaseModel):
question: str = Field(min_length=3, max_length=2000)
top_k: int = Field(default=5, ge=1, le=20) # how many chunks / কতগুলো
collection: str = Field(pattern=r"^[a-z0-9_-]+$") # safe names only
🛡️
EN: The
বাং:
pattern on collection is quiet security — it blocks path-traversal names like ../secrets from ever reaching your vector DB.বাং:
collection-এর pattern নীরব নিরাপত্তা — ../secrets-এর মতো নাম vector DB পর্যন্ত পৌঁছাতেই দেয় না।
11
LLM Structured Output
সবচেয়ে দামি প্যাটার্ন ⭐
EN
This is the #1 most valuable pattern in modern AI work. LLMs give you text; your program needs data. The recipe in 4 steps: 1) define a model, 2) send its JSON schema to the LLM, 3) validate the reply, 4) if it fails, send the error back and let the LLM fix it.
বাং
এটাই আধুনিক AI-এর সবচেয়ে দামি প্যাটার্ন। LLM দেয় text; আপনার দরকার data। ৪ ধাপের রেসিপি: ১) model লিখুন, ২) তার JSON schema LLM-কে দিন, ৩) উত্তর validate করুন, ৪) fail করলে error ফেরত পাঠিয়ে LLM-কে ঠিক করতে বলুন।
import json, anthropic
from pydantic import BaseModel, Field, ValidationError
client = anthropic.Anthropic()
# STEP 1 — the shape you want / যে আকৃতি চান
class ResumeInfo(BaseModel):
name: str
years_experience: int = Field(ge=0, le=60)
skills: list[str] = Field(min_length=1)
seniority: str = Field(pattern=r"^(junior|mid|senior|lead)$")
def extract(text, retries=2):
# STEP 2 — hand the schema to the LLM / LLM-কে schema দিন
schema = json.dumps(ResumeInfo.model_json_schema())
prompt = f"Extract as JSON matching this schema. JSON only.\n{schema}\n\n{text}"
for _ in range(retries + 1):
resp = client.messages.create(model="claude-sonnet-4-6",
max_tokens=1000,
messages=[{"role":"user","content":prompt}])
raw = resp.content[0].text.strip()
raw = raw.removeprefix("\`\`\`json").removesuffix("\`\`\`")
try:
# STEP 3 — validate / validate করুন
return ResumeInfo.model_validate_json(raw)
except ValidationError as e:
# STEP 4 — retry WITH the error as the new prompt!
# error-টাকেই নতুন prompt বানিয়ে আবার চেষ্টা!
prompt = f"Your JSON had errors:\n{e}\nFix & return JSON only:\n{raw}"
raise RuntimeError("LLM could not produce valid JSON / পারল না")
🔁
Why the retry loop works / retry কেন কাজ করে: Pydantic's errors are so precise ("years_experience: should be a valid integer") that the LLM almost always self-corrects in one try. You're using validation errors as prompts. — Pydantic-এর error এতই নিখুঁত যে LLM প্রায় সবসময় এক চেষ্টাতেই শুধরে নেয়। আপনি error-কেই prompt বানাচ্ছেন।
Tool / function calling / Tool calling
class WeatherQuery(BaseModel):
"""Get current weather for a city."""
city: str = Field(description="City name, e.g. 'Dhaka'")
unit: str = Field(default="celsius", pattern=r"^(celsius|fahrenheit)$")
# ONE model = docs + schema-for-LLM + validator-for-reply
# একটাই model = docs + LLM-এর schema + উত্তরের validator
tool = {
"name": "get_weather",
"description": WeatherQuery.__doc__,
"input_schema": WeatherQuery.model_json_schema(), # one line! ⚡
}
# When the LLM calls the tool, validate ITS arguments too:
def handle(args: dict):
q = WeatherQuery.model_validate(args) # LLM's args, checked! ✅
return fetch_weather(q.city, q.unit)
📦
EN: Libraries like Instructor, LangChain, and PydanticAI just package this exact pattern — now you know what's under the hood.
বাং: Instructor, LangChain, PydanticAI ঠিক এই প্যাটার্নটাই প্যাকেজ করে — এখন আপনি জানেন ভেতরে কী আছে।
বাং: Instructor, LangChain, PydanticAI ঠিক এই প্যাটার্নটাই প্যাকেজ করে — এখন আপনি জানেন ভেতরে কী আছে।
12
Advanced Patterns
এডভান্সড — কাজে লাগবে এমন কৌশল
1 · Enum & Literal — fixed choices / নির্দিষ্ট option
EN When only a few values are allowed (status = pending/running/done), use
Enum or Literal. Anything else → clear error listing the valid options.বাং যখন শুধু কয়েকটা মান চলবে (status = pending/running/done),
Enum বা Literal ব্যবহার করুন। অন্য কিছু দিলে বৈধ option দেখিয়ে পরিষ্কার error।from enum import Enum
class Status(str, Enum): # str + Enum → JSON-friendly
pending = "pending"
running = "running"
done = "done"
class Task(BaseModel):
status: Status = Status.pending
Task(status="running") # ✅
Task(status="paused") # ❌ lists the 3 valid values / ৩টা বৈধ মান দেখায়
2 · Immutable (frozen) models / অপরিবর্তনীয় model
from pydantic import BaseModel, ConfigDict
class Config(BaseModel):
model_config = ConfigDict(frozen=True) # can't change after creation
temperature: float
c = Config(temperature=0.7)
c.temperature = 1.5 # ❌ frozen! "who changed my settings?" bugs vanish
# "কে আমার settings বদলাল?" bug উধাও
3 · computed_field — auto-calculated output / হিসাব-করা মান
from pydantic import BaseModel, computed_field
class Usage(BaseModel):
input_tokens: int
output_tokens: int
@computed_field
@property
def total(self) -> int:
return self.input_tokens + self.output_tokens
Usage(input_tokens=1200, output_tokens=400).model_dump()
# {'input_tokens': 1200, 'output_tokens': 400, 'total': 1600}
# 'total' auto-appears in the JSON! / নিজে থেকেই JSON-এ যোগ হয়!
4 · TypeAdapter — validate without a class / class ছাড়াই যাচাই
from pydantic import TypeAdapter
# "Just check this is a list of floats" — no model needed
# "শুধু দেখো এটা float-এর list কিনা" — model লাগবে না
adapter = TypeAdapter(list[float])
embedding = adapter.validate_python([0.12, -0.5, 0.98]) # ✅ great for embeddings
13
Cheat Sheet
শেষ কথা ও চিটশিট
7 golden rules / ৭টি সোনালি নিয়ম
| # | Rule (EN) | নিয়ম (বাংলা) |
|---|---|---|
| 1 | Validate at every boundary — inside, trust your types | প্রতিটা সীমান্তে যাচাই — ভেতরে type-কে বিশ্বাস |
| 2 | Separate In / Out models (password in one, never the other) | In / Out আলাদা রাখুন (password একটাতে, অন্যটাতে কখনো না) |
| 3 | Constrain to reality (trained on 0–10 → le=10) | বাস্তবতা অনুযায়ী সীমা (0–10 হলে le=10) |
| 4 | Name by direction: ...Request / ...Prediction | দিক অনুযায়ী নাম: ...Request / ...Prediction |
| 5 | Cap every list with max_length (saves compute & bill) | প্রতি list-এ max_length (compute ও বিল বাঁচায়) |
| 6 | SecretStr for anything secret | গোপন সবকিছুতে SecretStr |
| 7 | Fail at startup, not at 3 AM (validate config with BaseSettings) | রাত ৩টায় নয়, startup-এ fail করুন |
Method cheat sheet / Method চিটশিট
| Method | Does what | কী করে |
|---|---|---|
Model(**kwargs) | create + validate | তৈরি + যাচাই |
.model_validate(dict) | validate a dict | dict যাচাই |
.model_validate_json(str) | validate raw JSON | কাঁচা JSON যাচাই |
.model_dump() | model → dict | model → dict |
.model_dump_json() | model → JSON string | model → JSON string |
.model_json_schema() | model → JSON Schema (for LLMs!) | model → JSON Schema (LLM-এর জন্য!) |
When to use what / কখন কোনটা
| Situation / পরিস্থিতি | Use / ব্যবহার |
|---|---|
| Simple type + range / সাধারণ type + সীমা | Field(gt=..., le=...) |
| One field custom logic / এক field নিজস্ব logic | @field_validator |
| Rule across fields / একাধিক field জুড়ে | @model_validator(mode="after") |
| Clean messy input / ময়লা input পরিষ্কার | mode="before" |
| Fixed choices / নির্দিষ্ট option | Enum / Literal |
| App config / অ্যাপ config | BaseSettings |
| Never changes / কখনো বদলাবে না | frozen=True |
🏁
The whole book in one line / পুরো বই এক লাইনে: Pydantic turns your type hints into enforceable contracts — guarding every boundary of a modern backend, ML, or LLM system. Learn one library, protect the whole system. — Pydantic আপনার type hint-কে বলবৎযোগ্য চুক্তিতে বদলায় — backend, ML, LLM-এর প্রতিটা সীমান্ত পাহারা দেয়। একটা লাইব্রেরি শিখুন, পুরো সিস্টেম রক্ষা করুন।
🎯 Practice projects / অনুশীলন প্রজেক্ট
- User API — build
UserCreate/UserOut; make sure password never appears in any response. — password যেন কোনো response-এ না থাকে। - ML endpoint — wrap any sklearn model with
Features+Prediction, batch capped at 100. — batch ১০০-তে সীমিত। - LLM extractor — use the schema→validate→retry loop to pull structured data from messy text. — অগোছালো text থেকে structured ডেটা বের করুন।
- Config refactor — move every hardcoded value into a
Settingsclass with a.env. — সব hardcoded মান.env-সহSettings-এ সরান।