Django'nun bir çok sorunu kolayca halletmemizi sağlayan/yardımcı olan bir çok modülü var. Kolayca RESTful API'ler inşa etmek isteyenler için de "Django REST Framework" bunlardan biri. Başlamadan önce, RESTful API hakkında bilgisi olmayan, meraktan gelenler buraya tıklayarak ön bilgi edinebilirler.

Pitaya

Kurulum

Django'nun hali hazırda kurulu olduğunu ve virtual environment içinde olduğunuzu varsayıyorum. Hemen pip3 ile yükleyelim.

pip3 install djangorestframework

Başlatılmış bir proje ve uygulama yoksa, aşağıdaki gibi oluşturabilirsiniz. Eğer hali hazırda varolan bir proje içinde kullanmak istiyorsanız o da mümkün. Şuan çalıştığınız uygulama içinde de kullanabilirsiniz tabii, ancak API için ayrı bir uygulama başlatmanızı öneririm.

django-admin startproject rest_apisi_olan_proje
cd rest_apisi_olan_proje
python manage.py startapp api

Oluşturduğunuz uygulamayı proje içinde kullanabilmek için settings.py içinde tanımlı olması gerekiyor.

INSTALLED_APPS = (
    # ...
    "rest_framework",
    "api")
Ayrı bir uygulama oluşturmamış olasanız bile rest_framework kısmını eklemelisiniz. Son olarak değişiklikleri veritabanına uygulayalım.
python3 manage.py makemigrations
python3 manage.py migrate

Kullanım

Şimdi, az önce oluşturduğunuz api uygulaması içinde serializers.py dosyası oluşturup, içinde API ile erişim sağlanacak modelleri serialize edecek nesneleri hazırlayın. Bu nesneler ModelSerializer sınıfından miras almak zorundalar ve meta sınıf içinde model ismi ile serileştirilecek alanların tanımlı olması zorunlu.

from app.models import Article

from rest_framework import serializers

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ("id", "title", "body")

Şimdi yine api uygulaması içindeki views.py dosyasına, aşağıdaki gibi API ile yapılabilmesini istediğimiz fonksiyonları ekleyelim.

from django.http import Http404

from app.models import Article
from api.serializers import ArticleSerializer

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class ArticleList(APIView):
    """
    Kayıtlı makaleleri listele ya da yenisini ekle.
    """
    def get(self, request, format=None):
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class ArticleSingle(APIView):
    """
    Tekil makale getir, düzenle ya da sil.
    """
    def get_object(self, pk):
        try:
            return Article.objects.get(pk=pk)
        except Article.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        article = self.get_object(pk)
        article = ArticleSerializer(article)
        return Response(article.data)

    def put(self, request, pk, format=None):
        article = self.get_object(pk)
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        article = self.get_object(pk)
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Burada APIView sınıflarının içlerinde bulunan fonksiyonlar request tiplerini gösteriyor. Daha önce karşılaşmayanlar için en çok kullanılan dört request tipi; GET listeleme, POST ekleme, PUT düzenleme ve DELETE silme isteği olduğunu belirtiyor. Tümünü incelemek isterseniz; Request methods.

Şimdi hazırladığınız APIView'lar için URL belirleme zamanı. Ben /api/articles veya /api/articles/1 gibi erişilmesini istediğim için aşağıdaki gibi yaptım. Siz dilediğiniz ismi verebilirsiniz, APIView sınıflarına verdiğiniz ismin bir önemi yok. Oluşturduğumuz api modülüne urls.py dosyasını ekleyip kaydedelim.

from django.conf.urls import url

from api import views

urlpatterns = [
    url(r"articles$", views.ArticleList.as_view(),
        name="api-article-list"),
    url(r"articles/(?P<pk>[0-9]+)$", views.ArticleSingle.as_view(),
        name="api-article-single"),
]

Tabi yukarıda adresler için /api ön eki yok. Onu, bu adresleri projenin adreslerine dahil ederken yapacağız. Üzerinde çalıştığınız projenin (ana projenin/uygulamanın) içindeki urls.py dosyasına girin ve şuna benzer bir şekilde yukarıda tanımladığımız adresleri dahil edin.

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r"^admin/", admin.site.urls),
    url(r"^api/", include("api.urls")),
]

Buraya kadar bir problem yoksa her şey tamam demektir. Her zamanki gibi debug serverını çalıştırıp sonucu gözlelerinizle görebilirsiniz.

python3 manage.py runserver

Yayınladığınız API'ın adresi http://127.0.0.1:8000/api/articles ya da buna benzer bir şey olacaktır.

Belirlediğiniz API adreslerine tarayıcıdan girdiğinizde hem o API View hakkında bilgi bulabilecek hem de altta test etmek için bir bölüm göreceksiniz. Bunun haricinde RESTClient gibi eklentilerle de test edebilirsiniz.

Birim Testi

Şimdi, API çalışıyor mu diye unit test ile teyit edelim. Önce, test için kullanılmak üzere uygulama içine bir kaç tane deneme kaydı ekleyin -ki buradaki örnekte Article- ve aşağıdaki komut ile bu kayıtlar için fixture oluşturun.

python3 manage.py dumpdata app --format=json --indent=4 > app/fixtures/articles.json

Tabii bu komuttaki app olan kısımları kendi uygulama isminiz ile değiştirmeyi unutmayın.

Ardından, oluşturduğunuz api uygulaması içine tests.py dosyası oluşturup içine aşağıdaki gibi ya da buna benzer bir test yazın. Konumuz birim testi değil ancak testlerin django-rest-framework ile beraber kullanımından bahsetmek istedim.

from django.test import TestCase
from django.core.urlresolvers import reverse

from rest_framework import status
from rest_framework.test import APIClient

from app.models import Article


class ArticleTestCase(TestCase):
    fixtures = ["articles.json"]

    def setUp(self):
        self.client = APIClient()

    def test_article_list_get(self):
        response = self.client.get(reverse("api-articles-list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_article_list_post(self):
        data = {
            "title": "Bu bir test başlığıdır.",
            "message": "Bu bir test içeriğidir.",
        }
        response = self.client.post(reverse("api-articles-list"),
                                    data,
                                    format="json")
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    def test_article_single_get(self):
        article = Article.objects.first()
        response = self.client.get(reverse("api-article-single",
                                           kwargs={"pk": article.pk}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_article_single_delete(self):
        article = Article.objects.first()
        response = self.client.delete(reverse("api-article-single",
                                      kwargs={"pk": article.pk}))

Son olarak testi başlatıyoruz.

python3 manage.py test api

Eğer aşağıdaki gibi bir ekranla karşılaşırsanız her şey yolunda demektir. Aksi halde karşılaşılan hataları görebilirsiniz.

Django REST Framework Tests

Merak ettiğiniz bir şey olursa yorumlar bölümünden sormaktan çekinmeyin. :)
Kolay gelsin...