1. Home
  2. FastApi
  3. Basic Fundamental
  4. Day 2 : Student Crud Sqli...
  5. Saerch and pagination

Saerch and pagination


🔍 ধাপ ১: Search ফিচার যোগ করুন

ফাইল: app/apps/students/routes.py

আপনার বিদ্যমান get_all_students ফাংশনকে এভাবে পরিবর্তন করুন:

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from app.db.session import get_db
from . import models, schemas

router = APIRouter(prefix="/students", tags=["Students"])

# ১. Create (তৈরি করা) - এটি আগের মতোই থাকবে
@router.post("/", response_model=schemas.StudentResponse)
def create_student(student: schemas.StudentCreate, db: Session = Depends(get_db)):
    """নতুন স্টুডেন্ট তৈরি করা"""
    db_student = models.Student(**student.dict())
    db.add(db_student)
    db.commit()
    db.refresh(db_student)
    return db_student

# ২. Read All with Search (সার্চ সহ সব দেখা) - নতুন ভার্সন
@router.get("/", response_model=List[schemas.StudentResponse])
def get_all_students(
    db: Session = Depends(get_db),
    search: Optional[str] = Query(None, description="নাম বা ইমেইল দিয়ে সার্চ করুন")
):
    """
    সব স্টুডেন্ট দেখা (সার্চ সহ)
    
    Query Parameters:
    - search: নাম বা ইমেইল দিয়ে সার্চ করুন (অপশনাল)
    
    উদাহরণ:
    - GET /students/ - সব স্টুডেন্ট
    - GET /students/?search=আহমেদ - নাম দিয়ে সার্চ
    - GET /students/?search=ahmed@example.com - ইমেইল দিয়ে সার্চ
    """
    query = db.query(models.Student)
    
    # যদি সার্চ টার্ম থাকে তাহলে ফিল্টার করুন
    if search:
        # নাম বা ইমেইল দিয়ে সার্চ করুন (কেস-ইনসেনসিটিভ)
        query = query.filter(
            (models.Student.name.ilike(f"%{search}%")) |
            (models.Student.email.ilike(f"%{search}%"))
        )
    
    return query.all()

# ৩. Read One (একটি দেখা) - এটি আগের মতোই থাকবে
@router.get("/{student_id}", response_model=schemas.StudentResponse)
def get_student(student_id: int, db: Session = Depends(get_db)):
    """নির্দিষ্ট স্টুডেন্ট দেখা"""
    db_student = db.query(models.Student).filter(models.Student.id == student_id).first()
    if not db_student:
        raise HTTPException(status_code=404, detail="Student not found")
    return db_student

# ৪. Update (পরিবর্তন করা) - এটি আগের মতোই থাকবে
@router.put("/{student_id}", response_model=schemas.StudentResponse)
def update_student(student_id: int, student_data: schemas.StudentUpdate, db: Session = Depends(get_db)):
    """স্টুডেন্ট আপডেট করা"""
    db_student = db.query(models.Student).filter(models.Student.id == student_id).first()
    
    if not db_student:
        raise HTTPException(status_code=404, detail="Student not found")
    
    updated_info = student_data.dict(exclude_unset=True)
    
    for key, value in updated_info.items():
        setattr(db_student, key, value)
    
    db.commit()
    db.refresh(db_student)
    return db_student

# ৫. Delete (মুছে ফেলা) - এটি আগের মতোই থাকবে
@router.delete("/{student_id}")
def delete_student(student_id: int, db: Session = Depends(get_db)):
    """স্টুডেন্ট মুছে ফেলা"""
    db_student = db.query(models.Student).filter(models.Student.id == student_id).first()
    if not db_student:
        raise HTTPException(status_code=404, detail="Student not found")
    
    db.delete(db_student)
    db.commit()
    return {"message": "Successfully Deleted"}

📝 বিস্তারিত আলোচনা:

Query(None, description="..."): এটি একটি Query Parameter। URL এ ?search=value এর মাধ্যমে পাঠানো হয়।

Optional[str]: এই প্যারামিটার অপশনাল। না পাঠালেও চলবে।

ilike(): Case-insensitive LIKE অপারেটর। বড় ছোট হরফ নিয়ে চিন্তা করে না।

f"%{search}%": সার্চ টার্মের আগে এবং পরে % যোগ করে। এটি যেকোনো জায়গায় ম্যাচ করে।

| (OR অপারেটর): নাম বা ইমেইল যেকোনো একটিতে ম্যাচ করলে রেজাল্ট দেবে।


📄 ধাপ ২: Pagination ফিচার যোগ করুন

ফাইল: app/apps/students/schemas.py

প্রথমে Pagination এর জন্য একটি নতুন স্কিমা যোগ করুন:

from pydantic import BaseModel, EmailStr
from typing import Optional, List

# কমন ডাটা
class StudentBase(BaseModel):
    name: str
    email: EmailStr
    age: int

# ইনপুট নেওয়ার জন্য (Create)
class StudentCreate(StudentBase):
    pass

# আপডেট করার জন্য
class StudentUpdate(BaseModel):
    name: Optional[str] = None
    email: Optional[EmailStr] = None
    age: Optional[int] = None

# ডাটা দেখানোর জন্য (Response)
class StudentResponse(StudentBase):
    id: int

    class Config:
        from_attributes = True

# Pagination এর জন্য নতুন স্কিমা
class PaginatedStudentResponse(BaseModel):
    """Pagination সহ স্টুডেন্ট লিস্ট"""
    total: int  # মোট স্টুডেন্ট সংখ্যা
    page: int  # বর্তমান পেজ
    page_size: int  # প্রতি পেজে কতটি আইটেম
    total_pages: int  # মোট পেজ সংখ্যা
    data: List[StudentResponse]  # স্টুডেন্ট লিস্ট

ফাইল: app/apps/students/routes.py

এখন Pagination সহ একটি নতুন এন্ডপয়েন্ট যোগ করুন:

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from math import ceil
from app.db.session import get_db
from . import models, schemas

router = APIRouter(prefix="/students", tags=["Students"])

# ... আগের সব ফাংশন এখানে থাকবে ...

# নতুন এন্ডপয়েন্ট: Pagination সহ সার্চ
@router.get("/search/paginated", response_model=schemas.PaginatedStudentResponse)
def search_students_paginated(
    db: Session = Depends(get_db),
    search: Optional[str] = Query(None, description="নাম বা ইমেইল দিয়ে সার্চ করুন"),
    page: int = Query(1, ge=1, description="পেজ নম্বর (১ থেকে শুরু)"),
    page_size: int = Query(10, ge=1, le=100, description="প্রতি পেজে কতটি আইটেম (১-১০০)")
):
    """
    Pagination সহ স্টুডেন্ট সার্চ করুন
    
    Query Parameters:
    - search: নাম বা ইমেইল দিয়ে সার্চ করুন (অপশনাল)
    - page: পেজ নম্বর (ডিফল্ট: ১)
    - page_size: প্রতি পেজে কতটি আইটেম (ডিফল্ট: ১০, ম্যাক্স: ১০০)
    
    উদাহরণ:
    - GET /students/search/paginated - প্রথম পেজ (১০টি আইটেম)
    - GET /students/search/paginated?page=2 - দ্বিতীয় পেজ
    - GET /students/search/paginated?search=আহমেদ&page=1&page_size=5 - সার্চ সহ
    """
    
    # বেস কোয়েরি তৈরি করুন
    query = db.query(models.Student)
    
    # যদি সার্চ টার্ম থাকে তাহলে ফিল্টার করুন
    if search:
        query = query.filter(
            (models.Student.name.ilike(f"%{search}%")) |
            (models.Student.email.ilike(f"%{search}%"))
        )
    
    # মোট রেকর্ড সংখ্যা
    total = query.count()
    
    # মোট পেজ সংখ্যা
    total_pages = ceil(total / page_size)
    
    # Offset এবং Limit ক্যালকুলেট করুন
    offset = (page - 1) * page_size
    
    # ডাটা ফেচ করুন
    students = query.offset(offset).limit(page_size).all()
    
    return {
        "total": total,
        "page": page,
        "page_size": page_size,
        "total_pages": total_pages,
        "data": students
    }

📝 বিস্তারিত আলোচনা:

page: int = Query(1, ge=1): পেজ নম্বর। ge=1 মানে ১ এর চেয়ে বড় বা সমান হতে হবে।

page_size: int = Query(10, ge=1, le=100): প্রতি পেজে কতটি আইটেম। le=100 মানে ১০০ এর চেয়ে বেশি হতে পারবে না।

ceil(total / page_size): মোট পেজ সংখ্যা ক্যালকুলেট করে। যেমন: ২৫ টি আইটেম এবং ১০ প্রতি পেজ = ৩ পেজ।

offset = (page - 1) * page_size: কত সংখ্যক রেকর্ড স্কিপ করবে। পেজ ২ এবং পেজ সাইজ ১০ হলে = ১০ স্কিপ করবে।

query.offset(offset).limit(page_size): নির্দিষ্ট সংখ্যক রেকর্ড স্কিপ করে বাকিটা নিয়ে আসে।


🎯 ধাপ ৩: উন্নত Search ফিচার

ফাইল: app/apps/students/routes.py

আরও উন্নত সার্চ ফিচার যোগ করুন (নির্দিষ্ট ফিল্ড দিয়ে সার্চ):

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from math import ceil
from app.db.session import get_db
from . import models, schemas

router = APIRouter(prefix="/students", tags=["Students"])

# উন্নত সার্চ এন্ডপয়েন্ট
@router.get("/advanced-search", response_model=List[schemas.StudentResponse])
def advanced_search(
    db: Session = Depends(get_db),
    name: Optional[str] = Query(None, description="নাম দিয়ে সার্চ"),
    email: Optional[str] = Query(None, description="ইমেইল দিয়ে সার্চ"),
    age_min: Optional[int] = Query(None, ge=0, description="ন্যূনতম বয়স"),
    age_max: Optional[int] = Query(None, ge=0, description="সর্বোচ্চ বয়স"),
    sort_by: Optional[str] = Query("id", description="সর্ট করার ফিল্ড (id, name, email, age)"),
    sort_order: Optional[str] = Query("asc", description="সর্ট অর্ডার (asc, desc)")
):
    """
    উন্নত সার্চ ফিচার
    
    Query Parameters:
    - name: নাম দিয়ে সার্চ
    - email: ইমেইল দিয়ে সার্চ
    - age_min: ন্যূনতম বয়স
    - age_max: সর্বোচ্চ বয়স
    - sort_by: সর্ট করার ফিল্ড
    - sort_order: সর্ট অর্ডার (asc/desc)
    
    উদাহরণ:
    - GET /students/advanced-search?name=আহমেদ
    - GET /students/advanced-search?age_min=18&age_max=25
    - GET /students/advanced-search?sort_by=name&sort_order=desc
    """
    
    query = db.query(models.Student)
    
    # নাম দিয়ে ফিল্টার
    if name:
        query = query.filter(models.Student.name.ilike(f"%{name}%"))
    
    # ইমেইল দিয়ে ফিল্টার
    if email:
        query = query.filter(models.Student.email.ilike(f"%{email}%"))
    
    # বয়স রেঞ্জ দিয়ে ফিল্টার
    if age_min is not None:
        query = query.filter(models.Student.age >= age_min)
    
    if age_max is not None:
        query = query.filter(models.Student.age <= age_max)
    
    # সর্ট করুন
    if sort_by == "name":
        sort_column = models.Student.name
    elif sort_by == "email":
        sort_column = models.Student.email
    elif sort_by == "age":
        sort_column = models.Student.age
    else:
        sort_column = models.Student.id
    
    if sort_order == "desc":
        query = query.order_by(sort_column.desc())
    else:
        query = query.order_by(sort_column.asc())
    
    return query.all()

📊 ধাপ ৪: API টেস্ট করুন

সাধারণ সার্চ:

GET /students/?search=আহমেদ

রেসপন্স:

[
  {
    "id": 1,
    "name": "আহমেদ",
    "email": "ahmed@example.com",
    "age": 20
  }
]

Pagination সহ সার্চ:

GET /students/search/paginated?search=আহমেদ&page=1&page_size=5

রেসপন্স:

{
  "total": 15,
  "page": 1,
  "page_size": 5,
  "total_pages": 3,
  "data": [
    {
      "id": 1,
      "name": "আহমেদ",
      "email": "ahmed@example.com",
      "age": 20
    },
    {
      "id": 2,
      "name": "আহমেদ আলী",
      "email": "ahmed.ali@example.com",
      "age": 21
    }
  ]
}

উন্নত সার্চ:

GET /students/advanced-search?age_min=18&age_max=25&sort_by=name&sort_order=asc

রেসপন্স:

[
  {
    "id": 2,
    "name": "আহমেদ আলী",
    "email": "ahmed.ali@example.com",
    "age": 21
  },
  {
    "id": 1,
    "name": "আহমেদ",
    "email": "ahmed@example.com",
    "age": 20
  }
]

🔧 ধাপ ৫: ফ্রন্টএন্ড থেকে কীভাবে কল করবেন

JavaScript/Fetch উদাহরণ:

// সাধারণ সার্চ
async function searchStudents(searchTerm) {
  const response = await fetch(`/students/?search=${searchTerm}`);
  const data = await response.json();
  console.log(data);
}

// Pagination সহ সার্চ
async function searchWithPagination(searchTerm, page, pageSize) {
  const response = await fetch(
    `/students/search/paginated?search=${searchTerm}&page=${page}&page_size=${pageSize}`
  );
  const data = await response.json();
  console.log(data);
}

// উন্নত সার্চ
async function advancedSearch(ageMin, ageMax, sortBy) {
  const response = await fetch(
    `/students/advanced-search?age_min=${ageMin}&age_max=${ageMax}&sort_by=${sortBy}`
  );
  const data = await response.json();
  console.log(data);
}

Python/Requests উদাহরণ:

import requests

# সাধারণ সার্চ
response = requests.get("http://localhost:8000/students/", params={"search": "আহমেদ"})
print(response.json())

# Pagination সহ সার্চ
response = requests.get(
    "http://localhost:8000/students/search/paginated",
    params={"search": "আহমেদ", "page": 1, "page_size": 5}
)
print(response.json())

# উন্নত সার্চ
response = requests.get(
    "http://localhost:8000/students/advanced-search",
    params={"age_min": 18, "age_max": 25, "sort_by": "name"}
)
print(response.json())

⚡ পারফরম্যান্স অপটিমাইজেশন

১. ডাটাবেস ইন্ডেক্স যোগ করুন

ফাইল: app/apps/students/models.py

from sqlalchemy import Column, Integer, String, Index
from app.db.session import Base

class Student(Base):
    __tablename__ = "students"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)  # সার্চের জন্য ইন্ডেক্স
    email = Column(String, unique=True, index=True)
    age = Column(Integer, index=True)  # ফিল্টারের জন্য ইন্ডেক্স
    
    # কম্পোজিট ইন্ডেক্স (একাধিক কলাম)
    __table_args__ = (
        Index('idx_name_email', 'name', 'email'),
    )

২. Limit যোগ করুন

সার্চ রেজাল্ট খুব বেশি হলে সীমিত করুন:

@router.get("/", response_model=List[schemas.StudentResponse])
def get_all_students(
    db: Session = Depends(get_db),
    search: Optional[str] = Query(None),
    limit: int = Query(100, le=1000)  # ম্যাক্স ১০০০
):
    query = db.query(models.Student)
    
    if search:
        query = query.filter(
            (models.Student.name.ilike(f"%{search}%")) |
            (models.Student.email.ilike(f"%{search}%"))
        )
    
    return query.limit(limit).all()

⚠️ সাধারণ সমস্যা এবং সমাধান

সমস্যা ১: সার্চ খুব ধীর

কারণ: ইন্ডেক্স নেই বা ডাটা খুব বেশি।

সমাধান:

  • ডাটাবেস ইন্ডেক্স যোগ করুন
  • Pagination ব্যবহার করুন
  • LIMIT যোগ করুন

সমস্যা ২: “Invalid page number”

কারণ: পেজ নম্বর ০ বা নেগেটিভ।

সমাধান: ge=1 ভ্যালিডেশন ব্যবহার করুন (ইতিমধ্যে করা আছে)।


সমস্যা ৩: সার্চ কেস-সেনসিটিভ

কারণ: like() ব্যবহার করছেন ilike() এর পরিবর্তে।

সমাধান: সবসময় ilike() ব্যবহার করুন।


📋 API এন্ডপয়েন্ট সারসংক্ষেপ

এন্ডপয়েন্টমেথডকাজ
/students/GETসাধারণ সার্চ সহ সব দেখা
/students/search/paginatedGETPagination সহ সার্চ
/students/advanced-searchGETউন্নত সার্চ (ফিল্ড নির্দিষ্ট)
/students/POSTনতুন স্টুডেন্ট তৈরি
/students/{id}GETএকটি স্টুডেন্ট দেখা
/students/{id}PUTস্টুডেন্ট আপডেট
/students/{id}DELETEস্টুডেন্ট মুছে ফেলা

🎯 পরবর্তী ধাপ

এখন আপনার কাছে সম্পূর্ণ Search এবং Pagination সিস্টেম আছে। আপনি যা করতে পারেন:

  1. ফিল্টারিং যোগ করুন – আরও উন্নত ফিল্টার
  2. সর্টিং অপশন বাড়ান – একাধিক ফিল্ড দিয়ে সর্ট
  3. এক্সপোর্ট ফিচার যোগ করুন – CSV/Excel এ এক্সপোর্ট
  4. ক্যাশিং যোগ করুন – রেডিস দিয়ে পারফরম্যান্স বাড়ান

📚 রেফারেন্স


আপনি চাইলে আমি এটাকে WordPress/Gutenberg-friendly ব্লক ফরম্যাটেও সাজিয়ে দিতে পারি।

How can we help?