Django ile Sıfırdan Bir İşletme Web Sitesi Geliştirme
Table of Contents
Bu rehber teorik bir Django yazısı olarak başlamıyor. Amacımız önce gerçek bir proje kurmak, her aşamayı test etmek, tarayıcıda görmek.
Senaryomuz Mudos Clean adında yerel bir temizlik şirketi. Web sitesi servisleri listelemeli, her servis için detay sayfası göstermeli, teklif talebi toplamalı ve işletme sahibinin servisleri Django admin üzerinden yönetmesine izin vermeli.
1. Ortamı Doğrulamak
Proje boş bir klasörde başladı. Django dosyası oluşturmadan önce ortam kontrol edildi:
python --version
python -m django --version
Doğrulanan ortam:
Python 3.11.9
Django 5.0.2
Bu adım önemli, çünkü sonraki her şey gerçek bir Django kurulumuna dayanıyor.
2. Django Projesini Oluşturmak
Proje şu komutla oluşturuldu:
python -m django startproject config .
Windows tarafında django-admin her zaman PATH içinde olmayabiliyor. Bu yüzden python -m django kullanmak daha güvenilir bir başlangıç sağladı. Komut, proje seviyesindeki config paketini ve manage.py dosyasını oluşturdu.
İlk yapı:
config/
manage.py
3. Services App’ini Oluşturmak
İş mantığını ayrı bir app içinde tutmak için services app’i oluşturuldu:
python manage.py startapp services
Sonra config/settings.py içindeki INSTALLED_APPS listesine eklendi:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"services",
]
Bu sayede servis listeleme, teklif talepleri, formlar, view’lar, template’ler ve testler services app’i içinde temiz bir sınırla toplandı.
4. İş Modelini Kurmak
Bu işletme web sitesi için iki temel veri modeli yeterli:
Service: Şirketin sunduğu temizlik hizmetiQuoteRequest: Ziyaretçinin gönderdiği teklif talebi
İlk model yapısı:
from django.db import models
class Service(models.Model):
title = models.CharField(max_length=120)
slug = models.SlugField(unique=True)
short_description = models.CharField(max_length=240)
description = models.TextField()
starting_price = models.DecimalField(max_digits=10, decimal_places=2)
is_featured = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["title"]
def __str__(self):
return self.title
class QuoteRequest(models.Model):
TIMING_CHOICES = [
("once", "One-time"),
("weekly", "Weekly"),
("monthly", "Monthly"),
]
service = models.ForeignKey(
Service,
on_delete=models.PROTECT,
related_name="quote_requests",
)
full_name = models.CharField(max_length=120)
phone = models.CharField(max_length=40)
email = models.EmailField(blank=True)
address = models.CharField(max_length=255)
timing = models.CharField(max_length=20, choices=TIMING_CHOICES)
note = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
is_contacted = models.BooleanField(default=False)
class Meta:
ordering = ["-created_at"]
def __str__(self):
return f"{self.full_name} - {self.service.title}"
Bu model kararlarının pratik karşılığı var:
slug, her servis için stabil URL sağlar.is_featured, öne çıkan servisleri homepage’de göstermeyi sağlar.PROTECT, teklif talebi olan bir servisin yanlışlıkla silinmesini engeller.is_contacted, işletme sahibine basit bir takip akışı verir.
5. Admin Yönetimini Eklemek
Bir işletme sitesi için içerik yönetimi erken aşamada gerekir. Bu yüzden Django admin hemen devreye alındı.
services/admin.py:
from django.contrib import admin
from .models import QuoteRequest, Service
@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ("title", "starting_price", "is_featured", "created_at")
list_filter = ("is_featured",)
search_fields = ("title", "short_description")
prepopulated_fields = {"slug": ("title",)}
@admin.register(QuoteRequest)
class QuoteRequestAdmin(admin.ModelAdmin):
list_display = (
"full_name",
"service",
"phone",
"timing",
"is_contacted",
"created_at",
)
list_filter = ("service", "timing", "is_contacted")
search_fields = ("full_name", "phone", "email", "address")
Böylece işletme sahibi kod yazmadan servisleri ve gelen talepleri yönetebilir.
6. Public Workflow’u Kurmak
İlk public akış üç sayfadan oluşuyor:
- Servis listesi
- Servis detay sayfası ve teklif formu
- Teklif talebi başarı sayfası
App URL yapısı:
from django.urls import path
from . import views
app_name = "services"
urlpatterns = [
path("", views.service_list, name="service_list"),
path("services/<slug:slug>/", views.service_detail, name="service_detail"),
path("quote-request-received/", views.quote_success, name="quote_success"),
]
Ana URL dosyası:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("services.urls")),
]
Artık tarayıcının ve testlerin vurabileceği gerçek route’lar var.
7. Teklif Talebi Formunu Eklemek
Form, QuoteRequest modeli üzerinden kuruldu:
from django import forms
from .models import QuoteRequest
class QuoteRequestForm(forms.ModelForm):
class Meta:
model = QuoteRequest
fields = ["full_name", "phone", "email", "address", "timing", "note"]
Detay view’i hem sayfayı render ediyor hem de POST geldiğinde formu kaydediyor:
def service_detail(request, slug):
service = get_object_or_404(Service, slug=slug)
if request.method == "POST":
form = QuoteRequestForm(request.POST)
if form.is_valid():
quote_request = form.save(commit=False)
quote_request.service = service
quote_request.save()
return redirect("services:quote_success")
else:
form = QuoteRequestForm()
return render(
request,
"services/service_detail.html",
{
"service": service,
"form": form,
},
)
Bu ilk tam iş döngüsü: ziyaretçi bir servisi görür, form doldurur ve lead veritabanına kaydedilir.
8. Database Migration Oluşturmak
Modeller yazıldıktan sonra migration oluşturuldu:
python manage.py makemigrations services
Çıktı:
Migrations for 'services':
services\migrations\0001_initial.py
- Create model Service
- Create model QuoteRequest
Bu, model katmanının gerçek database değişikliğine dönüştüğünü kanıtlar.
9. System Check Çalıştırmak
Testlerden önce Django’nun yerleşik kontrolü çalıştırıldı:
python manage.py check
Çıktı:
System check identified no issues (0 silenced).
Bu adım settings, app registration, URL config ve model tanımlarının yapısal olarak doğru olduğunu gösterir.
10. İlk Browser Kontrolünü Düzeltmek
İlk browser kontrolü iki gerçek problemi ortaya çıkardı.
İlki: migration uygulanmadan site açıldığında server error alındı. Çözüm:
python manage.py migrate --noinput
Bu komuttan sonra homepage 200 OK döndü.
İkincisi: sayfa teknik olarak çalışıyordu ama database boş olduğu için kullanıcıya boş görünüyordu. Bu da yeni bir Django projesi için normal ama blog/demonstrasyon için iyi bir ilk ekran değil. Bu yüzden seed command eklendi:
python manage.py seed_services
Bu komut üç demo servis yükler:
- Home Cleaning
- Office Cleaning
- Move-Out Cleaning
Çıktı:
Seeded demo services. Created: 3. Updated: 0.
Bu davranış da testlenebilir hale getirildi.
11. Temel Website Bileşenlerini Eklemek
İlk sürüm çalışıyordu ama sadece ham sayfalardan oluşuyordu. Gerçek bir işletme web sitesinde şu bileşenler gerekir:
- Marka ve navigation içeren header
- Net bir call-to-action
- İşletme bağlamı veren footer
- Mobil cihazlar için responsive grid ve spacing
Tekrarı azaltmak için ortak template eklendi:
services/templates/services/base.html
Sayfalar artık bunu extend ediyor:
{% extends "services/base.html" %}
Homepage, servis detay ve başarı sayfası aynı header, navigation, footer, typography ve responsive kuralları paylaşıyor.
Bu davranış testlere de eklendi:
self.assertContains(response, "Primary navigation")
self.assertContains(response, "A Django-powered service business website")
12. Görsel Geliştirme Senaryosunu Test Etmek
Temel site yapısı kurulduktan sonra şu soru geldi: servisleri daha iyi görselleştirmek isteseydik ne yapardık?
İki yaygın geliştirme test edildi:
- Servis kartlarına ikon eklemek
- Mobil navigation için hamburger menü eklemek
Service modeline küçük bir icon alanı eklendi:
ICON_CHOICES = [
("home", "Home"),
("briefcase", "Office"),
("sparkles", "Deep cleaning"),
]
icon = models.CharField(max_length=32, choices=ICON_CHOICES, default="sparkles")
İkinci migration oluştu:
services\migrations\0002_service_icon.py
- Add field icon to service
Seed command de her demo servise uygun ikon atayacak şekilde güncellendi.
Homepage ikonları şu partial üzerinden render ediyor:
services/templates/services/partials/service_icon.html
Mobil navigation için checkbox tabanlı hafif bir hamburger toggle eklendi. Frontend framework eklemeden test edilebilir ve basit bir çözüm elde edildi.
Testler genişletildi:
self.assertContains(response, "service-icon")
self.assertContains(response, "Toggle navigation menu")
Hamburger menünün açık halini deterministik olarak screenshot almak için şu route kullanıldı:
/?menu=open
13. Görsel Kanıt Almak
Seed data, header/footer, responsive layout, ikonlar ve mobil menü eklendikten sonra lokal siteden screenshot’lar alındı.
Desktop homepage:

Desktop service detail:

Mobile homepage:

Mobile service detail:

Mobil homepage, hamburger menü açık:

Testler davranışı kanıtlar; screenshot’lar ise yazının boş ya da bozuk bir ilk ekrana götürmediğini kanıtlar.
Mobil screenshot review sırasında CTA ve uzun metinlerin dar viewport’ta kırpıldığı görüldü. CSS, küçük ekranlarda güvenli sol hizalama, tek kolon kartlar ve sarılan başlıklar kullanacak şekilde düzeltildi.
14. Davranışı Testlerle Kanıtlamak
Test suite şu kritik davranışları kapsıyor:
- Servisin string representation’ı title olarak dönüyor.
- Servis liste sayfası servisleri gösteriyor.
- Servis detay sayfası teklif formunu gösteriyor.
- Teklif talebi kaydedilebiliyor.
- Seed command demo servisleri oluşturuyor.
- Header ve footer render ediliyor.
- Servis ikonları homepage’de görünüyor.
- Mobil menü açık halde render edilebiliyor.
- Versiyon dosyası okunuyor.
- Release command
VERSIONveCHANGELOG.mddosyalarını güncelliyor. - Remote release manifest ve workflow adımları doğrulanıyor.
Test komutu:
python manage.py test
Sonuç:
Found 11 test(s).
System check identified no issues (0 silenced).
...........
----------------------------------------------------------------------
Ran 11 tests in 0.151s
OK
Bu noktada site sadece anlatılmıyor; davranışları testlerle kanıtlanıyor.
15. Bakım Release’lerini Otomatikleştirmek
İlk çalışan sürümden sonra gerçek soru maintenance olur. Bir işletme sitesi yayına alındıktan sonra da yaşamaya devam eder: metinler değişir, servisler güncellenir, tasarım iyileştirilir, bug fix gelir, bağımlılıklar güncellenir ve ileride farklı dil versiyonları eklenir.
Bu süreci tekrar edilebilir yapmak için projeye basit bir versioning sistemi eklendi:
VERSION
CHANGELOG.md
VERSION dosyası aktif site versiyonunu tutar:
0.4.0
Footer bu dosyayı context processor üzerinden okur ve her sayfada gösterir:
Version
v0.4.0
Bakım release’i hazırlamak için management command eklendi:
python manage.py prepare_release 0.4.0 \
--note "Added remote tag-based release workflow." \
--note "Added release manifest artifact for deployment automation." \
--note "Added remote release runbook with deploy and rollback steps."
Komut VERSION dosyasını günceller ve CHANGELOG.md dosyasının başına yeni release notlarını ekler:
## 0.4.0
- Added remote tag-based release workflow.
- Added release manifest artifact for deployment automation.
- Added remote release runbook with deploy and rollback steps.
Lokal bakım akışı:
- Küçük güncellemeyi yap.
- Test ekle ya da mevcut testleri güncelle.
python manage.py testçalıştır.python manage.py prepare_releaseile release hazırla.CHANGELOG.mdkontrol et.- Deploy et.
Bu yapı, ileride Türkçe veya başka dil sürümleri üretirken de işe yarar. Her lokalizasyon kontrollü bir versiyona bağlanabilir.
16. Remote Release’leri Otomatikleştirmek
Lokal release hazırlığı faydalı ama tek başına yeterli değil. Yeni versiyon remote’a push edildiğinde ne olacağını da tanımlamak gerekir.
Bu proje için tag tabanlı bir release workflow eklendi:
.github/workflows/release.yml
Remote release, semantic version tag ile tetiklenir:
git tag v0.4.0
git push origin v0.4.0
Workflow mantığı:
Git tag -> CI tests -> release manifest -> deploy hook -> health check
Remote workflow lokal testleri tekrar çalıştırır:
python manage.py check
python manage.py test
Sonra release manifest üretir:
python manage.py build_release_manifest --environment production --deploy-target managed-paas
release-manifest.json şu bilgileri taşır:
{
"version": "0.4.0",
"tag": "v0.4.0",
"environment": "production",
"deploy_target": "managed-paas",
"required_checks": [
"python manage.py test",
"python manage.py check"
],
"remote_release_steps": [
"push git tag",
"run CI checks",
"deploy on successful checks",
"run post-deploy health check"
],
"rollback": {
"strategy": "redeploy previous successful tag"
}
}
Bu manifest her release için izlenebilir bir artifact verir: hangi versiyon, hangi tag, hangi ortam, hangi deploy hedefi, hangi kontroller ve hangi rollback stratejisi.
Workflow iki secret kullanır:
DEPLOY_HOOK_URL
PRODUCTION_HEALTHCHECK_URL
DEPLOYHOOKURL, hosting provider’ın deploy hook adresidir. Testler başarılı olduktan sonra production deploy tetiklenebilir.
PRODUCTIONHEALTHCHECKURL, deploy sonrasında başarılı dönmesi gereken public URL’dir.
Ayrıca remote release runbook eklendi:
docs/remote-release-runbook.md
Remote release akışı:
- Lokal release’i
prepare_releaseile hazırla. - Testleri lokal çalıştır.
VERSION,CHANGELOG.mdve kod değişikliklerini commit et.v0.4.0gibi bir Git tag oluştur.- Tag’i push et.
- CI testlerinin çalışmasını bekle.
- Deploy hook’un yeni sürümü yayınlamasını bekle.
- Health check’i doğrula.
- Sorun varsa önceki başarılı tag’i tekrar deploy ederek rollback yap.
Bu akış, gelecekteki Türkçe sürüm için de aynen kullanılabilir.
17. Nereye Deploy Etmeliyiz?
Bu proje hâlâ lokal tutorial uygulaması. Production’a geçmeden önce Django deployment checklist ele alınmalı:
SECRET_KEYenvironment variable’a alınmalı.DEBUG = Falseolmalı.ALLOWED_HOSTSayarlanmalı.- Lokal SQLite yerine PostgreSQL kullanılmalı.
- Production database’e migration uygulanmalı.
- Static dosyalar
python manage.py collectstaticile toplanmalı. - Django production’da Gunicorn gibi bir WSGI server ile çalışmalı.
- VPS kullanılıyorsa önüne Nginx konulmalı.
- HTTPS etkin olmalı.
Bu proje için iki mantıklı yol var.
En basit yol: managed PaaS
Python web service ve PostgreSQL sunan bir platform kullanmak ilk yayın için daha kolaydır:
Git repository -> Python web service -> PostgreSQL -> environment variables -> custom domain
Render, Fly.io, Railway veya benzeri bir platform bu tür uygulamalar için uygundur. Burada önemli olan platform isminden çok şu özelliklerdir: Python runtime, kalıcı PostgreSQL, environment variable desteği, HTTPS, deploy logları ve tahmin edilebilir build/deploy komutları.
Daha kontrollü yol: VPS
Sunucu yönetimini de öğretmek istiyorsak Ubuntu VPS kullanılabilir:
Ubuntu VPS
Nginx
Gunicorn
Django
PostgreSQL
systemd
HTTPS
Bu daha fazla kontrol verir ama firewall, update, process manager, log, backup ve SSL yenileme sorumluluğu da getirir.
Bu blog serisi için ilk adım olarak managed PaaS daha mantıklı. Daha sonra aynı projeyi VPS üzerinden deploy ederek sunucu yönetimi ayrı bir yazıda anlatılabilir.
18. Sırada Ne Var?
Bundan sonraki akışta aynı disiplin korunmalı:
- Yeni davranışı ekle.
- Test yaz ya da güncelle.
- Testleri çalıştır.
- Blog içeriğini ancak sonra güncelle.
İyi sonraki adımlar:
- Production-ready static files ve styling
- Service area modeli
- Teklif talebi gelince email bildirimi
- Servisler için SEO alanları
- Deployment’ı gerçek ortamda tamamlama
- Türkçe ve diğer dil sürümlerini versioned release olarak yayınlama
Sonuç
Bu yazının ana fikri süreç. Django tutorial’ı, sadece güzel anlatıldığında değil, çalışan kod ve geçen testlerle desteklendiğinde güçlenir. Bu iterasyonda proje kuruldu, iş modeli eklendi, public sayfalar oluşturuldu, teklif talepleri kaydedildi, migration’lar üretildi, boş ekran problemi seed data ile çözüldü, header/footer ve responsive yapı eklendi, servis ikonları ve hamburger navigation test edildi, lokal ve remote release otomasyonu kuruldu, desktop ve mobil screenshot’lar alındı ve 11 test başarıyla geçti.
Artık elimizde Türkçe dahil farklı dil versiyonları için güvenilir bir kaynak yapı var: her bölüm gerçek kod, gerçek test ve gerçek çıktıya dayanıyor.
References
- Django Documentation
- Django Testing Tools
- Django Deployment Checklist
- Django How to Deploy with WSGI
Evidence
- Ortam Python 3.11.9 ve Django 5.0.2 ile doğrulandı
- Proje python -m django startproject config . komutuyla oluşturuldu
- services app’i python manage.py startapp services komutuyla eklendi
- İlk browser kontrolü, migration uygulanmadan 500 hatası verdi
- İlk başarılı sayfa render’ı, demo servisler seed edilmeden boş göründü
- System check sorunsuz geçti
- Demo servisler python manage.py seed_services komutuyla eklendi
- Test suite başarılı: 11 test geçti
- Header, navigation, footer ve responsive layout için ortak base template eklendi
- Servis ikon alanı eklendi ve seed data üzerinden doğrulandı
- Mobil hamburger navigation eklendi ve açık menü screenshot’ı ile test edildi
- Bakım otomasyonu VERSION, CHANGELOG.md ve prepare_release komutu ile eklendi
- Footer artık VERSION dosyasından gelen aktif versiyonu gösteriyor
- Remote release otomasyonu tag tabanlı GitHub Actions workflow’u ile eklendi
- Deploy ve rollback izlenebilirliği için release manifest artifact üretildi
- Homepage ve service detail sayfaları için desktop screenshot’lar alındı
- Homepage ve service detail sayfaları için 390×844 mobil screenshot’lar alındı