Uçtan Uca Zekat Hesaplama ve Takip Sistemi: Django ve Flutter Entegrasyonu

Hem web hem de mobil platformların birbiriyle nasıl konuştuğunu, veri tabanı işlemlerinin (CRUD) uçtan uca nasıl yönetildiğini özellikle bir bilgisayar laboratuvarı ortamında öğrencilere veya diğer geliştiricilere göstermek için kusursuz bir mimari.

Django'nun güçlü arka planı ve Flutter'ın esnek ön yüzü birleştiğinde ortaya çok verimli bir ekosistem çıkıyor. Bu eğitim makalesinde, temel %2.5 (1/40) zekat oranını baz alarak, hem web arayüzünden hem de mobil uygulamadan yönetilebilen bir "Zekat Hesaplama ve Takip Sistemi" kuracağız.


Kullanıcıların varlıklarını girip zekat miktarlarını hesaplayabildikleri, bu kayıtları listeleyip, güncelleyip, silebildikleri (CRUD) tam teşekküllü bir sistem inşa edeceğiz.


Bölüm 1: Django ile Arka Plan (Backend) ve Web Arayüzü

Django, hem standart web sayfalarını (HTML/CSS) sunmak hem de Flutter'ın haberleşeceği RESTful API'yi oluşturmak için merkez üssümüz olacak.

1. Kurulum ve Model Tasarımı

Öncelikle projemizi ve uygulamamızı oluşturalım. REST API oluşturmak için djangorestframework paketine ihtiyacımız var.

Bash:
pip install django djangorestframework django-cors-headers
django-admin startproject zekat_projesi
cd zekat_projesi
python manage.py startapp hesaplayici

hesaplayici/models.py dosyasında veri tabanı modelimizi oluşturalım. Hesaplama mantığını modelin save metoduna ekleyerek işi otomatize edebiliriz:

Python:
from django.db import models

class ZekatKaydi(models.Model):
    kullanici_adi = models.CharField(max_length=100, verbose_name="İsim Soyisim")
    toplam_varlik = models.DecimalField(max_digits=15, decimal_places=2, verbose_name="Toplam Varlık (TL)")
    hesaplanan_zekat = models.DecimalField(max_digits=15, decimal_places=2, blank=True, null=True)
    kayit_tarihi = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        # 40'ta 1 (%2.5) oranında zekat hesaplama mantığı
        if self.toplam_varlik:
            self.hesaplanan_zekat = float(self.toplam_varlik) * 0.025
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.kullanici_adi} - {self.hesaplanan_zekat} TL"

2. Django Web Arayüzü (Web CRUD)

Web tarafı için standart Django formları ve görünümleri (Class-Based Views) kullanabilirsiniz.

hesaplayici/views.py içerisine standart web görünümlerini ekleyelim:

Python:
from django.urls import reverse_lazy
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from .models import ZekatKaydi

class ZekatListesi(ListView):
    model = ZekatKaydi
    template_name = 'hesaplayici/liste.html' # Bootstrap ile süsleyebilirsiniz

class ZekatEkle(CreateView):
    model = ZekatKaydi
    fields = ['kullanici_adi', 'toplam_varlik']
    success_url = reverse_lazy('zekat_listesi')

# UpdateView ve DeleteView da benzer şekilde tanımlanır.

3. Flutter İçin REST API (API CRUD)

Flutter uygulamasının veri okuyup yazabilmesi için verilerimizi JSON formatına çevirmemiz gerekiyor.

hesaplayici/serializers.py dosyasını oluşturun:

Python:
from rest_framework import serializers
from .models import ZekatKaydi

class ZekatKaydiSerializer(serializers.ModelSerializer):
    class Meta:
        model = ZekatKaydi
        fields = '__all__'

Şimdi API uç noktalarını (Endpoints) hesaplayici/api_views.py içinde oluşturalım:

Python:
from rest_framework import viewsets
from .models import ZekatKaydi
from .serializers import ZekatKaydiSerializer

class ZekatViewSet(viewsets.ModelViewSet):
    queryset = ZekatKaydi.objects.all().order_by('-kayit_tarihi')
    serializer_class = ZekatKaydiSerializer

Projenin urls.py dosyasında router ayarlarını yaparak API'yi dışa açın:

Python:
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from hesaplayici.api_views import ZekatViewSet

router = DefaultRouter()
router.register(r'zekat', ZekatViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)), # Flutter buradan bağlanacak
    # Web View URL'leri buraya eklenebilir
]

Not: Local ağda (örneğin 15 bilgisayarlık bir laboratuvar ağında) test yaparken Django'yu python manage.py runserver 0.0.0.0:8000 komutuyla başlatmayı ve settings.py içinde ALLOWED_HOSTS = ['*'] ile CORS_ALLOW_ALL_ORIGINS = True ayarlarını yapmayı unutmayın.


Bölüm 2: Flutter ile Mobil Arayüz

Mobil tarafta, oluşturduğumuz bu API'ye HTTP istekleri (GET, POST, PUT, DELETE) atarak CRUD işlemlerini gerçekleştireceğiz.

1. Kurulum ve Model

pubspec.yaml dosyasına http paketini ekleyin:

YAML:
dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0

Gelen JSON verisini karşılayacak Dart modelimizi oluşturalım (zekat_model.dart):

Dart:
class ZekatKaydi {
  final int? id;
  final String kullaniciAdi;
  final double toplamVarlik;
  final double? hesaplananZekat;

  ZekatKaydi({this.id, required this.kullaniciAdi, required this.toplamVarlik, this.hesaplananZekat});

  factory ZekatKaydi.fromJson(Map<String, dynamic> json) {
    return ZekatKaydi(
      id: json['id'],
      kullaniciAdi: json['kullanici_adi'],
      toplamVarlik: double.parse(json['toplam_varlik'].toString()),
      hesaplananZekat: json['hesaplanan_zekat'] != null ? double.parse(json['hesaplanan_zekat'].toString()) : null,
    );
  }
}

2. API Servis Sınıfı (CRUD İşlemleri)

Django sunucumuzla konuşacak olan ağ katmanını yazalım (api_service.dart). Sunucu IP adresini, Django'yu çalıştırdığınız makinenin yerel IP'si (örn: 192.168.1.50) olarak ayarlamalısınız.

Dart:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'zekat_model.dart';

class ApiService {
  // Makinenizin veya sunucunuzun IP adresi
  static const String baseUrl = 'http://192.168.1.X:8000/api/zekat/'; 

  // READ (Listeleme)
  Future<List<ZekatKaydi>> getKayitlar() async {
    final response = await http.get(Uri.parse(baseUrl));
    if (response.statusCode == 200) {
      List jsonResponse = json.decode(utf8.decode(response.bodyBytes));
      return jsonResponse.map((data) => ZekatKaydi.fromJson(data)).toList();
    } else {
      throw Exception('Veriler yüklenemedi');
    }
  }

  // CREATE (Ekleme)
  Future<void> addKayit(String isim, double varlik) async {
    await http.post(
      Uri.parse(baseUrl),
      headers: <String, String>{'Content-Type': 'application/json; charset=UTF-8'},
      body: jsonEncode(<String, dynamic>{
        'kullanici_adi': isim,
        'toplam_varlik': varlik,
      }),
    );
  }

  // DELETE (Silme)
  Future<void> deleteKayit(int id) async {
    await http.delete(Uri.parse('$baseUrl$id/'));
  }
}

3. Kullanıcı Arayüzü (UI)

Uygulamanın ana ekranında kayıtları listeleyebilir ve yeni kayıt eklemek için bir Floating Action Button kullanabiliriz. (Kodu sade tutmak adına temel liste yapısı aşağıdadır):

Dart:
import 'package:flutter/material.dart';
import 'api_service.dart';
import 'zekat_model.dart';

class AnaEkran extends StatefulWidget {
  @override
  _AnaEkranState createState() => _AnaEkranState();
}

class _AnaEkranState extends State<AnaEkran> {
  final ApiService api = ApiService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Zekat Takip Sistemi')),
      body: FutureBuilder<List<ZekatKaydi>>(
        future: api.getKayitlar(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
              itemCount: snapshot.data!.length,
              itemBuilder: (context, index) {
                var kayit = snapshot.data![index];
                return ListTile(
                  title: Text(kayit.kullaniciAdi),
                  subtitle: Text('Varlık: ${kayit.toplamVarlik} ₺ - Zekat: ${kayit.hesaplananZekat} ₺'),
                  trailing: IconButton(
                    icon: Icon(Icons.delete, color: Colors.red),
                    onPressed: () async {
                      await api.deleteKayit(kayit.id!);
                      setState(() {}); // Ekranı yenile
                    },
                  ),
                );
              },
            );
          } else if (snapshot.hasError) {
            return Center(child: Text("${snapshot.error}"));
          }
          return Center(child: CircularProgressIndicator());
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Burada AlertDialog veya yeni sayfa açılarak API.addKayit çağrılır
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

Sonuç

Bu mimariyle, veri bütünlüğünü Django'nun güvenli kollarında tutarken, Flutter ile akıcı bir mobil deneyim sağladık. Zekat miktarının backend (models.py) tarafında hesaplanması, hem web arayüzünden (Django Views) hem de mobil uygulamadan (Flutter HTTP POST) girilen verilerin her zaman aynı iş kurallarına tabi tutulmasını garanti eder. Bu yaklaşım, modern sistem tasarımının temel taşlarındandır ve öğrencilere web-mobil entegrasyonunu anlatmak için harika bir örnektir.

Projeyi doğrudan ayağa kaldırıp laboratuvarda veya kendi bilgisayarında test edebilmen için gereken dosyaların tam ve çalışır haldeki kodları:


Bölüm 1: Django (Backend & Web) Tam Kodları

Terminalde django-admin startproject zekat_projesi ve python manage.py startapp hesaplayici komutlarını çalıştırdıktan sonra dosyaları aşağıdaki gibi düzenleyebilirsin.

1. hesaplayici/models.py (Veri Tabanı ve Hesaplama Mantığı)

Python:
from django.db import models

class ZekatKaydi(models.Model):
    kullanici_adi = models.CharField(max_length=100, verbose_name="İsim Soyisim")
    toplam_varlik = models.DecimalField(max_digits=15, decimal_places=2, verbose_name="Toplam Varlık (TL)")
    hesaplanan_zekat = models.DecimalField(max_digits=15, decimal_places=2, blank=True, null=True, verbose_name="Hesaplanan Zekat")
    kayit_tarihi = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        # Toplam varlığın 1/40'ı (%2.5) hesaplanır
        if self.toplam_varlik:
            self.hesaplanan_zekat = float(self.toplam_varlik) * 0.025
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.kullanici_adi} - {self.hesaplanan_zekat} TL"

2. hesaplayici/serializers.py (JSON Dönüştürücü)

Python:
from rest_framework import serializers
from .models import ZekatKaydi

class ZekatKaydiSerializer(serializers.ModelSerializer):
    class Meta:
        model = ZekatKaydi
        fields = ['id', 'kullanici_adi', 'toplam_varlik', 'hesaplanan_zekat', 'kayit_tarihi']

3. hesaplayici/api_views.py (Flutter için REST API Görünümleri)

Python:
from rest_framework import viewsets
from .models import ZekatKaydi
from .serializers import ZekatKaydiSerializer

class ZekatViewSet(viewsets.ModelViewSet):
    queryset = ZekatKaydi.objects.all().order_by('-kayit_tarihi')
    serializer_class = ZekatKaydiSerializer

4. hesaplayici/views.py (Web Arayüzü için Görünümler)

Python:
from django.urls import reverse_lazy
from django.views.generic import ListView, CreateView, DeleteView
from .models import ZekatKaydi

class ZekatListesi(ListView):
    model = ZekatKaydi
    template_name = 'hesaplayici/liste.html'
    context_object_name = 'kayitlar'
    ordering = ['-kayit_tarihi']

class ZekatEkle(CreateView):
    model = ZekatKaydi
    fields = ['kullanici_adi', 'toplam_varlik']
    template_name = 'hesaplayici/form.html'
    success_url = reverse_lazy('zekat_listesi')

class ZekatSil(DeleteView):
    model = ZekatKaydi
    template_name = 'hesaplayici/sil_onay.html'
    success_url = reverse_lazy('zekat_listesi')

5. zekat_projesi/urls.py (Ana URL Yönlendirmeleri)

Python:
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from hesaplayici.api_views import ZekatViewSet
from hesaplayici import views

# API Router ayarları
router = DefaultRouter()
router.register(r'zekat', ZekatViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
    # Flutter'ın istek atacağı API endpoint'i:
    path('api/', include(router.urls)), 
    
    # Web arayüzü URL'leri:
    path('', views.ZekatListesi.as_view(), name='zekat_listesi'),
    path('ekle/', views.ZekatEkle.as_view(), name='zekat_ekle'),
    path('sil/<int:pk>/', views.ZekatSil.as_view(), name='zekat_sil'),
]

(Not: settings.py içinde INSTALLED_APPS listesine 'rest_framework', 'corsheaders' ve 'hesaplayici' uygulamalarını eklemeyi ve CORS ayarlarını yapmayı unutma.)


Bölüm 2: Flutter (Mobil Uygulama) Tam Kodları

Aşağıdaki kodları Flutter projenin lib klasörü altında ilgili dosyaları oluşturarak yapıştırabilirsin.

1. lib/zekat_model.dart (Veri Modeli)

Dart:
class ZekatKaydi {
  final int? id;
  final String kullaniciAdi;
  final double toplamVarlik;
  final double? hesaplananZekat;

  ZekatKaydi({
    this.id,
    required this.kullaniciAdi,
    required this.toplamVarlik,
    this.hesaplananZekat,
  });

  factory ZekatKaydi.fromJson(Map<String, dynamic> json) {
    return ZekatKaydi(
      id: json['id'],
      kullaniciAdi: json['kullanici_adi'],
      toplamVarlik: double.parse(json['toplam_varlik'].toString()),
      hesaplananZekat: json['hesaplanan_zekat'] != null
          ? double.parse(json['hesaplanan_zekat'].toString())
          : null,
    );
  }
}

2. lib/api_service.dart (Django ile Haberleşme Servisi)

Dart:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'zekat_model.dart';

class ApiService {
  // Django sunucusunun çalıştığı makinenin IP adresi (Pardus makinenin IP'si vb.)
  // Emülatör kullanıyorsan 10.0.2.2 olabilir. Gerçek cihazda aynı Wi-Fi'daki yerel IP'yi yazmalısın.
  static const String baseUrl = 'http://192.168.1.100:8000/api/zekat/'; 

  Future<List<ZekatKaydi>> getKayitlar() async {
    final response = await http.get(Uri.parse(baseUrl));
    if (response.statusCode == 200) {
      // Türkçe karakter sorunu yaşamamak için utf8.decode kullanıyoruz
      List jsonResponse = json.decode(utf8.decode(response.bodyBytes));
      return jsonResponse.map((data) => ZekatKaydi.fromJson(data)).toList();
    } else {
      throw Exception('Kayıtlar yüklenemedi: ${response.statusCode}');
    }
  }

  Future<void> addKayit(String isim, double varlik) async {
    final response = await http.post(
      Uri.parse(baseUrl),
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      },
      body: jsonEncode(<String, dynamic>{
        'kullanici_adi': isim,
        'toplam_varlik': varlik,
      }),
    );

    if (response.statusCode != 201) {
      throw Exception('Kayıt eklenemedi');
    }
  }

  Future<void> deleteKayit(int id) async {
    final response = await http.delete(Uri.parse('$baseUrl$id/'));
    if (response.statusCode != 204) {
      throw Exception('Kayıt silinemedi');
    }
  }
}

3. lib/main.dart (Tam ve Çalışır Kullanıcı Arayüzü)

Dart:
import 'package:flutter/material.dart';
import 'api_service.dart';
import 'zekat_model.dart';

void main() {
  runApp(const ZekatUygulamasi());
}

class ZekatUygulamasi extends StatelessWidget {
  const ZekatUygulamasi({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Zekat Hesaplayıcı',
      theme: ThemeData(primarySwatch: Colors.green),
      home: const AnaEkran(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class AnaEkran extends StatefulWidget {
  const AnaEkran({Key? key}) : super(key: key);

  @override
  _AnaEkranState createState() => _AnaEkranState();
}

class _AnaEkranState extends State<AnaEkran> {
  final ApiService apiService = ApiService();
  late Future<List<ZekatKaydi>> kayitlar;

  @override
  void initState() {
    super.initState();
    verileriYenile();
  }

  void verileriYenile() {
    setState(() {
      kayitlar = apiService.getKayitlar();
    });
  }

  // Yeni Kayıt Ekleme Penceresi (Dialog)
  void _kayitEkleDialogGoster() {
    final isimController = TextEditingController();
    final varlikController = TextEditingController();

    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('Yeni Zekat Kaydı Ekle'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: isimController,
                decoration: const InputDecoration(labelText: 'İsim Soyisim'),
              ),
              TextField(
                controller: varlikController,
                keyboardType: TextInputType.number,
                decoration: const InputDecoration(labelText: 'Toplam Varlık (TL)'),
              ),
            ],
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('İptal'),
            ),
            ElevatedButton(
              onPressed: () async {
                if (isimController.text.isNotEmpty && varlikController.text.isNotEmpty) {
                  double varlik = double.parse(varlikController.text);
                  await apiService.addKayit(isimController.text, varlik);
                  Navigator.pop(context);
                  verileriYenile(); // Listeyi güncelle
                }
              },
              child: const Text('Kaydet'),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Zekat Kayıtları (Flutter & Django)'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: verileriYenile,
          )
        ],
      ),
      body: FutureBuilder<List<ZekatKaydi>>(
        future: kayitlar,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('Hata oluştu: ${snapshot.error}'));
          } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
            return const Center(child: Text('Henüz kayıt bulunmuyor.'));
          }

          return ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              final kayit = snapshot.data![index];
              return Card(
                margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
                child: ListTile(
                  title: Text(
                    kayit.kullaniciAdi,
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                  subtitle: Text(
                    'Varlık: ${kayit.toplamVarlik.toStringAsFixed(2)} ₺\nZekat: ${kayit.hesaplananZekat?.toStringAsFixed(2)} ₺',
                  ),
                  isThreeLine: true,
                  trailing: IconButton(
                    icon: const Icon(Icons.delete, color: Colors.red),
                    onPressed: () async {
                      await apiService.deleteKayit(kayit.id!);
                      verileriYenile();
                    },
                  ),
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _kayitEkleDialogGoster,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Django'da views.py dosyasında çağırdığımız (liste.html, form.html, sil_onay.html) HTML şablon dosyalarını vermeyi atlamışım. 

Bu dosyaları Django projenin içinde, hesaplayici uygulamasının altında şu klasör yapısını oluşturarak içine kaydetmelisin:

hesaplayici/templates/hesaplayici/

Tasarımların düzgün görünmesi için temel bir Bootstrap 5 yapısı kullandım.

1. base.html (Ana Şablon)

Tüm sayfalarda tekrar eden menü ve kütüphane kodlarını tek bir yerde tutmak için bir ana şablon oluşturalım.

Dosya Yolu: hesaplayici/templates/hesaplayici/base.html

HTML:
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Zekat Takip Sistemi</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">

<nav class="navbar navbar-expand-lg navbar-dark bg-success mb-4">
    <div class="container">
        <a class="navbar-brand" href="{% url 'zekat_listesi' %}">Zekat Takip (Django Web)</a>
    </div>
</nav>

<div class="container">
    {% block content %}
    {% endblock %}
</div>

</body>
</html>

2. liste.html (Kayıtların Listelendiği Ana Sayfa)

Bu sayfa veri tabanındaki zekat kayıtlarını bir tablo halinde gösterir.

Dosya Yolu: hesaplayici/templates/hesaplayici/liste.html

HTML:
{% extends 'hesaplayici/base.html' %}

{% block content %}
<div class="card shadow-sm">
    <div class="card-header bg-white d-flex justify-content-between align-items-center">
        <h4 class="mb-0">Zekat Kayıtları</h4>
        <a href="{% url 'zekat_ekle' %}" class="btn btn-success">Yeni Kayıt Ekle</a>
    </div>
    <div class="card-body">
        {% if kayitlar %}
        <table class="table table-striped table-hover">
            <thead>
                <tr>
                    <th>İsim Soyisim</th>
                    <th>Toplam Varlık (TL)</th>
                    <th>Hesaplanan Zekat (TL)</th>
                    <th>Tarih</th>
                    <th>İşlemler</th>
                </tr>
            </thead>
            <tbody>
                {% for kayit in kayitlar %}
                <tr>
                    <td>{{ kayit.kullanici_adi }}</td>
                    <td>{{ kayit.toplam_varlik }} ₺</td>
                    <td><strong>{{ kayit.hesaplanan_zekat }} ₺</strong></td>
                    <td>{{ kayit.kayit_tarihi|date:"d.m.Y H:i" }}</td>
                    <td>
                        <a href="{% url 'zekat_sil' kayit.pk %}" class="btn btn-sm btn-danger">Sil</a>
                    </td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
        {% else %}
        <div class="alert alert-info">Henüz sisteme eklenmiş bir kayıt bulunmuyor.</div>
        {% endif %}
    </div>
</div>
{% endblock %}

3. form.html (Yeni Kayıt Ekleme Sayfası)

Kullanıcıdan isim ve toplam varlık bilgisini aldığımız form sayfasıdır.

Dosya Yolu: hesaplayici/templates/hesaplayici/form.html

HTML:
{% extends 'hesaplayici/base.html' %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card shadow-sm">
            <div class="card-header bg-white">
                <h4 class="mb-0">Yeni Zekat Kaydı Ekle</h4>
            </div>
            <div class="card-body">
                <form method="POST">
                    {% csrf_token %}
                    {{ form.as_p }}
                    
                    <div class="d-grid gap-2 mt-4">
                        <button type="submit" class="btn btn-success">Kaydet ve Hesapla</button>
                        <a href="{% url 'zekat_listesi' %}" class="btn btn-secondary">İptal</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

4. sil_onay.html (Kayıt Silme Onay Sayfası)

Yanlışlıkla silmeleri önlemek için kullanıcıya "Emin misiniz?" diye sorduğumuz sayfa.

Dosya Yolu: hesaplayici/templates/hesaplayici/sil_onay.html

HTML:
{% extends 'hesaplayici/base.html' %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card shadow-sm border-danger">
            <div class="card-header bg-danger text-white">
                <h4 class="mb-0">Kayıt Silme Onayı</h4>
            </div>
            <div class="card-body text-center">
                <p class="lead"><strong>{{ object.kullanici_adi }}</strong> isimli kişiye ait zekat kaydını silmek istediğinize emin misiniz?</p>
                <p class="text-muted">Bu işlem geri alınamaz.</p>
                
                <form method="POST">
                    {% csrf_token %}
                    <button type="submit" class="btn btn-danger px-4">Evet, Sil</button>
                    <a href="{% url 'zekat_listesi' %}" class="btn btn-secondary px-4">İptal</a>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

Bu dosyaları ekledikten sonra, web tarayıcından http://127.0.0.1:8000/ adresine (veya belirlediğin IP'ye) girdiğinde güzel görünümlü bir web arayüzü ile karşılaşacaksın. Buradan eklediğin veriler anında Flutter mobil uygulamana da yansıyacaktır. 

zekat_projesi/settings.py dosyasını açıp aşağıdaki ilgili bölümleri güncelleyebilirsin:

1. INSTALLED_APPS (Kurulu Uygulamalar)

Kendi oluşturduğumuz hesaplayici uygulamasını, rest_framework'ü ve dışarıdan gelen API isteklerini kabul etmek için corsheaders'ı bu listeye eklemelisin.

Python:
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Kendi uygulamamız ve kurduğumuz kütüphaneler:
    'hesaplayici',
    'rest_framework',
    'corsheaders',
]

2. MIDDLEWARE (Ara Katmanlar)

CORS middleware'ini listeye eklemen gerekiyor. Bunun CommonMiddleware'in üzerinde olması son derece önemlidir.

Python:
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    
    # CORS Middleware buraya eklenmeli:
    'corsheaders.middleware.CorsMiddleware', 
    
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

3. ALLOWED_HOSTS ve CORS İzinleri

Geliştirme aşamasında API'nin her IP'den (özellikle telefonundan veya emülatörden) gelen isteklere yanıt verebilmesi için ALLOWED_HOSTS ayarını güncellemelisin. Ayrıca dosyanın en altına CORS izin kodunu eklemelisin.

Python:
# Dosyanın üst kısımlarındaki bu ayarı bulup '*' yapın:
ALLOWED_HOSTS = ['*']


# ----- DOSYANIN EN ALTINA EKLEYEBİLİRSİNİZ -----

# REST Framework varsayılan ayarları (İsteğe bağlı ama iyi bir standarttır)
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}

# Flutter mobil uygulamasından gelecek isteklere izin verir
CORS_ALLOW_ALL_ORIGINS = True 

Bu ayarları kaydettikten sonra sırasıyla şu komutları çalıştırarak projeyi hazır hale getirebilirsin:

  1. python manage.py makemigrations

  2. python manage.py migrate

  3. python manage.py runserver 0.0.0.0:8000 (Bu şekilde başlatırsan yerel ağındaki diğer cihazlar da bu sunucuya bağlanabilir).

Yorumlar

Bu blogdaki popüler yayınlar

Dart Uygulama Sınavı: Pardus ETAP 23 Kurulum Otomasyonu

Dart Programlama Dil Uygulama Sınavı Çalışma Soruları

Pardus Üzerinde Flutter Geliştirme Ortamı Kurulumu