Python ile Veritabanı Temelleri: sqlite3 Modülü
Programlarımız çalışırken veriler RAM'de yaşar. Program kapandığı anda her şey uçar — tıpkı kaydetmeden kapattığın bir Word belgesi gibi. Peki ya kullanıcı bilgilerini, notları, sipariş geçmişini kalıcı olarak saklamak istersen? İşte tam burada veritabanları devreye girer.
Bu derste Python'un yerleşik (built-in) veritabanı modülü olan sqlite3'ü sıfırdan öğreneceğiz. Hiçbir kurulum yapmadan, hiçbir sunucu ayarlamadan, doğrudan Python ile veritabanı işlemlerini keşfedeceğiz.
1. Veritabanı Nedir?
Veritabanı (database), verilerin yapılandırılmış ve kalıcı olarak saklandığı bir sistemdir. Dosyaya yazmaktan farkı şu: veritabanları verileri düzenli tablolarda tutar, hızlıca arayabilir, filtreleyebilir ve güncelleyebilirsin.
📂 Analoji: Excel Tablosu → Veritabanı Tablosu
Bir Excel dosyası düşün. Her sayfa (sheet) bir tablo (table), her sütun bir alan (column/field), her satır ise bir kayıt (row/record). Veritabanı da tam olarak böyle çalışır — ama çok daha hızlı, çok daha güvenli ve milyonlarca satırı bile rahatça yönetebilir.
| Excel Kavramı | Veritabanı Karşılığı |
|---|---|
| Dosya (.xlsx) | Veritabanı (database) |
| Sayfa (sheet) | Tablo (table) |
| Sütun başlığı | Alan adı (column name) |
| Satır | Kayıt (row/record) |
| Hücre | Değer (value) |
SQLite Neden Özel?
SQLite, dünyanın en yaygın kullanılan veritabanı motorudur. Telefonunda, tarayıcında, hatta uçaktaki eğlence sisteminde bile SQLite çalışıyor olabilir. Avantajları:
Dosya tabanlı: Tüm veritabanı tek bir
.dbdosyası. Taşıması, yedeklemesi kolay.Kurulum gerektirmez: PostgreSQL veya MySQL gibi sunucu kurmana gerek yok.
Python ile yerleşik gelir:
import sqlite3— hepsi bu. Ekstrapip installyok.Sıfır yapılandırma: Bağlan ve kullanmaya başla.
Küçük-orta ölçekli projeler, prototipler ve masaüstü programlar için mükemmel bir seçim.
2. sqlite3 Modülü ile Bağlantı Kurma
Veritabanıyla çalışmanın ilk adımı bağlantı (connection) kurmak. Ardından bir cursor (imleç) oluşturuyoruz — SQL komutlarını bu cursor üzerinden çalıştırıyoruz. İşimiz bitince bağlantıyı kapatıyoruz.
import sqlite3
# Veritabanına bağlan (dosya yoksa otomatik oluşturulur)
conn = sqlite3.connect("notlar.db")
# Cursor oluştur — SQL komutlarını bu nesne çalıştırır
cursor = conn.cursor()
# Basit bir SQL komutu çalıştır
cursor.execute("SELECT sqlite_version()")
version = cursor.fetchone()
print(f"SQLite sürümü: {version[0]}")
# Bağlantıyı kapat
conn.close()Burada üç temel adım var: connect → cursor → close. connect() fonksiyonuna bir dosya adı veriyoruz. O dosya yoksa SQLite otomatik olarak oluşturur. cursor nesnesi ise SQL komutlarını veritabanına gönderen bir "aracı" gibi düşünülebilir.
Eğer veritabanını dosyaya kaydetmek istemiyorsan, bellekte (in-memory) de çalışabilirsin:
import sqlite3
# Bellekte geçici veritabanı — program kapanınca kaybolur
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("SELECT 1 + 1")
result = cursor.fetchone()
print(f"Sonuç: {result[0]}") # Sonuç: 2
conn.close():memory: özellikle testlerde çok işe yarar. Her test için temiz bir veritabanı oluşturabilirsin.
💡 İpucu:
conn.close()çağrısını unutmak kaynak sızıntısına (resource leak) yol açabilir. Bir sonraki bölümde öğreneceğimiz context manager bu sorunu otomatik çözer.
3. Context Manager ile Bağlantı
try/finally ile close() çağırmak yerine, Python'un with ifadesini kullanmak çok daha güvenli ve temiz. Context manager bağlantıyı otomatik olarak yönetir.
import sqlite3
with sqlite3.connect("notlar.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT sqlite_version()")
version = cursor.fetchone()
print(f"SQLite sürümü: {version[0]}")
# with bloğu bitince:
# - Hata yoksa conn.commit() otomatik çağrılır
# - Hata varsa conn.rollback() otomatik çağrılır
# NOT: Bağlantı kapatılmaz, sadece transaction yönetilirÖnemli bir detay: with sqlite3.connect(...) bağlantıyı kapatmaz, sadece transaction'ı (işlemi) yönetir. Hata olmazsa commit(), hata olursa rollback() yapılır. Bu, veri bütünlüğü için harika bir güvence.
Gerçekten tam kapatma istersen contextlib.closing ile sarabilirsin, ama çoğu durumda with sqlite3.connect(...) as conn: yeterli ve temiz.
4. Tablo Oluşturma — CREATE TABLE
Verilerimizi saklamak için önce bir tablo (table) tanımlamamız gerekiyor. Her tablonun sütunları ve her sütunun bir veri tipi vardır.
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS ogrenciler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
isim TEXT NOT NULL,
yas INTEGER,
ortalama REAL,
kayit_tarihi TEXT DEFAULT CURRENT_TIMESTAMP
)
""")
print("Tablo oluşturuldu!")IF NOT EXISTS ifadesi çok kritik. Bu olmadan, tabloyu ikinci kez oluşturmaya çalışırsan hata alırsın. Bu ifade "tablo zaten varsa dokunma" der.
SQLite'ın temel veri tipleri şunlar:
| SQLite Tipi | Python Karşılığı | Açıklama |
|---|---|---|
INTEGER | int | Tam sayı |
REAL | float | Ondalıklı sayı |
TEXT | str | Metin |
BLOB | bytes | İkili veri (binary) |
NULL | None | Boş değer |
Birden fazla tablo oluşturma örneği:
import sqlite3
with sqlite3.connect("magazin.db") as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS kategoriler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ad TEXT NOT NULL UNIQUE
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS urunler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ad TEXT NOT NULL,
fiyat REAL NOT NULL,
stok INTEGER DEFAULT 0,
kategori_id INTEGER,
FOREIGN KEY (kategori_id) REFERENCES kategoriler(id)
)
""")
print("Tablolar oluşturuldu!")PRIMARY KEY AUTOINCREMENT her yeni kayda otomatik artan benzersiz bir ID verir. NOT NULL o alanın boş bırakılamayacağını, UNIQUE ise değerin tekrar edemeyeceğini belirtir. FOREIGN KEY ise iki tablo arasında ilişki kurar.
5. Veri Ekleme — INSERT INTO
Tablomuza veri eklemek için INSERT INTO SQL komutunu kullanıyoruz.
Tek Kayıt Ekleme
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO ogrenciler (isim, yas, ortalama)
VALUES (?, ?, ?)
""", ("Elif Yılmaz", 20, 3.75))
print(f"Eklenen kayıt ID: {cursor.lastrowid}")Burada ? işaretlerine dikkat et — bunlar parametre yer tutucuları (parameter placeholders). Değerleri doğrudan SQL string'ine yazmak yerine tuple olarak ayrı geçiyoruz. Nedenini birazdan "SQL Injection" bölümünde göreceksin.
Çoklu Kayıt Ekleme — executemany
Birden fazla kaydı tek seferde eklemek için executemany() kullanıyoruz:
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
ogrenciler = [
("Ahmet Kaya", 21, 3.20),
("Zeynep Demir", 19, 3.90),
("Can Özkan", 22, 2.85),
("Selin Aksoy", 20, 3.55),
("Mert Çelik", 23, 3.10),
]
cursor.executemany("""
INSERT INTO ogrenciler (isim, yas, ortalama)
VALUES (?, ?, ?)
""", ogrenciler)
print(f"{cursor.rowcount} öğrenci eklendi.")executemany() her tuple için aynı SQL komutunu tekrar tekrar çalıştırır ama bunu optimize edilmiş bir şekilde yapar. 1000 kayıt eklemen gerekiyorsa, döngü yerine kesinlikle executemany() tercih et.
cursor.lastrowid son eklenen kaydın ID'sini, cursor.rowcount ise etkilenen satır sayısını verir.
6. Parameterized Queries — SQL Injection Koruması
Bu bölüm en önemli bölümlerden biri. Kullanıcıdan gelen verileri doğrudan SQL string'ine gömmek, uygulamanı ciddi güvenlik açıklarına maruz bırakır.
⚠️ Dikkat: SQL Injection Tehlikesi!
>
Kullanıcı girdisini asla doğrudan SQL string'ine ekleme. Bu, veritabanının tamamının silinmesine veya hassas verilerin çalınmasına yol açabilir. Her zaman parameterized query kullan.
❌ YANLIŞ — Asla Böyle Yapma!
import sqlite3
# TEHLİKELİ! Kullanıcı girdisi doğrudan SQL'de
kullanici_adi = input("İsim girin: ")
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# BU SATIR GÜVENLİK AÇIĞI!
cursor.execute(f"SELECT * FROM ogrenciler WHERE isim = '{kullanici_adi}'")Kullanıcı isim yerine '; DROP TABLE ogrenciler; -- yazarsa ne olur? SQL komutu şöyle olur:
SELECT * FROM ogrenciler WHERE isim = ''; DROP TABLE ogrenciler; --'Tüm tablo silinir! Buna SQL Injection saldırısı denir.
✅ DOĞRU — Positional Parameters (?)
import sqlite3
kullanici_adi = input("İsim girin: ")
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# Güvenli: ? yer tutucusu kullan
cursor.execute(
"SELECT * FROM ogrenciler WHERE isim = ?",
(kullanici_adi,)
)
sonuclar = cursor.fetchall()
for row in sonuclar:
print(row)? yer tutucusunu kullandığında, SQLite değerleri otomatik olarak kaçışlar (escape). Kullanıcı ne yazarsa yazsın, o veri SQL komutu olarak yorumlanmaz.
✅ DOĞRU — Named Parameters (:name)
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
filtre = {
"min_yas": 19,
"min_ort": 3.0
}
cursor.execute("""
SELECT isim, yas, ortalama FROM ogrenciler
WHERE yas >= :min_yas AND ortalama >= :min_ort
ORDER BY ortalama DESC
""", filtre)
for row in cursor.fetchall():
print(f"{row[0]} - Yaş: {row[1]}, Ort: {row[2]}")İsimli parametreler (named parameters) özellikle çok parametreli sorgularda kodun okunabilirliğini artırır. Sözlük (dict) olarak geçiriyorsun, anahtar adları SQL'deki :name ile eşleşiyor.
💡 İpucu: Hangisini kullanmalısın? Az parametreli basit sorgularda
?, çok parametreli veya karmaşık sorgularda:nametercih et. Hangisini seçersen seç, asla f-string veyaformat()ile SQL oluşturma.
7. Veri Sorgulama — SELECT
Veritabanından veri çekmek en sık yapacağın işlem. SELECT komutu ile verileri sorgulayabilir, filtreleyebilir ve sıralayabilirsin.
Temel SELECT
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# Tüm kayıtları getir
cursor.execute("SELECT * FROM ogrenciler")
tum_ogrenciler = cursor.fetchall()
for ogrenci in tum_ogrenciler:
print(ogrenci)SELECT * tüm sütunları getirir. Performans için sadece ihtiyacın olan sütunları belirtmek daha iyi:
cursor.execute("SELECT isim, ortalama FROM ogrenciler")WHERE ile Filtreleme
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# Ortalaması 3.5'ten yüksek öğrenciler
cursor.execute("""
SELECT isim, ortalama FROM ogrenciler
WHERE ortalama > ?
""", (3.5,))
for row in cursor.fetchall():
print(f"{row[0]}: {row[1]}")ORDER BY ile Sıralama ve LIMIT
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# En yüksek ortalamalı 3 öğrenci
cursor.execute("""
SELECT isim, ortalama FROM ogrenciler
ORDER BY ortalama DESC
LIMIT ?
""", (3,))
print("--- En Başarılı 3 Öğrenci ---")
for i, row in enumerate(cursor.fetchall(), 1):
print(f"{i}. {row[0]} — Ortalama: {row[1]}")ORDER BY ... DESC büyükten küçüğe sıralar (ASC ise küçükten büyüğe, varsayılan). LIMIT dönen kayıt sayısını sınırlar. Büyük tablolarda LIMIT kullanmak performans için çok önemli.
BETWEEN, AND, OR, LIKE, IN gibi SQL operatörlerini WHERE ile kullanabilirsin. LIKE özellikle metin aramalarında işe yarar: WHERE isim LIKE '%Kaya%' isim içinde "Kaya" geçen tüm kayıtları bulur.
8. fetchone(), fetchall(), fetchmany() Farkları
Sorgu sonuçlarını farklı şekillerde alabilirsin. Her birinin kullanım alanı farklı.
fetchone() — Tek Kayıt
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM ogrenciler WHERE id = ?", (1,))
ogrenci = cursor.fetchone()
if ogrenci:
print(f"İsim: {ogrenci[1]}, Yaş: {ogrenci[2]}")
else:
print("Öğrenci bulunamadı.")fetchone() bir kayıt döner veya sonuç yoksa None döner. Tek bir kayıt beklediğin durumlarda (ID ile arama gibi) ideal.
fetchall() — Tüm Kayıtlar
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT isim FROM ogrenciler")
tumu = cursor.fetchall() # Liste döner: [(isim1,), (isim2,), ...]
print(f"Toplam {len(tumu)} öğrenci var.")fetchall() tüm sonuçları bir listeye yükler. Küçük veri setleri için sorun yok ama milyonlarca kayıt dönüyorsa belleği tüketebilir.
fetchmany(size) — Belirli Sayıda Kayıt
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM ogrenciler ORDER BY id")
while True:
batch = cursor.fetchmany(2) # 2'şer 2'şer al
if not batch:
break
for row in batch:
print(row)
print("--- sonraki grup ---")fetchmany(size) belirtilen sayıda kayıt döner. Büyük veri setlerini parça parça işlemek istediğinde kullan. Veri bittiğinde boş liste döner.
Cursor'ı Doğrudan Iterate Etme
Aslında en verimli yol cursor'ı direkt döngüde kullanmak:
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT isim, ortalama FROM ogrenciler")
for isim, ortalama in cursor: # Her seferinde 1 satır belleğe alır
print(f"{isim}: {ortalama}")Bu yöntem bellek dostu (memory-efficient) çünkü tüm sonuçları bir kerede belleğe yüklemez. Büyük veri setleri için en iyi seçenek.
9. Veri Güncelleme — UPDATE
Mevcut kayıtları değiştirmek için UPDATE komutunu kullanıyoruz.
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# Belirli bir öğrencinin ortalamasını güncelle
cursor.execute("""
UPDATE ogrenciler
SET ortalama = ?
WHERE id = ?
""", (3.95, 1))
print(f"{cursor.rowcount} kayıt güncellendi.")cursor.rowcount kaç kaydın etkilendiğini söyler. WHERE koşulu olmadan UPDATE çalıştırırsan tüm kayıtlar güncellenir — çok dikkatli ol!
Birden fazla alanı aynı anda güncelleyebilirsin:
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
cursor.execute("""
UPDATE ogrenciler
SET yas = ?, ortalama = ?
WHERE isim = ?
""", (21, 3.80, "Elif Yılmaz"))
if cursor.rowcount == 0:
print("Eşleşen kayıt bulunamadı!")
else:
print(f"{cursor.rowcount} kayıt güncellendi.")⚠️ Dikkat:
UPDATEveyaDELETEkomutlarındaWHEREkoşulunu unutma!WHEREolmadan tüm tablodaki kayıtlar etkilenir. Bu çok yaygın ve tehlikeli bir hatadır.
10. Veri Silme — DELETE
Kayıtları silmek için DELETE FROM komutunu kullanıyoruz.
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# Belirli bir kaydı sil
cursor.execute("DELETE FROM ogrenciler WHERE id = ?", (3,))
print(f"{cursor.rowcount} kayıt silindi.")Koşullu silme örnekleri:
import sqlite3
with sqlite3.connect("okul.db") as conn:
cursor = conn.cursor()
# Ortalaması 2.0'ın altında olanları sil
cursor.execute(
"DELETE FROM ogrenciler WHERE ortalama < ?",
(2.0,)
)
print(f"{cursor.rowcount} kayıt silindi.")
# Tüm tabloyu temizlemek istersen (DİKKAT!)
# cursor.execute("DELETE FROM ogrenciler")Gerçek projelerde silmeden önce kaydın var olup olmadığını kontrol etmek iyi bir pratik.
11. Row Factory — sqlite3.Row Kullanımı
Şimdiye kadar sorgu sonuçları hep tuple olarak geldi: (1, "Elif", 20, 3.75). Hangi indeksin hangi sütun olduğunu hatırlamak zor. sqlite3.Row bunu çözer ve sonuçlara sözlük (dict) gibi erişmeni sağlar.
import sqlite3
with sqlite3.connect("okul.db") as conn:
conn.row_factory = sqlite3.Row # Bu satır farkı yaratır
cursor = conn.cursor()
cursor.execute("SELECT * FROM ogrenciler WHERE id = ?", (1,))
ogrenci = cursor.fetchone()
# Artık sütun adıyla erişebilirsin
print(f"İsim: {ogrenci['isim']}")
print(f"Yaş: {ogrenci['yas']}")
print(f"Ortalama: {ogrenci['ortalama']}")
print(f"Sütunlar: {ogrenci.keys()}")sqlite3.Row hem indeks (ogrenci[1]) hem de anahtar (ogrenci['isim']) erişimini destekler. Kodu çok daha okunabilir hale getirir.
Bir Row nesnesini gerçek sözlüğe çevirmek de kolay:
import sqlite3
with sqlite3.connect("okul.db") as conn:
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("SELECT * FROM ogrenciler")
ogrenciler = [dict(row) for row in cursor.fetchall()]
for ogr in ogrenciler:
print(ogr)
# {'id': 1, 'isim': 'Elif Yılmaz', 'yas': 20, 'ortalama': 3.75, ...}Bu teknik özellikle JSON API'ler yaparken çok işe yarar çünkü sözlükleri doğrudan JSON'a dönüştürebilirsin.
💡 İpucu:
conn.row_factory = sqlite3.Rowsatırını bağlantıyı açtıktan hemen sonra ayarla. Bundan sonra o bağlantıdan oluşturulan tüm cursor'larRownesneleri döner. Projelerinde varsayılan olarak kullanmanı tavsiye ederim.
12. Transaction Yönetimi — commit ve rollback
Bir transaction (işlem), birbirine bağlı veritabanı operasyonlarının "ya hep ya hiç" mantığıyla çalışmasını sağlar. Ya tümü başarılı olur, ya hiçbiri uygulanmaz.
Banka havalesi düşün: Bir hesaptan para çıkacak, diğerine girecek. Para çıktıktan sonra bir hata olursa, para havada kalmamalı — ilk işlem de geri alınmalı. İşte commit() ve rollback() bunu sağlar.
import sqlite3
conn = sqlite3.connect("banka.db")
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS hesaplar (
id INTEGER PRIMARY KEY,
isim TEXT,
bakiye REAL
)
""")
conn.commit()
try:
# Ali'den Veli'ye 500 TL transfer
cursor.execute("UPDATE hesaplar SET bakiye = bakiye - 500 WHERE isim = ?", ("Ali",))
cursor.execute("UPDATE hesaplar SET bakiye = bakiye + 500 WHERE isim = ?", ("Veli",))
# Her şey başarılıysa onayla
conn.commit()
print("Transfer başarılı!")
except Exception as e:
# Hata olursa tüm değişiklikleri geri al
conn.rollback()
print(f"Hata! Transfer iptal edildi: {e}")
finally:
conn.close()SQLite'ın varsayılan davranışı: conn.commit() çağrılana kadar değişiklikler veritabanına yazılmaz. with bloğu kullandığında bu otomatik yönetilir.
Genel tavsiye: with bloğunu kullan, transaction yönetimi otomatik olsun. Manuel commit/rollback sadece karmaşık, çok adımlı senaryolarda gerekir.
13. Gerçek Dünya Projesi: Basit Not Defteri (CRUD)
Şimdi öğrendiklerimizi birleştirip CRUD (Create, Read, Update, Delete) operasyonlarını içeren gerçek bir uygulama yapalım: basit bir not defteri.
import sqlite3
from datetime import datetime
def veritabani_baglantisi(db_path="notlar.db"):
"""Veritabanı bağlantısı kur ve tabloyu oluştur."""
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
conn.execute("""
CREATE TABLE IF NOT EXISTS notlar (
id INTEGER PRIMARY KEY AUTOINCREMENT,
baslik TEXT NOT NULL,
icerik TEXT NOT NULL,
olusturma_tarihi TEXT DEFAULT CURRENT_TIMESTAMP,
guncelleme_tarihi TEXT
)
""")
conn.commit()
return conn
def not_ekle(conn, baslik, icerik):
"""Yeni bir not ekle. (CREATE)"""
cursor = conn.execute(
"INSERT INTO notlar (baslik, icerik) VALUES (?, ?)",
(baslik, icerik)
)
conn.commit()
print(f"Not eklendi (ID: {cursor.lastrowid})")
return cursor.lastrowid
def notlari_listele(conn):
"""Tüm notları listele. (READ)"""
cursor = conn.execute(
"SELECT id, baslik, olusturma_tarihi FROM notlar ORDER BY id DESC"
)
notlar = cursor.fetchall()
if not notlar:
print("Henüz not yok.")
return
print(f"\n{'ID':<5} {'Başlık':<30} {'Tarih':<20}")
print("-" * 55)
for n in notlar:
print(f"{n['id']:<5} {n['baslik']:<30} {n['olusturma_tarihi']:<20}")
def not_detay(conn, not_id):
"""Belirli bir notu göster. (READ)"""
cursor = conn.execute(
"SELECT * FROM notlar WHERE id = ?", (not_id,)
)
n = cursor.fetchone()
if n is None:
print(f"ID {not_id} ile not bulunamadı.")
return None
print(f"\n📝 {n['baslik']}")
print(f" Tarih: {n['olusturma_tarihi']}")
if n['guncelleme_tarihi']:
print(f" Güncelleme: {n['guncelleme_tarihi']}")
print(f"\n{n['icerik']}\n")
return dict(n)
def not_guncelle(conn, not_id, baslik=None, icerik=None):
"""Bir notu güncelle. (UPDATE)"""
mevcut = conn.execute(
"SELECT * FROM notlar WHERE id = ?", (not_id,)
).fetchone()
if mevcut is None:
print(f"ID {not_id} ile not bulunamadı.")
return False
yeni_baslik = baslik if baslik else mevcut['baslik']
yeni_icerik = icerik if icerik else mevcut['icerik']
simdi = datetime.now().isoformat()
conn.execute("""
UPDATE notlar
SET baslik = ?, icerik = ?, guncelleme_tarihi = ?
WHERE id = ?
""", (yeni_baslik, yeni_icerik, simdi, not_id))
conn.commit()
print(f"Not güncellendi (ID: {not_id})")
return True
def not_sil(conn, not_id):
"""Bir notu sil. (DELETE)"""
mevcut = conn.execute(
"SELECT baslik FROM notlar WHERE id = ?", (not_id,)
).fetchone()
if mevcut is None:
print(f"ID {not_id} ile not bulunamadı.")
return False
conn.execute("DELETE FROM notlar WHERE id = ?", (not_id,))
conn.commit()
print(f"'{mevcut['baslik']}' silindi.")
return True
def not_ara(conn, anahtar_kelime):
"""Notlarda arama yap."""
cursor = conn.execute("""
SELECT id, baslik, olusturma_tarihi FROM notlar
WHERE baslik LIKE ? OR icerik LIKE ?
ORDER BY olusturma_tarihi DESC
""", (f"%{anahtar_kelime}%", f"%{anahtar_kelime}%"))
sonuclar = cursor.fetchall()
print(f"\n'{anahtar_kelime}' için {len(sonuclar)} sonuç bulundu:")
for n in sonuclar:
print(f" [{n['id']}] {n['baslik']} — {n['olusturma_tarihi']}")
return sonuclarKullanım örneği:
# Veritabanını hazırla
conn = veritabani_baglantisi()
# Not ekle
not_ekle(conn, "Python Dersi", "sqlite3 modülünü öğrendim, harika!")
not_ekle(conn, "Alışveriş Listesi", "Süt, ekmek, yumurta, peynir")
not_ekle(conn, "Python Projesi", "Not defteri uygulaması tamamlandı")
# Listele
notlari_listele(conn)
# Detay gör
not_detay(conn, 1)
# Güncelle
not_guncelle(conn, 2, icerik="Süt, ekmek, yumurta, peynir, zeytin")
# Ara
not_ara(conn, "Python")
# Sil
not_sil(conn, 3)
# Bağlantıyı kapat
conn.close()Bu küçük proje, tüm temel veritabanı operasyonlarını bir arada gösteriyor. Fonksiyonları ayrı tutmak kodun test edilmesini ve bakımını kolaylaştırır. row_factory = sqlite3.Row sayesinde sütunlara isimleriyle erişiyoruz — çok daha okunabilir.
14. sqlite3 vs SQLAlchemy vs Django ORM — Ne Zaman Hangisi?
Python'da veritabanı ile çalışmanın birden fazla yolu var. Her aracın güçlü olduğu senaryolar farklı.
sqlite3 — Ham SQL, Tam Kontrol
# sqlite3: Doğrudan SQL yazıyorsun
cursor.execute("SELECT * FROM users WHERE age > ?", (18,))✅ Yerleşik, ek kurulum yok
✅ SQL öğrenmek için ideal
✅ Küçük projeler, scriptler, prototipler
❌ SQL string'lerini elle yönetmek zahmetli
❌ Farklı veritabanına geçmek zor (SQL dialektleri farklı)
SQLAlchemy — Profesyonel ORM
# SQLAlchemy: Python nesneleriyle çalışıyorsun
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
engine = create_engine("sqlite:///okul.db")
with Session(engine) as session:
ogrenciler = session.query(Ogrenci).filter(
Ogrenci.yas > 18
).all()✅ Hem ORM hem ham SQL desteği
✅ Veritabanı bağımsız — PostgreSQL, MySQL, SQLite arası geçiş kolay
✅ Migration (veritabanı versiyonlama) desteği (Alembic ile)
✅ Büyük ve orta ölçekli projeler için ideal
❌ Öğrenme eğrisi daha yüksek
❌ Küçük scriptler için overkill olabilir
Django ORM — Web Framework'ün Parçası
# Django ORM: Model tanımla, Django gerisini halleder
from myapp.models import Ogrenci
ogrenciler = Ogrenci.objects.filter(yas__gt=18).order_by("-ortalama")✅ Django web projelerinde doğal entegrasyon
✅ Admin paneli, migration, form entegrasyonu
✅ Çok hızlı geliştirme
❌ Django dışında kullanmak pratik değil
❌ Karmaşık sorgularda ham SQL'e düşmek gerekebilir
Karar Tablosu
| Senaryo | Önerilen Araç |
|---|---|
| Python öğreniyorum, SQL pratiği yapacağım | sqlite3 |
| Küçük script, veri analizi, otomasyon | sqlite3 |
| Orta-büyük proje, birden fazla veritabanı desteği | SQLAlchemy |
| REST API (FastAPI, Flask) | SQLAlchemy |
| Django web projesi | Django ORM |
| Mobil uygulama backend'i | SQLAlchemy veya Django ORM |
| Tek seferlik veri işleme scripti | sqlite3 |
Tavsiyem: önce sqlite3 ile SQL'in temellerini öğren. SQL'i anlamadan ORM kullanmak, araba kullanmayı bilmeden otomatik vites sürmek gibi — çalışır ama ne yaptığını bilmezsin.
Özet
📌 SQLite dosya tabanlı, kurulum gerektirmeyen, Python ile yerleşik gelen bir veritabanı motorudur.
import sqlite3ile hemen kullanmaya başlayabilirsin.📌 Temel iş akışı:
connect()→cursor()→execute()→commit()→close(). Context manager (with) kullanarak transaction yönetimini otomatikleştirebilirsin.📌 Parameterized queries (
?veya:name) her zaman kullan. Kullanıcı girdisini asla doğrudan SQL string'ine ekleme — SQL injection saldırılarına karşı bu tek savunma hattın.📌
fetchone(),fetchall(),fetchmany()ve cursor iteration ile sorgu sonuçlarını farklı şekillerde alabilirsin. Büyük veri setlerinde cursor'ı doğrudan iterate etmek en verimli yöntem.📌
conn.row_factory = sqlite3.Rowayarı ile sorgu sonuçlarına sözlük gibi (row['isim']) erişebilirsin. Kodun okunabilirliğini ciddi ölçüde artırır.📌 Proje büyüdükçe
sqlite3'ten SQLAlchemy'ye veya Django ORM'e geçmeyi düşün. Ama önce SQL temellerinisqlite3ile öğrenmek, ilerideki tüm veritabanı çalışmalarını kolaylaştırır.
*Bu ders, Python V2 müfredatının "Veritabanı Temelleri" konusunu destekleyen ek bir kaynaktır. Örnekleri kendi bilgisayarında deneyerek öğrenmeyi pekiştirebilirsin.*
AI Asistan
Sorularını yanıtlamaya hazır