Django ile Adım Adım CRUD İşlemleri ve Veritabanı İlişkileri
Django’da ilişkisel veritabanı yapısını ve CRUD (Create, Read, Update, Delete) işlemlerini anlamak, bir web geliştirici için "olmazsa olmaz" temeldir.
Karmaşık teorilere boğulmadan, gerçek hayattan bir senaryo kuralım: Bir Kütüphane Yönetim Sistemi.
Bire Bir (One-to-One): Yazar ve Biyografisi (Her yazarın bir biyografisi, her biyografinin bir yazarı vardır).
Bire Çok (One-to-Many): Yazar ve Kitapları (Bir yazarın birden çok kitabı olabilir).
Çoka Çok (Many-to-Many): Kitap ve Kategoriler (Bir kitap birden çok kategoride olabilir, bir kategoride birden çok kitap olabilir).
1. Modelleri Tanımlayalım (models.py)
Django modelleri, veritabanı şemanı belirler. İlişkileri burada kuruyoruz.
from django.db import models
class Yazar(models.Model):
isim = models.CharField(max_length=100)
def __str__(self):
return self.isim
class Biyografi(models.Model):
yazar = models.OneToOneField(Yazar, on_delete=models.CASCADE)
detay = models.TextField()
# Kategori modelini Kitap'tan önce tanımlıyoruz ki Kitap onu tanıyabilsin
class Kategori(models.Model):
isim = models.CharField(max_length=50)
def __str__(self):
return self.isim
class Kitap(models.Model):
baslik = models.CharField(max_length=100)
yazar = models.ForeignKey(Yazar, on_delete=models.CASCADE, related_name='kitaplar')
# Çoka Çok ilişkiyi Kitap modeline taşıdık!
kategoriler = models.ManyToManyField(Kategori, related_name='kitaplar')
def __str__(self):
return self.baslik
2. CRUD İşlemleri (views.py)
Django'da veri işlemek için shell kullanabilirsin ama gerçek dünyada bunları view içinde yaparız. İşte temel operasyonlar:
A. Create (Oluşturma)
İlişkili veriyi kaydederken save() metodunu kullanırız.
# Yazar oluşturma
yazar = Yazar.objects.create(isim="Orhan Pamuk")
# Bire Bir (One-to-One) ekleme
Biyografi.objects.create(yazar=yazar, detay="Nobel ödüllü yazar...")
# Bire Çok (One-to-Many) ekleme
yazar.kitaplar.create(baslik="Masumiyet Müzesi")
# Çoka Çok (Many-to-Many) ekleme
kategori = Kategori.objects.create(isim="Roman")
kitap = yazar.kitaplar.first()
kitap.kategoriler.add(kategori) # İlişkiyi ekledik
B. Read (Okuma)
Django'nun ORM yapısı burada parlıyor.
# Tüm kitapları olan yazarları getir
yazarlar = Yazar.objects.all()
# Belirli bir kitabın kategorilerini getir
kitap = Kitap.objects.get(id=1)
kategoriler = kitap.kategoriler.all()
C. Update (Güncelleme)
Nesneyi al, değiştir ve kaydet.
kitap = Kitap.objects.get(id=1)
kitap.baslik = "Yeni Başlık"
kitap.save()
D. Delete (Silme)
on_delete=models.CASCADE sayesinde, bir yazarı silersen onun biyografisi ve kitapları da otomatik silinir (Dikkatli kullan!).
yazar = Yazar.objects.get(id=1)
yazar.delete()
Stratejik İpucu
İlişkileri yönetirken en çok yapılan hata, ForeignKey kullanman gereken yere ManyToManyField kullanmaktır. Basit kural: "Biri diğerine ait mi?" diye sor.
Kitap -> Yazar: Kitap bir yazara aittir. (ForeignKey - Bire Çok)
Yazar -> Biyografi: Biyografi yazara aittir. (OneToOne - Bire Bir)
Kitap -> Kategori: Kitap bir kategoriye ait olmak zorunda değil, birden fazla olabilir. (ManyToMany - Çoka Çok)
Django'nun MVT (Model-View-Template) yapısında form işlemlerinin nasıl aktığını anlamak için aşağıdaki şemayı göz önünde bulundurabilirsin:
En karmaşık ilişkileri barındıran Kitap modelimiz (hem bire çok hem de çoka çok ilişkiye sahip) üzerinden bir kitap ekleme ve güncelleme formu (Create & Update) hazırlayalım.
1. Form Sınıfını Oluşturma (forms.py)
Uygulama klasörünün içine forms.py adında bir dosya oluştur (eğer yoksa) ve ModelForm yapısını tanımla. Bu yapı, modelini okuyup otomatik olarak uygun HTML input'larını (metin kutusu, açılır liste vs.) üretecektir.
from django import forms
from .models import Kitap
class KitapForm(forms.ModelForm):
class Meta:
model = Kitap
fields = ['baslik', 'yazar', 'kategoriler']
# Form elemanlarına CSS sınıfları veya etiketler ekleyebiliriz
widgets = {
'kategoriler': forms.CheckboxSelectMultiple(), # Çoka çok ilişki için çoklu seçim kutuları
}
2. View'ları Yazma (views.py)
Kullanıcının formu göreceği ve gönderdiği verilerin işleneceği fonksiyonları yazalım.
from django.shortcuts import render, redirect, get_object_or_404
from .forms import KitapForm
from .models import Kitap
# CREATE: Yeni Kitap Ekleme
def kitap_ekle(request):
if request.method == 'POST':
form = KitapForm(request.POST)
if form.is_valid(): # Veriler kurallara uygun mu?
form.save() # Hem kitabı hem de çoklu ilişkileri otomatik kaydeder!
return redirect('kitap_listesi') # Başarılıysa yönlendir
else:
# GET isteği ise boş form göster
form = KitapForm()
return render(request, 'kutuphane/kitap_form.html', {'form': form})
# UPDATE: Mevcut Kitabı Güncelleme
def kitap_duzenle(request, kitap_id):
# İlgili kitabı bul, yoksa 404 hatası ver
kitap = get_object_or_404(Kitap, id=kitap_id)
# instance=kitap diyerek formun içinin dolu gelmesini sağlıyoruz
if request.method == 'POST':
form = KitapForm(request.POST, instance=kitap)
if form.is_valid():
form.save()
return redirect('kitap_listesi')
else:
form = KitapForm(instance=kitap)
return render(request, 'kutuphane/kitap_form.html', {'form': form})
3. URL Yönlendirmeleri (urls.py)
Kullanıcıların bu sayfalara erişebilmesi için rotaları belirleyelim.
from django.urls import path
from . import views
urlpatterns = [
path('kitap/ekle/', views.kitap_ekle, name='kitap_ekle'),
path('kitap/duzenle/<int:kitap_id>/', views.kitap_duzenle, name='kitap_duzenle'),
]
4. Şablonu Hazırlama (kitap_form.html)
Son olarak, formu ekranda gösterecek HTML dosyasını oluşturalım. Django'nun {{ form.as_p }} etiketi işleri çok kolaylaştırır. (Bu dosyayı templates/kutuphane/ klasörü altına koymalısın).
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Kitap Formu</title>
</head>
<body>
<h2>Kitap Bilgileri</h2>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Kaydet</button>
</form>
</body>
</html>
Perde Arkasında Neler Oluyor?
ModelFormsayesinde Django, Yazar alanı için veritabanındaki yazarları çekip otomatik bir açılır liste (<select>) oluşturur.Kategoriler alanı için
CheckboxSelectMultiplewidget'ını kullandığımız için, mevcut tüm kategorileri onay kutusu (<input type="checkbox">) olarak listeler.form.save()çağrıldığında,ManyToManyField(Çoka çok ilişki) verileri de arka planda otomatik olarak ilgili ara tabloya kaydedilir. Saf ORM kullanırken yaptığımız.add()işlemine gerek kalmaz.
İşin içine biraz stil katmak, projeyi anında profesyonel bir seviyeye taşır. Hızlı ve şık bir çözüm olduğu için dünyanın en popüler CSS framework'ü olan Bootstrap 5'i kullanacağız.
Bunu yapmak için iki adım atacağız: Önce Django formumuza Bootstrap'in CSS sınıflarını (class) tanıtacağız, ardından HTML şablonumuzu baştan aşağı yenileyeceğiz.
1. Form Sınıfını Bootstrap ile Güncelleme (forms.py)
Django'nun ürettiği HTML etiketlerine CSS sınıfları eklemek için widgets sözlüğünü kullanırız.
from django import forms
from .models import Kitap
class KitapForm(forms.ModelForm):
class Meta:
model = Kitap
fields = ['baslik', 'yazar', 'kategoriler']
# Etiketleri Türkçeleştirelim
labels = {
'baslik': 'Kitap Başlığı',
'yazar': 'Yazar Seçimi',
'kategoriler': 'Kategoriler'
}
# Bootstrap CSS sınıflarını (form-control, form-select) ekleyelim
widgets = {
'baslik': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Kitap adını giriniz...'
}),
'yazar': forms.Select(attrs={
'class': 'form-select'
}),
'kategoriler': forms.CheckboxSelectMultiple(attrs={
'class': 'form-check-input' # Checkbox'lar için Bootstrap sınıfı
}),
}
2. Şık ve Modern Şablon Tasarımı (kitap_form.html)
Şimdi sıkıcı {{ form.as_p }} yapısından kurtulalım ve formu "Card" (kart) tasarımı içinde, çok daha estetik bir şekilde sunalım. HTML dosyanı aşağıdaki kodla değiştirebilirsin:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kitap İşlemleri</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">
{% if form.instance.pk %}Kitabı Güncelle{% else %}Yeni Kitap Ekle{% endif %}
</h4>
</div>
<div class="card-body">
<form method="POST">
{% csrf_token %}
<div class="mb-3">
<label class="form-label fw-bold">{{ form.baslik.label }}</label>
{{ form.baslik }}
</div>
<div class="mb-3">
<label class="form-label fw-bold">{{ form.yazar.label }}</label>
{{ form.yazar }}
</div>
<div class="mb-4">
<label class="form-label fw-bold">{{ form.kategoriler.label }}</label>
<div class="border rounded p-3 bg-white">
{% for checkbox in form.kategoriler %}
<div class="form-check">
{{ checkbox.tag }}
<label class="form-check-label" for="{{ checkbox.id_for_label }}">
{{ checkbox.choice_label }}
</label>
</div>
{% endfor %}
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-success btn-lg">Kaydet</button>
<a href="#" class="btn btn-outline-secondary">İptal</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Ne Değişti?
CDN Entegrasyonu:
<head>etiketleri arasına Bootstrap linkini ekledik, böylece hiçbir şey kurmana gerek kalmadan modern stiller yüklendi.Görsel Hiyerarşi:
cardveshadow-smsınıflarını kullanarak formu ekranın ortasına zarif bir kutu olarak yerleştirdik.Akıllı Başlık:
{% if form.instance.pk %}mantığı sayesinde aynı HTML sayfası hem "Yeni Ekleme" hem de "Güncelleme" sırasında doğru başlığı gösterecek.Özelleştirilmiş Çoka Çok Alan: Kategorileri düz bir liste yerine etrafı çizgili (
border rounded), derli toplu bir kutu içine aldık.
Artık Create (Oluşturma) ve Update (Güncelleme) işlemlerimiz çok şık. Ancak eklediğimiz bu kitapları görebileceğimiz bir sayfaya ihtiyacımız var.
Artık eksik parçaları tamamlayıp uygulamamızı tam bir "CRUD" (Create, Read, Update, Delete) sistemine dönüştürüyoruz.
Veritabanındaki kitapları listeleyeceğimiz (Read) ve sileceğimiz (Delete) sayfaları hazırlayalım.
1. Görünümleri (Views) Ekleyelim (views.py)
Mevcut views.py dosyanın en altına şu iki fonksiyonu ekle:
# READ: Kitapları Listeleme (Ana Sayfa)
def kitap_listesi(request):
# Veritabanındaki tüm kitapları çekiyoruz
kitaplar = Kitap.objects.all()
return render(request, 'kutuphane/kitap_listesi.html', {'kitaplar': kitaplar})
# DELETE: Kitap Silme İşlemi
def kitap_sil(request, kitap_id):
kitap = get_object_or_404(Kitap, id=kitap_id)
# Silme işlemi her zaman güvenlik için POST isteği ile yapılmalıdır!
if request.method == 'POST':
kitap.delete()
return redirect('kitap_listesi')
# GET isteği gelirse onay sayfasını göster
return render(request, 'kutuphane/kitap_sil_onay.html', {'kitap': kitap})
2. URL Rotalarını Güncelleyelim (urls.py)
Kullanıcı siteye girdiğinde direkt listeyi görsün ve silme işlemi için bir adresi olsun:
from django.urls import path
from . import views
urlpatterns = [
# Ana sayfa rotamız artık kitap listesi
path('', views.kitap_listesi, name='kitap_listesi'),
path('kitap/ekle/', views.kitap_ekle, name='kitap_ekle'),
path('kitap/duzenle/<int:kitap_id>/', views.kitap_duzenle, name='kitap_duzenle'),
# Silme rotası
path('kitap/sil/<int:kitap_id>/', views.kitap_sil, name='kitap_sil'),
]
3. Ana Sayfa (Listeleme) Şablonu (kitap_listesi.html)
templates/kutuphane/ klasörü içine kitap_listesi.html dosyasını oluştur. Burada yine Bootstrap kullanarak şık bir tablo yapacağız. Çoka çok (Many-to-Many) ilişkiyi tabloda nasıl göstereceğimize dikkat et.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Kitap Listesi</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Kütüphane Yönetim Sistemi</h2>
<a href="{% url 'kitap_ekle' %}" class="btn btn-primary">+ Yeni Kitap Ekle</a>
</div>
<div class="card shadow-sm">
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead class="table-dark">
<tr>
<th>#</th>
<th>Kitap Başlığı</th>
<th>Yazar (Bire Çok)</th>
<th>Kategoriler (Çoka Çok)</th>
<th class="text-end">İşlemler</th>
</tr>
</thead>
<tbody>
{% for kitap in kitaplar %}
<tr>
<td>{{ kitap.id }}</td>
<td class="fw-bold">{{ kitap.baslik }}</td>
<td>{{ kitap.yazar.isim }}</td>
<td>
{% for kategori in kitap.kategoriler.all %}
<span class="badge bg-secondary">{{ kategori.isim }}</span>
{% empty %}
<span class="text-muted">Kategori Yok</span>
{% endfor %}
</td>
<td class="text-end">
<a href="{% url 'kitap_duzenle' kitap.id %}" class="btn btn-sm btn-outline-success">Düzenle</a>
<a href="{% url 'kitap_sil' kitap.id %}" class="btn btn-sm btn-outline-danger">Sil</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center text-muted py-4">
Henüz hiç kitap eklenmemiş.
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
4. Silme Onay Sayfası (kitap_sil_onay.html)
Yanlışlıkla butona basılma ihtimaline karşı standart ve güvenli olan "Emin misiniz?" sayfası.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Kitap Sil</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card border-danger shadow-sm">
<div class="card-header bg-danger text-white">
<h5 class="mb-0">Uyarı: Silme İşlemi</h5>
</div>
<div class="card-body text-center">
<p class="fs-5">
<strong>"{{ kitap.baslik }}"</strong> adlı kitabı silmek istediğinize emin misiniz?
</p>
<p class="text-muted">Bu işlem geri alınamaz.</p>
<form method="POST">
{% csrf_token %}
<a href="{% url 'ana_sayfa' %}" class="btn btn-secondary">İptal, Geri Dön</a>
<button type="submit" class="btn btn-danger">Evet, Sil</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Kaynak Kod : https://github.com/nuritiras/okulkutuphane.git
Mükemmel Bir CRUD Uygulaması Çıktı! 🎉
Şu anda elinde ne var özetleyelim:
Modeller: Birbiriyle tam entegre Bire-Bir, Bire-Çok ve Çoka-Çok ilişkili tablolar.
Formlar: Kullanıcıdan gelen verileri doğrulayan ve bootstrap ile süslenmiş yapılar.
Views: Veriyi yaratma (Create), okuma (Read), güncelleme (Update) ve güvenli silme (Delete) mantıkları.
Şablonlar: Bootstrap 5 ile modern, kart tasarımlı, rozet (badge) kullanan şık sayfalar.
Django'nun en büyük süper gücü, sana tek satır kod bile yazmadan muazzam bir Yönetim Paneli (Admin Panel) sunmasıdır.
Ancak Django, senin hangi modelleri bu panelde görmek ve yönetmek istediğini bilemez. İşte admin.py dosyası tam olarak bunun için var: Modellerimizi Django'nun yönetim paneline tanıtmak.
admin.py Dosyasını Güncelleyelim
Uygulamanın içindeki (kutuphane/admin.py) dosyasını aç ve içeriğini aşağıdaki kodla değiştir. Sana hem temel kayıt yöntemini hem de paneli nasıl profesyonelce özelleştirebileceğini göstereceğim:
from django.contrib import admin
from .models import Yazar, Biyografi, Kategori, Kitap
# 1. Temel Kayıt Yöntemi
# Modelleri en sade haliyle admin paneline ekleriz
admin.site.register(Yazar)
admin.site.register(Biyografi)
admin.site.register(Kategori)
# 2. Özelleştirilmiş Kayıt Yöntemi (Dekoratör ile)
# Kitap modeli daha detaylı olduğu için onu özel bir sınıfla yönetelim
@admin.register(Kitap)
class KitapAdmin(admin.ModelAdmin):
# Paneldeki listede hangi sütunlar görünsün?
list_display = ('baslik', 'yazar')
# Sağ tarafa kategoriye göre filtreleme menüsü ekle
list_filter = ('kategoriler',)
# Üst tarafa kitap başlığına göre arama yapabilen bir arama çubuğu ekle
search_fields = ('baslik',)
Bu Paneli Nasıl Göreceğiz? (Superuser Oluşturma)
Kodu kaydettikten sonra bu panele giriş yapabilmek için "en yetkili" bir yönetici hesabı oluşturmamız gerekiyor.
Terminaline dön ve şu adımları izle:
1. Yönetici (Superuser) Hesabı Oluştur:
Aşağıdaki komutu yazıp Enter'a bas. Sana sırasıyla kullanıcı adı, e-posta ve şifre soracak. (Not: Şifreni yazarken ekranda hiçbir karakter görünmez, bu güvenlik amaçlıdır. Sen yazıp Enter'a bas.)
python manage.py createsuperuser
2. Sunucuyu Tekrar Başlat:
Eğer sunucun kapalıysa tekrar çalıştır:
python manage.py runserver
3. Yönetim Paneline Giriş Yap:
Tarayıcında şu adrese git: http://127.0.0.1:8000/admin/
Az önce oluşturduğun kullanıcı adı ve şifreyle giriş yap.
Karşında uygulamamızdaki tüm yazarları, biyografileri, kategorileri ve kitapları ekleyip silebileceğin, arama yapabileceğin harika bir arayüz göreceksin. Hatta az önce formla bizim yaptığımız her şeyi buradan da yapabildiğini fark edeceksin!
Yorumlar
Yorum Gönder