OzbekPython / Bob 22

SQLite bilan maʼlumotlar bazasi

Python içiga örnatilgan sqlite3 moduli yordamida jadval yaratiş, maʼlumot qöşiş, qidiriş, yangilaş va öçirişni örganamiz.

Örta daraja22 daqiqa18 kod misoli22 / 24

Maʼlumotlar bazasi nima?

Tasavvur qiling, siz kiçik kitob dökoni yoki maktab kutubxonasini yuritasiz. Avval kitoblarni daftarga yozasiz, keyin Excel jadvaliga ötkazasiz. Kitoblar soni minglab bölganda esa savollar qiyinlaşadi: «1930-yildan oldin çiqqan, 300 sahifadan qalin kitoblar qaysilar?», «Bir vaqtning özida ikki kişi yozsa nima böladi?».

Maʼlumotlar bazasi (maʼlumotlar ombori) — maʼlumotlarni tartibli saqlaş, tez qidiriş va xavfsiz özgartiriş uçun möljallangan dastur. Eng keng tarqalgan turi — relyatsion bazalar: ularda maʼlumot jadvallarda (qator va ustunlarda) saqlanadi, ular bilan esa maxsus til — SQL (Structured Query Language) orqali gaplaşiladi.

Relyatsion bazalarga misollar: PostgreSQL, MySQL, SQLite. Bu bobda biz SQLite bilan işlaymiz, çunki:

  • u alohida server talab qilmaydi — butun baza bitta fayl içida saqlanadi;
  • Python bilan birga keladi: sqlite3 modulini pip orqali örnatiş şart emas;
  • mobil ilovalar, brauzerlar va köplab dasturlar içida aynan şu baza işlaydi;
  • bu yerda örgangan SQL bilimingiz boşqa bazalarda ham deyarli özgarişsiz işlaydi.
TuşunçaMaʼnosiKundalik öxşatiş
Baza (database)Barça jadvallar töplamiButun kutubxona
Jadval (table)Bir turdagi yozuvlarKitoblar röyxati daftari
Ustun (column)Yozuvning bitta xususiyati«Muallif» grafasi
Qator (row)Bitta yozuvBitta kitob haqidagi satr
Birlamçi kalit (PRIMARY KEY)Har bir qatorning noyob raqamiKitobning inventar raqami

Bazaga ulaniş

Işni sqlite3.connect() funksiyasidan boşlaymiz. U Connection (ulaniş) obyektini qaytaradi. Agar fayl nomini bersangiz (masalan "kutubxona.db"), baza öşa faylda saqlanadi. Maxsus ":memory:" nomi esa bazani faqat operativ xotirada yaratadi — dastur tugaşi bilan u yöqoladi. Maşq qiliş uçun bu juda qulay, şuning uçun bu bobdagi işga tuşiriladigan misollar aynan şundan foydalanadi.

SQL buyruqlarini bajariş uçun cursor() (kursor) obyekti kerak. Kursor — baza içida «körsatkiç» vazifasini bajaradi: buyruqni yuboradi va natijalarni qatorma-qator olib keladi.

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
kursor = ulanish.cursor()

kursor.execute("""
    CREATE TABLE kitoblar (
        id      INTEGER PRIMARY KEY AUTOINCREMENT,
        nomi    TEXT NOT NULL,
        muallif TEXT NOT NULL,
        yil     INTEGER,
        sahifa  INTEGER
    )
""")

# Bazadagi jadvallar röyxatini köramiz
kursor.execute("SELECT name FROM sqlite_master WHERE type = 'table'")
print(kursor.fetchall())

ulanish.close()

Natija:

Natija
[('kitoblar',), ('sqlite_sequence',)]

sqlite_sequence jadvalini SQLite özi yaratdi: u AUTOINCREMENT ustunining oxirgi qiymatini eslab qoladi. Ustun turlari haqida qisqaça:

SQLite turiPython turiMisol
INTEGERint1926
REALfloat4.5
TEXTstr"Ötkan kunlar"
BLOBbytesrasm, fayl
NULLNoneqiymat yöq

NOT NULL — bu ustun böş qolişi mumkin emasligini bildiradi. Agar jadval allaqaçon mavjud bölişi mumkin bölsa, CREATE TABLE IF NOT EXISTS yoziş odat tusiga kirgan — şunda dastur ikkinçi marta işga tuşganda xato bermaydi.

Maʼlumot qöşiş: INSERT va ? belgilari

Jadvalga yangi qator qöşiş uçun INSERT INTO buyruği işlatiladi. Qiymatlarni SQL matniga töğridan-töğri yozmaymiz, balki ? belgisini qöyib, qiymatlarni alohida kortej (tuple) sifatida beramiz:

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("""CREATE TABLE kitoblar (
    id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)""")

kitob = ("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400)
kursor = ulanish.execute(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
    kitob,
)
ulanish.commit()  # özgarişni bazaga muhrlaymiz

print("Yangi kitob id raqami:", kursor.lastrowid)
print("Qöşilgan qatorlar soni:", kursor.rowcount)

Natija:

Natija
Yangi kitob id raqami: 1
Qöşilgan qatorlar soni: 1

Bu yerda ikki muhim narsaga eʼtibor bering:

  • ulanish.execute() — qisqa yöl: u içida yangi kursor yaratadi va uni qaytaradi. Kiçik dasturlarda şunday yoziş qulay.
  • commit() — özgarişlarni doimiy qiladi. Uni çaqirmasangiz, ulaniş yopilganda qöşilgan maʼlumotlar yöqolişi mumkin.

Nega f-string bilan SQL yoziş xavfli? (SQL injection)

Yangi boşlovçilar köpinça şunday yozişni xohlaydi: f"SELECT * FROM foydalanuvçilar WHERE login = '{login}'". Körinişidan qulay, lekin bu — eng maşhur xavfsizlik teşiklaridan biri, uning nomi SQL injection (SQL inʼeksiyasi).

Muammo şundaki, foydalanuvçi kiritgan matn SQL buyruğining bir qismiga aylanib qoladi. Agar u oddiy login örniga maxsus tuzilgan matn yozsa, buyruqning maʼnosini özgartirib yuboradi. Quyidagi misolda «hujumçi» parolni bilmasdan barça foydalanuvçilar röyxatini oladi:

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("CREATE TABLE foydalanuvçilar (login TEXT, parol TEXT)")
ulanish.executemany(
    "INSERT INTO foydalanuvçilar VALUES (?, ?)",
    [("aziza", "qwerty1"), ("bekzod", "12345"), ("admin", "Maxfiy!9")],
)

kiritilgan = "kimdir' OR '1'='1"  # foydalanuvçi yozgan «login»

# XAVFLI usul: matn SQL içiga yopiştiriladi
sorov = f"SELECT login, parol FROM foydalanuvçilar WHERE login = '{kiritilgan}'"
print("Hosil bölgan SQL:", sorov)
print("Xavfli natija:", ulanish.execute(sorov).fetchall())

# XAVFSIZ usul: qiymat alohida uzatiladi
xavfsiz = ulanish.execute(
    "SELECT login, parol FROM foydalanuvçilar WHERE login = ?", (kiritilgan,)
).fetchall()
print("Xavfsiz natija:", xavfsiz)

Natija:

Natija
Hosil bölgan SQL: SELECT login, parol FROM foydalanuvçilar WHERE login = 'kimdir' OR '1'='1'
Xavfli natija: [('aziza', 'qwerty1'), ('bekzod', '12345'), ('admin', 'Maxfiy!9')]
Xavfsiz natija: []

OR '1'='1' şarti har doim rost, şuning uçun birinçi sörov butun jadvalni qaytardi. ? işlatilganda esa sqlite3 qiymatni maʼlumot sifatida uzatadi, uni heç qaçon SQL kodi sifatida öqimaydi — bunday login yöq, natija böş.

Eslatib ötamiz: belgilar faqat qiymatlar örnida işlaydi. Jadval yoki ustun nomini ? bilan bera olmaysiz — ular dastur içida qatʼiy yozilişi kerak.

Köp qatorni birdaniga qöşiş: executemany()

Bitta-bitta INSERT qiliş örniga röyxatni executemany() ga beriş mumkin. Bundan taşqari, ? örniga nomlangan belgilar (:nomi) ham işlatiladi — şunda qiymatlarni luğat (dict) körinişida berasiz va tartibni adaştirib qöyiş xavfi kamayadi.

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("""CREATE TABLE kitoblar (
    id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)""")

kitoblar = [
    ("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400),
    ("Mehrobdan çayon", "Abdulla Qodiriy", 1929, 320),
    ("Keça va kunduz", "Çölpon", 1936, 280),
]
ulanish.executemany(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)", kitoblar
)

# Nomlangan belgilar bilan
yangi = [
    {"nomi": "Sarob", "muallif": "Abdulla Qahhor", "yil": 1937, "sahifa": 350},
    {"nomi": "Şum bola", "muallif": "Ğafur Ğulom", "yil": 1936, "sahifa": 150},
]
ulanish.executemany(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) "
    "VALUES (:nomi, :muallif, :yil, :sahifa)",
    yangi,
)
ulanish.commit()

soni = ulanish.execute("SELECT COUNT(*) FROM kitoblar").fetchone()[0]
print(f"Jadvalda {soni} ta kitob bor")

Natija:

Natija
Jadvalda 5 ta kitob bor

Maʼlumot öqiş: SELECT, WHERE, ORDER BY

SELECT — eng köp işlatiladigan buyruq. Uning asosiy qismlari:

  • SELECT ustunlar FROM jadval — qaysi ustunlarni oliş (* — hammasi);
  • WHERE shart — qaysi qatorlarni oliş (=, >, <, AND, OR, BETWEEN);
  • ORDER BY ustun — saralaş (ASC — ösiş, DESC — kamayiş tartibida);
  • LIMIT n — faqat birinçi n ta qatorni oliş.

Natijani olişning uç usuli bor: fetchone() — bitta qator (yoki None), fetchall() — barça qatorlar röyxati, fetchmany(n) — keyingi n ta qator. Kursorning özini for sikli bilan aylanib çiqiş ham mumkin — katta jadvallarda bu xotirani tejaydi.

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("""CREATE TABLE kitoblar (
    id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)""")
ulanish.executemany(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
    [
        ("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400),
        ("Mehrobdan çayon", "Abdulla Qodiriy", 1929, 320),
        ("Keça va kunduz", "Çölpon", 1936, 280),
        ("Sarob", "Abdulla Qahhor", 1937, 350),
        ("Şum bola", "Ğafur Ğulom", 1936, 150),
    ],
)

print("300 sahifadan qalin kitoblar (qalinidan boşlab):")
for nomi, sahifa in ulanish.execute(
    "SELECT nomi, sahifa FROM kitoblar WHERE sahifa > ? ORDER BY sahifa DESC", (300,)
):
    print(f"  {nomi:<18} {sahifa} bet")

print("\n1930-yilgaça çiqqanlar:")
qatorlar = ulanish.execute(
    "SELECT nomi, yil FROM kitoblar WHERE yil < ? ORDER BY yil", (1930,)
).fetchall()
print(qatorlar)

eng_yupqa = ulanish.execute(
    "SELECT nomi, sahifa FROM kitoblar ORDER BY sahifa LIMIT 1"
).fetchone()
print("\nEng yupqa kitob:", eng_yupqa)

Natija:

Natija
300 sahifadan qalin kitoblar (qalinidan boşlab):
  Ötkan kunlar       400 bet
  Sarob              350 bet
  Mehrobdan çayon    320 bet

1930-yilgaça çiqqanlar:
[('Ötkan kunlar', 1926), ('Mehrobdan çayon', 1929)]

Eng yupqa kitob: ('Şum bola', 150)

Matn böyiça qidiriş: LIKE

LIKE operatori naqş böyiça qidiradi: % — istalgan uzunlikdagi har qanday belgilar, _ — aynan bitta belgi. SQLite da LIKE lotin harflari uçun katta-kiçik harfni farqlamaydi.

Foydalanuvçi kiritgan sözni qidirganda ham ? dan foydalanamiz, % belgilarini esa Python tomonida qöşamiz:

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("""CREATE TABLE kitoblar (
    id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)""")
ulanish.executemany(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
    [
        ("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400),
        ("Mehrobdan çayon", "Abdulla Qodiriy", 1929, 320),
        ("Keça va kunduz", "Çölpon", 1936, 280),
        ("Sarob", "Abdulla Qahhor", 1937, 350),
    ],
)

def qidir(soz: str) -> list[tuple]:
    naqsh = f"%{soz}%"  # % ni qiymatga qöşamiz, SQL matniga emas
    return ulanish.execute(
        "SELECT nomi, muallif FROM kitoblar WHERE nomi LIKE ? OR muallif LIKE ?",
        (naqsh, naqsh),
    ).fetchall()

print(qidir("qodiriy"))
print(qidir("kun"))
print(qidir("Navoiy"))

# "Abdulla" bilan boşlanuvçi mualliflar soni
print(ulanish.execute(
    "SELECT COUNT(*) FROM kitoblar WHERE muallif LIKE ?", ("Abdulla%",)
).fetchone())

Natija:

Natija
[('Ötkan kunlar', 'Abdulla Qodiriy'), ('Mehrobdan çayon', 'Abdulla Qodiriy')]
[('Ötkan kunlar', 'Abdulla Qodiriy'), ('Keça va kunduz', 'Çölpon')]
[]
(3,)

Qulay qatorlar: row_factory = sqlite3.Row

Standart holatda har bir qator oddiy kortej bölib keladi va ustunlarga qator[2] kabi raqam bilan murojaat qilasiz. Ustunlar köpaysa, bu çalkaşlikka olib keladi. ulanish.row_factory = sqlite3.Row qilib qöysangiz, qatorlar ham raqam, ham nom böyiça öqiladigan böladi:

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.row_factory = sqlite3.Row
ulanish.execute("""CREATE TABLE kitoblar (
    id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)""")
ulanish.execute(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
    ("Keça va kunduz", "Çölpon", 1936, 280),
)

qator = ulanish.execute("SELECT * FROM kitoblar").fetchone()
print(qator["nomi"], "—", qator["muallif"])
print("Ustunlar:", qator.keys())
print("Luğat körinişida:", dict(qator))

Natija:

Natija
Keça va kunduz — Çölpon
Ustunlar: ['id', 'nomi', 'muallif', 'yil', 'sahifa']
Luğat körinişida: {'id': 1, 'nomi': 'Keça va kunduz', 'muallif': 'Çölpon', 'yil': 1936, 'sahifa': 280}

dict(qator) ayniqsa foydali: natijani JSON ga aylantiriş yoki şablonga uzatiş oson böladi.

Özgartiriş va öçiriş: UPDATE va DELETE

UPDATE mavjud qatorlarni özgartiradi, DELETE esa öçiradi. Ikkalasida ham WHERE juda muhim: uni unutsangiz, buyruq butun jadvalga taʼsir qiladi. Neçta qator özgarganini rowcount orqali bilib olamiz.

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("""CREATE TABLE kitoblar (
    id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)""")
ulanish.executemany(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
    [
        ("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400),
        ("Mehrobdan çayon", "Abdulla Qodiriy", 1929, 320),
        ("Sarob", "Abdulla Qahhor", 1937, 350),
    ],
)

# Yangi naşr: sahifa sonini yangilaymiz
k = ulanish.execute("UPDATE kitoblar SET sahifa = ? WHERE nomi = ?", (416, "Ötkan kunlar"))
print("Yangilandi:", k.rowcount)

# Bir muallifning barça kitoblariga 10 bet qöşamiz
k = ulanish.execute(
    "UPDATE kitoblar SET sahifa = sahifa + 10 WHERE muallif = ?", ("Abdulla Qodiriy",)
)
print("Yangilandi:", k.rowcount)

k = ulanish.execute("DELETE FROM kitoblar WHERE id = ?", (3,))
print("Öçirildi:", k.rowcount)
ulanish.commit()

for qator in ulanish.execute("SELECT id, nomi, sahifa FROM kitoblar"):
    print(qator)

Natija:

Natija
Yangilandi: 1
Yangilandi: 2
Öçirildi: 1
(1, 'Ötkan kunlar', 426)
(2, 'Mehrobdan çayon', 330)

Tranzaksiyalar: commit() va rollback()

Tranzaksiya — «hammasi yoki heç narsa» tamoyili bilan bajariladigan amallar guruhi. Klassik misol — bank ötkazmasi: bir hisobdan pul yeçiladi va boşqasiga tuşadi. Agar ikkinçi qadamda xato çiqsa, birinçi qadam ham bekor qilinişi kerak, aks holda pul «havoda» yöqoladi.

sqlite3 da özgartiruvçi buyruqlar (INSERT, UPDATE, DELETE) avtomatik tranzaksiya oçadi. commit() uni tasdiqlaydi, rollback() esa oxirgi commit() dan keyingi barça özgarişlarni bekor qiladi:

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("CREATE TABLE hisob (egasi TEXT PRIMARY KEY, balans INTEGER)")
ulanish.executemany("INSERT INTO hisob VALUES (?, ?)", [("Aziz", 500_000), ("Malika", 100_000)])
ulanish.commit()

def otkazma(kimdan: str, kimga: str, summa: int) -> None:
    try:
        ulanish.execute("UPDATE hisob SET balans = balans - ? WHERE egasi = ?", (summa, kimdan))
        balans = ulanish.execute("SELECT balans FROM hisob WHERE egasi = ?", (kimdan,)).fetchone()[0]
        if balans < 0:
            raise ValueError("Mablağ yetarli emas")
        ulanish.execute("UPDATE hisob SET balans = balans + ? WHERE egasi = ?", (summa, kimga))
        ulanish.commit()
        print(f"{summa:,} söm ötkazildi")
    except ValueError as xato:
        ulanish.rollback()
        print("Bekor qilindi:", xato)

otkazma("Aziz", "Malika", 200_000)
otkazma("Malika", "Aziz", 900_000)
print(ulanish.execute("SELECT * FROM hisob").fetchall())

Natija:

Natija
200,000 söm ötkazildi
Bekor qilindi: Mablağ yetarli emas
[('Aziz', 300000), ('Malika', 300000)]

Ikkinçi ötkazmada Malikaning balansi vaqtinça manfiy bölib qoldi, lekin rollback() uni oldingi holatga qaytardi.

with bilan tranzaksiya

Har safar try/except va commit/rollback yoziş zerikarli. Connection obyekti kontekst menejeri sifatida işlaydi: with ulanish: bloki xatosiz tugasa — avtomatik commit(), xato çiqsa — avtomatik rollback() qilinadi va xato yuqoriga uzatiladi.

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("CREATE TABLE kitoblar (id INTEGER PRIMARY KEY, nomi TEXT UNIQUE)")

with ulanish:
    ulanish.execute("INSERT INTO kitoblar (nomi) VALUES (?)", ("Sarob",))

try:
    with ulanish:
        ulanish.execute("INSERT INTO kitoblar (nomi) VALUES (?)", ("Şum bola",))
        ulanish.execute("INSERT INTO kitoblar (nomi) VALUES (?)", ("Sarob",))  # takror!
except sqlite3.IntegrityError as xato:
    print("Xato:", xato)

# "Şum bola" ham saqlanmadi — butun blok bekor qilindi
print(ulanish.execute("SELECT nomi FROM kitoblar").fetchall())
ulanish.close()

Natija:

Natija
Xato: UNIQUE constraint failed: kitoblar.nomi
[('Sarob',)]

Haqiqiy faylga yoziladigan dasturda odatda şunday yoziladi (bu misol kompyuteringizda işga tuşiriş uçun):

Python
import sqlite3
from contextlib import closing
from pathlib import Path

baza_fayli = Path("kutubxona.db")

with closing(sqlite3.connect(baza_fayli)) as ulanish:
    with ulanish:  # tranzaksiya
        ulanish.execute(
            "CREATE TABLE IF NOT EXISTS kitoblar "
            "(id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)"
        )
        ulanish.execute(
            "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
            ("Ikki eşik orasi", "Ötkir Hoşimov", 1986, 500),
        )
    soni = ulanish.execute("SELECT COUNT(*) FROM kitoblar").fetchone()[0]
    print(f"{baza_fayli} faylida {soni} ta kitob bor")

Dasturni har işga tuşirganingizda kitoblar soni bittaga oşib boradi — maʼlumotlar fayl içida saqlanib qoladi.

Hisob-kitob sörovlari

SQL faqat maʼlumot olib bermaydi, balki hisoblaydi ham: COUNT, SUM, AVG, MIN, MAX. GROUP BY esa qatorlarni guruhlab, har bir guruh uçun alohida hisoblaydi:

Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("""CREATE TABLE kitoblar (
    id INTEGER PRIMARY KEY, nomi TEXT, muallif TEXT, yil INTEGER, sahifa INTEGER)""")
ulanish.executemany(
    "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
    [
        ("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400),
        ("Mehrobdan çayon", "Abdulla Qodiriy", 1929, 320),
        ("Keça va kunduz", "Çölpon", 1936, 280),
        ("Sarob", "Abdulla Qahhor", 1937, 350),
    ],
)

sorov = """
    SELECT muallif, COUNT(*) AS soni, SUM(sahifa) AS jami
    FROM kitoblar
    GROUP BY muallif
    ORDER BY jami DESC
"""
for muallif, soni, jami in ulanish.execute(sorov):
    print(f"{muallif:<16} {soni} ta kitob, {jami} bet")

ortacha = ulanish.execute("SELECT AVG(sahifa) FROM kitoblar").fetchone()[0]
print(f"Örtaça qalinlik: {ortacha:.1f} bet")

Natija:

Natija
Abdulla Qodiriy  2 ta kitob, 720 bet
Abdulla Qahhor   1 ta kitob, 350 bet
Çölpon           1 ta kitob, 280 bet
Örtaça qalinlik: 337.5 bet

Amaliy misol: Kutubxona klassi

Katta dasturda SQL buyruqlarini hamma joyga soçib taşlaş yaxşi emas. Odatda baza bilan işlaş bitta klass içiga yiğiladi, dasturning qolgan qismi esa faqat uning metodlarini çaqiradi. Bu yondaşuv kodni öqişni, sinaşni va keyinçalik boşqa bazaga ötişni osonlaştiradi.

Python
import sqlite3
from dataclasses import dataclass

@dataclass
class Kitob:
    nomi: str
    muallif: str
    yil: int
    sahifa: int
    id: int | None = None


class Kutubxona:
    def __init__(self, fayl: str = ":memory:") -> None:
        self.ulanish = sqlite3.connect(fayl)
        self.ulanish.row_factory = sqlite3.Row
        with self.ulanish:
            self.ulanish.execute("""
                CREATE TABLE IF NOT EXISTS kitoblar (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    nomi TEXT NOT NULL, muallif TEXT NOT NULL,
                    yil INTEGER, sahifa INTEGER)""")

    def qosh(self, kitob: Kitob) -> int:
        with self.ulanish:
            kursor = self.ulanish.execute(
                "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
                (kitob.nomi, kitob.muallif, kitob.yil, kitob.sahifa),
            )
        kitob.id = kursor.lastrowid
        return kitob.id

    def _kitobga(self, qator: sqlite3.Row) -> Kitob:
        return Kitob(**dict(qator))

    def hammasi(self) -> list[Kitob]:
        qatorlar = self.ulanish.execute("SELECT * FROM kitoblar ORDER BY yil")
        return [self._kitobga(q) for q in qatorlar]

    def qidir(self, soz: str) -> list[Kitob]:
        naqsh = f"%{soz}%"
        qatorlar = self.ulanish.execute(
            "SELECT * FROM kitoblar WHERE nomi LIKE ? OR muallif LIKE ?", (naqsh, naqsh)
        )
        return [self._kitobga(q) for q in qatorlar]

    def sahifani_yangila(self, kitob_id: int, sahifa: int) -> bool:
        with self.ulanish:
            k = self.ulanish.execute(
                "UPDATE kitoblar SET sahifa = ? WHERE id = ?", (sahifa, kitob_id)
            )
        return k.rowcount == 1

    def ochir(self, kitob_id: int) -> bool:
        with self.ulanish:
            k = self.ulanish.execute("DELETE FROM kitoblar WHERE id = ?", (kitob_id,))
        return k.rowcount == 1

    def yop(self) -> None:
        self.ulanish.close()


kutubxona = Kutubxona()
kutubxona.qosh(Kitob("Sarob", "Abdulla Qahhor", 1937, 350))
kutubxona.qosh(Kitob("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400))
yangi_id = kutubxona.qosh(Kitob("Şum bola", "Ğafur Ğulom", 1936, 150))

for k in kutubxona.hammasi():
    print(f"[{k.id}] {k.nomi} — {k.muallif}, {k.yil}-yil, {k.sahifa} bet")

print("\nQidiruv 'abdulla':", [k.nomi for k in kutubxona.qidir("abdulla")])
print("Yangilandimi?", kutubxona.sahifani_yangila(yangi_id, 160))
print("Öçirildimi?", kutubxona.ochir(99))
kutubxona.yop()

Natija:

Natija
[2] Ötkan kunlar — Abdulla Qodiriy, 1926-yil, 400 bet
[3] Şum bola — Ğafur Ğulom, 1936-yil, 150 bet
[1] Sarob — Abdulla Qahhor, 1937-yil, 350 bet

Qidiruv 'abdulla': ['Sarob', 'Ötkan kunlar']
Yangilandimi? True
Öçirildimi? False

Kitob(**dict(qator)) — sqlite3.Row ni luğatga, söng luğatni dataclass obyektiga aylantiradi. Bu işlaşi uçun ustun nomlari maydon nomlari bilan bir xil bölişi kerak. Kutubxona("kutubxona.db") deb çaqirsangiz, xuddi şu klass maʼlumotlarni faylga saqlaydi.

Xulosa

  • sqlite3 Python bilan birga keladi; baza bitta faylda yoki ":memory:" da yaşaydi.
  • connect() → ulaniş, execute() → SQL buyruği, fetchone() / fetchall() → natija.
  • Qiymatlarni doim ? yoki :nom belgilari orqali uzating — f-string SQL injection ga yöl oçadi.
  • executemany() köp qatorni bir yöla qöşadi; rowcount va lastrowid natija haqida maʼlumot beradi.
  • row_factory = sqlite3.Row ustunlarga nom bilan murojaat qiliş imkonini beradi.
  • UPDATE va DELETE da WHERE ni unutmang.
  • commit() tasdiqlaydi, rollback() bekor qiladi; with ulanish: buni avtomatik bajaradi, lekin ulanişni yopmaydi.
  • Baza bilan işlaşni bitta klassga yiğiş kodni tartibli qiladi.

Maşqlar

  1. talabalar jadvalini yarating (id, ism, guruh, baho). 5 ta talabani executemany() bilan qöşing va bahosi 80 dan yuqori bölganlarni bahosi böyiça kamayiş tartibida çiqaring.
Yeçim
Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("CREATE TABLE talabalar (id INTEGER PRIMARY KEY, ism TEXT, guruh TEXT, baho INTEGER)")
ulanish.executemany(
    "INSERT INTO talabalar (ism, guruh, baho) VALUES (?, ?, ?)",
    [("Dilnoza", "IT-21", 92), ("Jasur", "IT-21", 75), ("Kamola", "IT-22", 88),
     ("Otabek", "IT-22", 64), ("Sevara", "IT-21", 81)],
)
for ism, baho in ulanish.execute(
    "SELECT ism, baho FROM talabalar WHERE baho > ? ORDER BY baho DESC", (80,)
):
    print(ism, baho)
  1. Oldingi jadval uçun har bir guruhning örtaça bahosini GROUP BY yordamida hisoblang.
Yeçim
Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("CREATE TABLE talabalar (id INTEGER PRIMARY KEY, ism TEXT, guruh TEXT, baho INTEGER)")
ulanish.executemany(
    "INSERT INTO talabalar (ism, guruh, baho) VALUES (?, ?, ?)",
    [("Dilnoza", "IT-21", 92), ("Jasur", "IT-21", 75), ("Kamola", "IT-22", 88),
     ("Otabek", "IT-22", 64), ("Sevara", "IT-21", 81)],
)
for guruh, ortacha in ulanish.execute(
    "SELECT guruh, AVG(baho) FROM talabalar GROUP BY guruh ORDER BY guruh"
):
    print(f"{guruh}: {ortacha:.1f}")
  1. mahsulotlar jadvali (nomi, narx) uçun narx_oshir(foiz) funksiyasini yozing: u barça narxlarni berilgan foizga oşirsin va neçta qator özgarganini qaytarsin. with bilan tranzaksiyadan foydalaning.
Yeçim
Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("CREATE TABLE mahsulotlar (nomi TEXT, narx INTEGER)")
ulanish.executemany(
    "INSERT INTO mahsulotlar VALUES (?, ?)",
    [("non", 4000), ("sut", 12000), ("guruç", 18000)],
)

def narx_oshir(foiz: int) -> int:
    with ulanish:
        k = ulanish.execute(
            "UPDATE mahsulotlar SET narx = narx * (100 + ?) / 100", (foiz,)
        )
    return k.rowcount

print("Özgardi:", narx_oshir(10))
print(ulanish.execute("SELECT * FROM mahsulotlar").fetchall())
  1. Quyidagi funksiya nima uçun xavfli? Uni xavfsiz qilib qayta yozing: def topish(ism): return ulanish.execute(f"SELECT * FROM talabalar WHERE ism = '{ism}'").fetchall()
Yeçim
Python
import sqlite3

ulanish = sqlite3.connect(":memory:")
ulanish.execute("CREATE TABLE talabalar (ism TEXT, baho INTEGER)")
ulanish.executemany("INSERT INTO talabalar VALUES (?, ?)", [("Jasur", 75), ("Kamola", 88)])

# f-string içidagi qiymat SQL kodining bir qismiga aylanadi.
# Xavfsiz variant — qiymatni ? orqali uzatiş:
def topish(ism: str) -> list[tuple]:
    return ulanish.execute("SELECT * FROM talabalar WHERE ism = ?", (ism,)).fetchall()

print(topish("Kamola"))
print(topish("x' OR '1'='1"))
  1. Kutubxona klassiga muallif_boyicha(muallif) metodini qöşing: u berilgan muallifning kitoblarini yil böyiça tartiblab, faqat nomlarini röyxat sifatida qaytarsin.
Yeçim
Python
import sqlite3

class Kutubxona:
    def __init__(self) -> None:
        self.ulanish = sqlite3.connect(":memory:")
        self.ulanish.row_factory = sqlite3.Row
        self.ulanish.execute(
            "CREATE TABLE kitoblar (id INTEGER PRIMARY KEY, nomi TEXT, "
            "muallif TEXT, yil INTEGER, sahifa INTEGER)"
        )

    def qosh(self, nomi: str, muallif: str, yil: int, sahifa: int) -> None:
        with self.ulanish:
            self.ulanish.execute(
                "INSERT INTO kitoblar (nomi, muallif, yil, sahifa) VALUES (?, ?, ?, ?)",
                (nomi, muallif, yil, sahifa),
            )

    def muallif_boyicha(self, muallif: str) -> list[str]:
        qatorlar = self.ulanish.execute(
            "SELECT nomi FROM kitoblar WHERE muallif = ? ORDER BY yil", (muallif,)
        )
        return [q["nomi"] for q in qatorlar]

k = Kutubxona()
k.qosh("Mehrobdan çayon", "Abdulla Qodiriy", 1929, 320)
k.qosh("Ötkan kunlar", "Abdulla Qodiriy", 1926, 400)
k.qosh("Sarob", "Abdulla Qahhor", 1937, 350)
print(k.muallif_boyicha("Abdulla Qodiriy"))