Django JSON ve API Kavramları
1. API Nedir?
API (Application Programming Interface) farklı yazılımların birbiri ile veri alışverişi yapmasını sağlayan arayüzdür.
Örnek:
Bir Flutter mobil uygulaması
Bir React web uygulaması
Bir IoT cihazı
hepsi bir Django API ile konuşabilir.
Günlük Hayattan Örnek
API'yi restoran garsonu gibi düşünebiliriz.
Müşteri → Garson → Mutfak
←
Yazılım dünyasında:
Mobil Uygulama → API → Veritabanı
←
2. JSON Nedir?
JSON (JavaScript Object Notation) veri taşımak için kullanılan metin tabanlı veri formatıdır.
JSON Örneği
{
"ad": "Ali",
"soyad": "Yılmaz",
"yas": 16,
"sehir": "Ankara"
}
JSON Özellikleri
Anahtar → Değer yapısı vardır
İnsan tarafından okunabilir
API'lerde en çok kullanılan veri formatıdır
3. Django'da JSON Döndürme
Django'da JSON döndürmek için JsonResponse kullanılır.
Örnek
views.py
from django.http import JsonResponse
def ogrenci_api(request):
veri = {
"ad": "Ayşe",
"soyad": "Demir",
"yas": 17
}
return JsonResponse(veri)
Çalışma Mantığı
Browser
↓
Django View
↓
Python Dictionary
↓
JSON
↓
Kullanıcı
4. JSON Liste Döndürme
API'lerde genelde liste şeklinde veri döndürülür.
Örnek
def ogrenciler(request):
veri = [
{"ad": "Ali", "yas": 16},
{"ad": "Ayşe", "yas": 17},
{"ad": "Mehmet", "yas": 15}
]
return JsonResponse(veri, safe=False)
safe=False Neden Kullanılır?
Django normalde sadece dictionary döndürmeye izin verir.
Liste döndürmek için:
safe=False
kullanılır.
5. Django Model Verisini JSON Yapmak
Gerçek projelerde veri modelden gelir.
Model
models.py
from django.db import models
class Ogrenci(models.Model):
ad = models.CharField(max_length=100)
soyad = models.CharField(max_length=100)
yas = models.IntegerField()
def __str__(self):
return self.ad
View
from django.http import JsonResponse
from .models import Ogrenci
def ogrenci_listesi(request):
ogrenciler = Ogrenci.objects.all()
veri = list(ogrenciler.values())
return JsonResponse(veri, safe=False)
Çalışma Diyagramı
Database
│
▼
Django Model
│
▼
QuerySet
│
▼
values()
│
▼
JSON
│
▼
Client
6. Django REST API Mantığı
REST API'de her işlem için HTTP method kullanılır.
| HTTP Method | İşlem |
|---|---|
| GET | Veri okuma |
| POST | Veri ekleme |
| PUT | Veri güncelleme |
| DELETE | Veri silme |
7. Basit Django API Örneği
GET API
def ogrenci_list(request):
if request.method == "GET":
ogrenciler = Ogrenci.objects.all()
veri = list(ogrenciler.values())
return JsonResponse(veri, safe=False)
POST API
import json
def ogrenci_ekle(request):
if request.method == "POST":
data = json.loads(request.body)
Ogrenci.objects.create(
ad=data["ad"],
soyad=data["soyad"],
yas=data["yas"]
)
return JsonResponse({"mesaj": "Öğrenci eklendi"})
8. Django API Veri Alma
POST isteğinde veri şu şekilde gelir:
{
"ad":"Ahmet",
"soyad":"Kaya",
"yas":18
}
Python'da okumak için:
data = json.loads(request.body)
9. API Endpoint Nedir?
Endpoint = API adresidir.
Örnek:
/api/ogrenciler/
/api/ogrenci-ekle/
/api/ogrenci-sil/5
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("api/ogrenciler/", views.ogrenci_list),
path("api/ogrenci-ekle/", views.ogrenci_ekle),
]
10. API Test Etme
API test etmek için:
Araçlar
Postman
Thunder Client
curl
Browser
GET isteği
http://127.0.0.1:8000/api/ogrenciler/
POST isteği
POST /api/ogrenci-ekle/
Body:
{
"ad":"Ali",
"soyad":"Yılmaz",
"yas":16
}
11. Mini Proje
Öğrenci API Sistemi
Özellikler
öğrenci listeleme
öğrenci ekleme
öğrenci silme
Model
class Ogrenci(models.Model):
ad = models.CharField(max_length=100)
soyad = models.CharField(max_length=100)
yas = models.IntegerField()
View
def ogrenci_sil(request, id):
ogrenci = Ogrenci.objects.get(id=id)
ogrenci.delete()
return JsonResponse({"mesaj":"silindi"})
urls.py
urlpatterns = [
path("api/ogrenciler/", views.ogrenci_list),
path("api/ogrenci-ekle/", views.ogrenci_ekle),
path("api/ogrenci-sil/<int:id>/", views.ogrenci_sil),
]
12. Django API Kullanım Alanları
Django API şu sistemlerde kullanılır:
Mobil Uygulamalar
Flutter
Kotlin
Swift
Web Frontend
React
Vue
Angular
Diğer Sistemler
IoT cihazları
Mikro servisler
Veri entegrasyonu
13. Django API Mimarisi
Mobil Uygulama
│
│ JSON
▼
Django API
│
│ ORM
▼
Database
14. Profesyonel Projelerde Kullanılan Araç
Gerçek projelerde genelde şu kütüphane kullanılır:
Django REST Framework
Avantajları:
otomatik serializer
authentication
pagination
browsable API
filtering
15. Django REST Framework Örneği
Serializer:
from rest_framework import serializers
from .models import Ogrenci
class OgrenciSerializer(serializers.ModelSerializer):
class Meta:
model = Ogrenci
fields = "__all__"
View:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(["GET"])
def ogrenciler(request):
ogrenciler = Ogrenci.objects.all()
serializer = OgrenciSerializer(ogrenciler, many=True)
return Response(serializer.data)
Sonuç
Django ile API geliştirme şu adımlardan oluşur:
1️⃣ Model oluşturma
2️⃣ Veriyi JSON'a çevirme
3️⃣ HTTP method kullanma
4️⃣ URL endpoint oluşturma
5️⃣ API test etme
Öğrenci İçin Alıştırma
Aşağıdaki API'yi yazınız.
Kitap API
Model:
Kitap
- ad
- yazar
- sayfa
API'ler:
GET /api/kitaplar
POST /api/kitap-ekle
DELETE /api/kitap-sil
Bu yapı özellikle mobil uygulama + backend mimarisini anlamak için kullanılır.
1. Django API Genel Mimari Diyagramı
Bir mobil uygulama doğrudan veritabanına bağlanmaz. Arada API katmanı olur.
┌───────────────────────┐
│ Flutter App │
│ (Mobil Uygulama) │
└───────────┬───────────┘
│
│ HTTP Request
│ (GET / POST / PUT / DELETE)
▼
┌───────────────────────┐
│ Django API │
│ Views │
└───────────┬───────────┘
│
│ ORM
▼
┌───────────────────────┐
│ Django Model │
│ (Veri Yapısı) │
└───────────┬───────────┘
│
▼
┌───────────────────────┐
│ Database │
│ SQLite / PostgreSQL │
└───────────────────────┘
Akış
1️⃣ Flutter veri ister
2️⃣ Django API isteği alır
3️⃣ Model üzerinden veritabanına gider
4️⃣ JSON olarak cevap döner
2. API Request Response Diyagramı
Flutter App
│
│ GET /api/ogrenciler
▼
Django URL Router
│
▼
Django View
│
▼
Django Model (ORM)
│
▼
Database
│
│
▲
│
JSON Response
│
▼
Flutter App UI
3. Django API İç Yapısı
Django içinde API şu dosyalarla çalışır.
Django Project
│
├── models.py
│ Veri yapısı
│
├── views.py
│ API işlemleri
│
├── urls.py
│ API adresleri
│
└── serializers.py
JSON dönüşümü (DRF)
4. Flutter + Django Veri Akışı
Flutter UI
│
│ HTTP Request
▼
Flutter Service Layer
(API çağrısı)
│
▼
Internet
│
▼
Django API
│
▼
Database
5. Flutter + Django Tam Proje
Şimdi sana tam mini proje mimarisi gösteriyorum.
Proje
Öğrenci Listeleme Uygulaması
Özellikler
öğrenci ekleme
öğrenci listeleme
Flutter'da gösterme
6. Django Backend Kurulumu
Proje oluşturma
django-admin startproject okul_api
cd okul_api
python manage.py startapp ogrenci
15. Tam Sistem Diyagramı
Flutter Mobile App
│
│ HTTP Request
▼
Django REST API
│
│ ORM
▼
Database
│
│ JSON
▼
Flutter UI Güncellenir
BÖLÜM 1: Django ile RESTful API Geliştirme (Backend)
Django, normalde kendi HTML şablonlarını (template) sunmak üzere tasarlanmıştır.
Ancak biz burada Django'yu sadece bir veri sağlayıcısı (API) olarak kullanacağız.
Dışarıdan ekstra bir kütüphane (Django REST Framework gibi) kullanmadan,
saf Django ile JSON yanıtları döndüreceğiz.
1. Modelin Oluşturulması (models.py)
İlk olarak veritabanımızda "Öğrenci" tablosunu nasıl tutacağımızı tanımlıyoruz.
Python:# ogrenci/models.py
from django.db import models
class Ogrenci(models.Model):
ad = models.CharField(max_length=100)
soyad = models.CharField(max_length=100)
yas = models.IntegerField()
def __str__(self):
return f"{self.ad} {self.soyad}"
Not: Bu kodu yazdıktan sonra python manage.py makemigrations ve
python manage.py migrate komutlarını çalıştırarak veritabanı tablolarını oluşturmayı unutmayın.
2. Görünümlerin ve İş Mantığının Yazılması (views.py)
Uygulamamızın kalbi burasıdır. Flutter'dan gelecek olan GET, POST, PUT ve DELETE
isteklerini burada karşılayacağız.
Django normalde dışarıdan gelen form verileri için bir güvenlik anahtarı (CSRF Token) bekler.
Flutter gibi dış bir istemciden istek atacağımız için
@csrf_exempt dekoratörünü kullanarak bu sayfaları dış erişime açıyoruz.
Python:# ogrenci/views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from .models import Ogrenci
import json
# 1. READ (Okuma - GET)
def ogrenci_list(request):
ogrenciler = list(Ogrenci.objects.values())
return JsonResponse(ogrenciler, safe=False)
# 2. CREATE (Oluşturma - POST)
@csrf_exempt
def ogrenci_ekle(request):
if request.method == "POST":
data = json.loads(request.body)
Ogrenci.objects.create(
ad=data["ad"],
soyad=data["soyad"],
yas=data["yas"]
)
return JsonResponse({"mesaj": "Öğrenci başarıyla eklendi"}, status=201)
# 3. UPDATE (Güncelleme - PUT)
@csrf_exempt
def ogrenci_guncelle(request, id):
if request.method == "PUT":
try:
ogrenci = Ogrenci.objects.get(id=id)
data = json.loads(request.body)
ogrenci.ad = data.get("ad", ogrenci.ad)
ogrenci.soyad = data.get("soyad", ogrenci.soyad)
ogrenci.yas = data.get("yas", ogrenci.yas)
ogrenci.save()
return JsonResponse({"mesaj": "Öğrenci güncellendi"}, status=200)
except Ogrenci.DoesNotExist:
return JsonResponse({"hata": "Öğrenci bulunamadı"}, status=404)
# 4. DELETE (Silme - DELETE)
@csrf_exempt
def ogrenci_sil(request, id):
if request.method == "DELETE":
try:
ogrenci = Ogrenci.objects.get(id=id)
ogrenci.delete()
return JsonResponse({"mesaj": "Öğrenci silindi"}, status=200)
except Ogrenci.DoesNotExist:
return JsonResponse({"hata": "Öğrenci bulunamadı"}, status=404)
3. API Uç Noktalarının Tanımlanması (urls.py)
Son olarak, yazdığımız bu fonksiyonları belirli web adreslerine (URL'lere) bağlıyoruz.
<int:id> yapısı sayesinde, URL'den gelen öğrenci numarasını dinamik olarak yakalayabiliyoruz
(Örneğin: /api/ogrenci-sil/5/).
Python:# ogrenci/urls.py (veya ana urls.py)
from django.urls import path
from ogrenci import views
urlpatterns = [
path("api/ogrenciler/", views.ogrenci_list),
path("api/ogrenci-ekle/", views.ogrenci_ekle),
path("api/ogrenci-guncelle/<int:id>/", views.ogrenci_guncelle),
path("api/ogrenci-sil/<int:id>/", views.ogrenci_sil),
]
Django API'miz hazır! python manage.py runserver komutu ile sunucumuzu ayağa kaldırıp Flutter tarafına geçebiliriz.
BÖLÜM 2: Flutter ile Mobil İstemci Geliştirme (Frontend)
Uygulamamızın mobil arayüzünü Flutter ile inşa edeceğiz.
Projenize API istekleri atabilmek için terminalden flutter pub add http komutunu çalıştırarak
http paketini projenize dahil edin.
1. Veri Modeli (ogrenci.dart)
Django'dan gelen JSON verilerini Dart dilinde kullanabileceğimiz Nesnelere dönüştürmemiz
ve kendi verilerimizi Django'ya yollarken tekrar JSON'a çevirmemiz gerekir.
Dart:// ogrenci.dart
class Ogrenci {
final int? id; // Yeni kayıtlarda ID olmaz, o yüzden '?' kullanıyoruz
final String ad;
final String soyad;
final int yas;
Ogrenci({
this.id,
required this.ad,
required this.soyad,
required this.yas,
});
// Gelen JSON verisini Dart nesnesine çevirir (GET işlemleri için)
factory Ogrenci.fromJson(Map<String, dynamic> json) {
return Ogrenci(
id: json['id'],
ad: json['ad'],
soyad: json['soyad'],
yas: json['yas'],
);
}
// Dart nesnesini JSON verisine çevirir (POST ve PUT işlemleri için)
Map<String, dynamic> toJson() {
return {
'ad': ad,
'soyad': soyad,
'yas': yas,
};
}
}
2. API Servis Katmanı (api_service.dart)
Tüm HTTP işlemlerimizi tek bir merkezden yönetmek, kodun temiz kalmasını sağlar.
Önemli İpucu: Android emülatör kullanıyorsanız 127.0.0.1 yerine 10.0.2.2 kullanmalısınız,
aksi takdirde bağlantı reddedilir.
Dart:// api_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'ogrenci.dart';
class ApiService {
// Django sunucumuzun adresi
static const baseUrl = "http://127.0.0.1:8000";
// 1. GET: Tüm Öğrencileri Getir
static Future<List<Ogrenci>> ogrencileriGetir() async {
final response = await http.get(Uri.parse("$baseUrl/api/ogrenciler/"));
if (response.statusCode == 200) {
// Türkçe karakter sorunu olmaması için utf8.decode kullanıyoruz
List data = json.decode(utf8.decode(response.bodyBytes));
return data.map((e) => Ogrenci.fromJson(e)).toList();
} else {
throw Exception("API Hatası: Veriler alınamadı.");
}
}
// 2. POST: Yeni Öğrenci Ekle
static Future<void> ogrenciEkle(Ogrenci ogrenci) async {
final response = await http.post(
Uri.parse("$baseUrl/api/ogrenci-ekle/"),
headers: {"Content-Type": "application/json; charset=UTF-8"},
body: json.encode(ogrenci.toJson()),
);
if (response.statusCode != 201 && response.statusCode != 200) {
throw Exception("Ekleme başarısız.");
}
}
// 3. PUT: Öğrenci Güncelle
static Future<void> ogrenciGuncelle(int id, Ogrenci ogrenci) async {
final response = await http.put(
Uri.parse("$baseUrl/api/ogrenci-guncelle/$id/"),
headers: {"Content-Type": "application/json; charset=UTF-8"},
body: json.encode(ogrenci.toJson()),
);
if (response.statusCode != 200) {
throw Exception("Güncelleme başarısız.");
}
}
// 4. DELETE: Öğrenci Sil
static Future<void> ogrenciSil(int id) async {
final response = await http.delete(
Uri.parse("$baseUrl/api/ogrenci-sil/$id/")
);
if (response.statusCode != 204 && response.statusCode != 200) {
throw Exception("Silme işlemi başarısız.");
}
}
}
3. Kullanıcı Arayüzü (main.dart)
Artık verilerimizi ekrana çizebiliriz. Listeyi API'den asenkron (Future) çektiğimiz için
FutureBuilder kullanacağız. Veriler eklendiğinde veya silindiğinde ekranın güncellenmesi
için bir StatefulWidget kullanıyoruz.
Dart:// main.dart
import 'package:flutter/material.dart';
import 'ogrenci.dart';
import 'api_service.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Öğrenci Yönetimi',
theme: ThemeData(primarySwatch: Colors.indigo),
home: const OgrenciListesiEkrani(),
);
}
}
class OgrenciListesiEkrani extends StatefulWidget {
const OgrenciListesiEkrani({super.key});
@override
State<OgrenciListesiEkrani> createState() => _OgrenciListesiEkraniState();
}
class _OgrenciListesiEkraniState extends State<OgrenciListesiEkrani> {
// Gelecekte gelecek olan veri listemiz
late Future<List<Ogrenci>> _ogrenciListesiFuture;
@override
void initState() {
super.initState();
_listeyiYenile();
}
// API'ye tekrar istek atarak UI'ı güncelleyen fonksiyon
void _listeyiYenile() {
setState(() {
_ogrenciListesiFuture = ApiService.ogrencileriGetir();
});
}
// Ekleme ve Güncelleme için açılan form penceresi
void _formGoster([Ogrenci? mevcutOgrenci]) {
final adController = TextEditingController(text: mevcutOgrenci?.ad ?? '');
final soyadController = TextEditingController(text: mevcutOgrenci?.soyad ?? '');
final yasController = TextEditingController(text: mevcutOgrenci?.yas.toString() ?? '');
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(mevcutOgrenci == null ? 'Öğrenci Ekle' : 'Öğrenci Düzenle'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(controller: adController, decoration: const InputDecoration(labelText: 'Ad')),
TextField(controller: soyadController, decoration: const InputDecoration(labelText: 'Soyad')),
TextField(
controller: yasController,
decoration: const InputDecoration(labelText: 'Yaş'),
keyboardType: TextInputType.number,
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('İptal'),
),
ElevatedButton(
onPressed: () async {
final yeniOgrenci = Ogrenci(
ad: adController.text,
soyad: soyadController.text,
yas: int.tryParse(yasController.text) ?? 0,
);
if (mevcutOgrenci == null) {
await ApiService.ogrenciEkle(yeniOgrenci);
} else {
await ApiService.ogrenciGuncelle(mevcutOgrenci.id!, yeniOgrenci);
}
if (mounted) Navigator.pop(context); // Dialogu kapat
_listeyiYenile(); // Değişiklikleri görmek için listeyi yenile
},
child: const Text('Kaydet'),
),
],
);
},
);
}
void _ogrenciSil(int id) async {
await ApiService.ogrenciSil(id);
_listeyiYenile();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Öğrenciler')),
body: FutureBuilder<List<Ogrenci>>(
future: _ogrenciListesiFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Hata: ${snapshot.error}'));
} else if (snapshot.hasData) {
final ogrenciler = snapshot.data!;
if (ogrenciler.isEmpty) {
return const Center(child: Text('Kayıtlı öğrenci yok.'));
}
return ListView.builder(
itemCount: ogrenciler.length,
itemBuilder: (context, index) {
final ogrenci = ogrenciler[index];
return Card(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: ListTile(
leading: CircleAvatar(child: Text(ogrenci.id.toString())),
title: Text('${ogrenci.ad} ${ogrenci.soyad}'),
subtitle: Text('Yaş: ${ogrenci.yas}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.blue),
onPressed: () => _formGoster(ogrenci),
),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => _ogrenciSil(ogrenci.id!),
),
],
),
),
);
},
);
}
return const SizedBox.shrink();
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _formGoster(),
child: const Icon(Icons.add),
),
);
}
}
Sonuç
Django ile veritabanı yönetimi yapabilen, JSON üreten bir backend ve
Flutter ile bu verileri tüketip ekranda gösteren dinamik bir frontend geliştirdiniz.
Bu mimari, günümüzde Instagram'dan bankacılık uygulamalarına kadar pek çok modern
projenin temelini oluşturmaktadır.
Django Kaynak Kod : https://github.com/nuritiras/ogrenci.gitFlutter Kaynak Kod : https://github.com/nuritiras/flutter_ogrenci_api.git
16. Gerçek Projelerde Kullanılan Mimari
Profesyonel projelerde genelde şu mimari kullanılır:
Flutter
│
▼
Repository Layer
│
▼
API Service
│
▼
Django REST API
│
▼
PostgreSQL
1️⃣ Flutter + Django tam gerçek proje
Örneğin:
Öğrenci kayıt sistemi
Login sistemi
JWT authentication
CRUD işlemleri
2️⃣ Profesyonel Django API mimari diyagram seti
Şunları içeren:
REST API mimarisi
MVC vs MVT diyagramı
Django request lifecycle
Flutter → API → Database akışı
3️⃣ Flutter + Django okul yönetim sistemi (tam proje)
İçinde
öğrenci
öğretmen
not sistemi
giriş sistemi
Flutter + Django REST API
1. Giriş
Modern uygulamalarda genellikle şu mimari kullanılır:
Frontend (Mobil / Web) → Flutter
Backend (API) → Django
Veritabanı → PostgreSQL / SQLite
Bu mimariye Client – Server Architecture denir.
2. Sistem Mimarisi
Flutter Mobile App
│
│ HTTP Request (JSON)
▼
Django REST API
│
│ ORM
▼
Database (PostgreSQL / SQLite)
│
│ JSON Response
▼
Flutter UI Güncellenir
3. REST API Nedir?
REST API, HTTP protokolünü kullanarak veri alışverişi yapan bir mimaridir.
| HTTP Method | İşlem |
|---|---|
| GET | Veri alma |
| POST | Veri ekleme |
| PUT | Veri güncelleme |
| DELETE | Veri silme |
Örnek endpointler:
GET /api/ogrenciler
POST /api/ogrenci-ekle
PUT /api/ogrenci-guncelle/1
DELETE /api/ogrenci-sil/1
Modern yazılım dünyasında en çok tercih edilen Client-Server (İstemci-Sunucu) mimarisini kullanarak basit bir öğrenci yönetim sisteminin nasıl inşa edileceğini anlatır.
1. Mimari Yapı ve Veri Akışı
Profesyonel projelerde verinin izlediği yol şöyledir:
Flutter UI: Kullanıcı düğmeye basar (Örn: Öğrenci Listele).
Repository/Service: Flutter, Django API'sine bir HTTP isteği (GET/POST) gönderir.
Django URL & View: İstek karşılanır, veritabanından veri çekilir.
Serializer: Veritabanı nesneleri (Queryset) JSON formatına dönüştürülür.
Response: JSON veri Flutter'a döner ve arayüz güncellenir.
2. Backend: Django REST API Kurulumu
Adım 1: Ortam Hazırlığı
Pardus veya macOS terminalinizde şu komutları çalıştırın:
mkdir okul_projesi && cd okul_projesi
python3 -m venv venv
source venv/bin/activate # macOS/Linux için
pip install django djangorestframework django-cors-headers
Önemli Not:
django-cors-headerspaketini ekledik çünkü Flutter (Client) ve Django (Server) farklı portlarda çalıştığı için CORS hatası almamak gerekir.
Adım 2: Proje ve App Oluşturma
django-admin startproject backend .
python manage.py startapp ogrenci
Adım 3: Ayarlar (settings.py)
INSTALLED_APPS ve MIDDLEWARE kısımlarına eklemeleri yapın:
INSTALLED_APPS = [
# ...
'rest_framework',
'corsheaders',
'ogrenci',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # En üste ekle
# ...
]
CORS_ALLOW_ALL_ORIGINS = True # Geliştirme aşaması için
Adım 4: Model ve Serializer
ogrenci/models.py:
from django.db import models
class Ogrenci(models.Model):
ad = models.CharField(max_length=100)
soyad = models.CharField(max_length=100)
numara = models.IntegerField(unique=True)
def __str__(self):
return f"{self.ad} {self.soyad}"
ogrenci/serializers.py:
from rest_framework import serializers
from .models import Ogrenci
class OgrenciSerializer(serializers.ModelSerializer):
class Meta:
model = Ogrenci
fields = '__all__'
Adım 5: Views ve URLS
ogrenci/views.py:
from rest_framework import viewsets
from .models import Ogrenci
from .serializers import OgrenciSerializer
class OgrenciViewSet(viewsets.ModelViewSet):
queryset = Ogrenci.objects.all()
serializer_class = OgrenciSerializer
(Not: viewsets kullanarak GET, POST, PUT, DELETE işlemlerini tek seferde hallettik.)
backend/urls.py:
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from ogrenci.views import OgrenciViewSet
router = DefaultRouter()
router.register(r'ogrenciler', OgrenciViewSet)
urlpatterns = [
path('admin/', admin.hsite.urls),
path('api/', include(router.urls)),
]
3. Frontend: Flutter Uygulaması
Adım 1: Bağımlılıklar
pubspec.yaml dosyasına ekleyin:
dependencies:
http: ^1.1.0
1. lib/ogrenci_model.dart
Veri yapısını tanımlayan sınıfımız.
class Ogrenci {
final int? id;
final String ad;
final String soyad;
final int numara;
Ogrenci({this.id, required this.ad, required this.soyad, required this.numara});
// JSON'dan Dart nesnesine dönüşüm
factory Ogrenci.fromJson(Map<String, dynamic> json) => Ogrenci(
id: json['id'],
ad: json['ad'],
soyad: json['soyad'],
numara: json['numara'],
);
// Dart nesnesinden JSON'a dönüşüm (Ekleme işlemi için)
Map<String, dynamic> toJson() => {
"ad": ad,
"soyad": soyad,
"numara": numara,
};
}
2. lib/api_service.dart
Django API ile konuşan servis katmanımız.
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'ogrenci_model.dart';
class ApiService {
// ÖNEMLİ: Android Emülatör kullanıyorsanız 127.0.0.1 yerine 10.0.2.2 kullanın.
// Gerçek cihaz için bilgisayarınızın yerel IP'sini yazın (örn: 192.168.1.x)
static const String baseUrl = "http://127.0.0.1:8000/api/ogrenciler/";
// Listeleme (GET)
Future<List<Ogrenci>> fetchOgrenciler() async {
try {
final response = await http.get(Uri.parse(baseUrl));
if (response.statusCode == 200) {
// Türkçe karakterler için utf8.decode kullanımı kritiktir
List jsonResponse = json.decode(utf8.decode(response.bodyBytes));
return jsonResponse.map((data) => Ogrenci.fromJson(data)).toList();
} else {
throw Exception('Veriler alınamadı: ${response.statusCode}');
}
} catch (e) {
throw Exception('Bağlantı hatası: $e');
}
}
// Ekleme (POST)
Future<bool> ogrenciEkle(Ogrenci ogrenci) async {
try {
final response = await http.post(
Uri.parse(baseUrl),
headers: {"Content-Type": "application/json"},
body: json.encode(ogrenci.toJson()),
);
return response.statusCode == 201;
} catch (e) {
return false;
}
}
}
3. lib/main.dart
Uygulamanın ana giriş noktası ve arayüzü.
import 'package:flutter/material.dart';
import 'api_service.dart';
import 'ogrenci_model.dart';
void main() {
runApp(const OkulApp());
}
class OkulApp extends StatelessWidget {
const OkulApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Okul Yönetim Sistemi',
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.indigo, useMaterial3: true),
home: const OgrenciListeEkrani(),
);
}
}
class OgrenciListeEkrani extends StatefulWidget {
const OgrenciListeEkrani({super.key});
@override
State<OgrenciListeEkrani> createState() => _OgrenciListeEkraniState();
}
class _OgrenciListeEkraniState extends State<OgrenciListeEkrani> {
final ApiService apiService = ApiService();
late Future<List<Ogrenci>> ogrenciListesi;
@override
void initState() {
super.initState();
_listeyiYenile();
}
void _listeyiYenile() {
setState(() {
ogrenciListesi = apiService.fetchOgrenciler();
});
}
// Yeni Öğrenci Ekleme Dialog Penceresi
void _ogrenciEkleDialog() {
final adController = TextEditingController();
final soyadController = TextEditingController();
final numaraController = TextEditingController();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("Yeni Öğrenci Ekle"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(controller: adController, decoration: const InputDecoration(labelText: "Ad")),
TextField(controller: soyadController, decoration: const InputDecoration(labelText: "Soyad")),
TextField(
controller: numaraController,
decoration: const InputDecoration(labelText: "Okul No"),
keyboardType: TextInputType.number,
),
],
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text("İptal")),
ElevatedButton(
onPressed: () async {
if (adController.text.isNotEmpty && numaraController.text.isNotEmpty) {
final yeni = Ogrenci(
ad: adController.text,
soyad: soyadController.text,
numara: int.parse(numaraController.text),
);
bool basarili = await apiService.ogrenciEkle(yeni);
if (mounted) {
Navigator.pop(context);
if (basarili) {
_listeyiYenile();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Öğrenci başarıyla eklendi!")),
);
}
}
}
},
child: const Text("Kaydet"),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Öğrenci Yönetimi"),
actions: [
IconButton(icon: const Icon(Icons.refresh), onPressed: _listeyiYenile),
],
),
body: FutureBuilder<List<Ogrenci>>(
future: ogrenciListesi,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text("Hata: ${snapshot.error}"));
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Center(child: Text("Henüz kayıtlı öğrenci yok."));
}
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final ogrenci = snapshot.data![index];
return Card(
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
leading: CircleAvatar(child: Text(ogrenci.ad[0].toUpperCase())),
title: Text("${ogrenci.ad} ${ogrenci.soyad}"),
subtitle: Text("Numara: ${ogrenci.numara}"),
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _ogrenciEkleDialog,
child: const Icon(Icons.add),
),
);
}
}
🚀 Çalıştırma Planı
Django'yu Başlat: Terminalde
python manage.py runserverkomutunun çalıştığından emin olun.Flutter'ı Başlat:
flutter runkomutuyla uygulamayı ayağa kaldırın.Test Et: "+" butonuna basın, bilgileri girin ve "Kaydet" deyin. Liste anında güncellenecektir.
Kodun Önemli Detayları:
CORS Hatası Hatırlatması: Eğer Pardus üzerinde geliştirme yapıyorsan ve emülatör yerine tarayıcıda (Flutter Web) çalıştırıyorsan, Django tarafındaki
django-cors-headersayarlarının yapıldığından emin olmalısın.utf8.decode:api_service.dartiçinde kullandığımızutf8.decode(response.bodyBytes)kısmı, Türkçe karakterlerin (ş, ğ, ı, İ vb.) düzgün görünmesi için kritiktir.State Management: Bu örnekte basitlik olması açısından
setStateveFutureBuilderkullandık. Profesyonel projelerde bunuProviderveyaBlocile yönetmek daha sürdürülebilir olacaktır.
4. Proje README.md Dosyası
# 🏫 Okul Yönetim Sistemi (Flutter & Django)
Bu proje, bir Computer Science eğitim materyali olarak geliştirilmiştir. Django tabanlı bir API ile Flutter mobil uygulamasının nasıl haberleştiğini gösterir.
## 🚀 Özellikler
- **Django REST Framework** ile tam CRUD desteği.
- **CORS** yapılandırması ile güvenli erişim.
- **Flutter FutureBuilder** ile asenkron veri listeleme.
- **Clean Architecture** prensiplerine uygun katmanlı yapı.
## 🛠 Kurulum
### 1. Backend (Django)
```bash
cd backend
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver
2. Frontend (Flutter)
cd frontend
flutter pub get
flutter run
📝 API Endpoints
GET /api/ogrenciler/- Tüm öğrencileri listeler.POST /api/ogrenciler/- Yeni öğrenci ekler.GET /api/ogrenciler/{id}/- Detay görüntüler.
👨🏫 Eğitmen
Nuri TIRAŞ - Bilişim Teknolojileri Öğretmeni
16. Flutter → Django Veri Akışı
Flutter UI
│
│ http.get()
▼
API Service
│
▼
Internet
│
▼
Django REST API
│
▼
Serializer
│
▼
Database
17. Profesyonel Projelerde Kullanılan Katmanlar
Gerçek projelerde genelde şu mimari kullanılır:
Flutter
│
├── UI
├── State Management
├── Repository
└── API Service
│
▼
Django REST API
│
▼
Database
18. Projeyi Geliştirme Fikirleri
Bu projeye şu özellikleri ekleyebilirsin:
Login sistemi
JWT Authentication
CRUD
öğrenci ekle
öğrenci güncelle
öğrenci sil
Filtreleme
/api/ogrenciler?yas=16
Pagination
/api/ogrenciler?page=2
19. Gerçek Hayat Kullanımı
Flutter + Django kombinasyonu şu projelerde kullanılır:
okul yönetim sistemi
e-ticaret uygulaması
haber uygulaması
sosyal medya uygulaması
görev takip uygulaması
Sonuç
Flutter + Django REST API mimarisi şu avantajları sağlar:
mobil ve web aynı API'yi kullanabilir
backend ve frontend ayrılır
ölçeklenebilir sistem kurulur
"ClientException: Failed to fetch" hatası, Flutter Web projelerinde farklı bir porttaki veya alan adındaki bir sunucuya (backend) istek atıldığında karşılaşılan klasik bir CORS (Cross-Origin Resource Sharing) kısıtlamasıdır.
Tarayıcın (Flutter uygulaman) localhost:37145 portunda çalışırken, veriyi 127.0.0.1:8000 adresinden çekmeye çalışıyor. Tarayıcılar, güvenlik politikaları gereği sunucu tarafından açıkça izin verilmediği sürece bu çapraz port/alan adı isteklerini engeller ve Flutter bunu "Failed to fetch" olarak ekrana yansıtır.
Bu sorunu çözmek için backend tarafında tarayıcıya "Bu isteğe izin veriyorum" demen gerekiyor.
Çözüm Adımları
1. Sunucunun Çalıştığından Emin Ol
Öncelikle arka plandaki API sunucunun (127.0.0.1:8000) gerçekten ayakta ve çalışır durumda olduğunu kontrol et.
2. Backend'de CORS Ayarlarını Yapılandır (Django Örneği)
Eğer backend tarafında Django kullanıyorsan, CORS izinlerini ayarlamak için django-cors-headers paketini kullanabilirsin. Geliştirme ortamında bu sorunu aşmak için şu adımları izleyebilirsin:
Paketi Kur:
Terminalinde backend klasörüne giderek paketi yükle:
Bash:pip install django-cors-headerssettings.pyDosyasını Güncelle:Projendeki
settings.pydosyasını açıp aşağıdaki eklemeleri yap:Python:# INSTALLED_APPS içine ekle INSTALLED_APPS = [ # ... diğer uygulamalar ... 'corsheaders', ] # MIDDLEWARE içine ekle (Mümkün olan en üst sıralara, CommonMiddleware'den önceye koyman önemlidir) MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # ... diğer middleware'ler ... ]İzinleri Tanımla:
Yine
settings.pydosyasının alt kısımlarına geliştirme aşamasında her porttan gelen isteği kabul etmesi için şu satırı ekle:Python:CORS_ALLOW_ALL_ORIGINS = True(Not: Uygulamanı canlıya (production) alırken güvenlik amacıyla
TrueyerineCORS_ALLOWED_ORIGINS = ['http://localhost:37145']şeklinde sadece güvendiğin adresleri tanımlamanı tavsiye ederim. Ancak Flutter Web her çalıştığında port değişebildiği için geliştirme sırasındaTrueyapmak en pratik yoldur.)
Bu ayarları kaydedip Django sunucunu (python manage.py runserver) yeniden başlattıktan sonra, Flutter uygulamanın sayfayı yenilediğinde (Refresh) öğrencilerin listesini sorunsuz bir şekilde çekebildiğini göreceksin.


Yorumlar
Yorum Gönder