دليل شامل لإطار Django REST مع أمثلة

عالم تطوير واجهات برمجة التطبيقات (APIs) أصبح أكثر أهمية من أي وقت مضى، حيث تعد APIs الجسر الأساسي بين التطبيقات المختلفة. ومع ظهور أدوات متقدمة مثل Django REST Framework (DRF)، أصبح بناء واجهات برمجة قوية ومرنة أسهل وأكثر كفاءة. إذا كنت مطورًا يعمل على إنشاء RESTful APIs باستخدام Django، فإن فهم الفئات المختلفة التي يقدمها DRF، مثل APIView وViewSets، يعتبر خطوة أساسية نحو إنشاء حلول فعالة ومتقنة. في هذا المقال، سنستعرض تفاصيل هذه الفئات، ميزاتها، وأمثلة عملية لاستخدامها، لنساعدك على اختيار الأنسب لمشروعك.

Generic Views:

Django REST framework يوفر مجموعة من الـ Generic Views التي تسهّل إنشاء واجهات برمجية API باستخدام أقل قدر من الكود. إليك شرحًا باللغة العربية لكل منها:

1. CreateAPIView

  • تُستخدم لإنشاء كائن جديد في قاعدة البيانات.
  • تتيح فقط طريقة الطلب HTTP POST.

مثال:

from rest_framework.generics import CreateAPIView
from .models import MyModel
from .serializers import MyModelSerializer

class MyModelCreateView(CreateAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

2. DestroyAPIView

  • تُستخدم لحذف كائن معين.
  • تتيح فقط طريقة الطلب HTTP DELETE.

مثال:

from rest_framework.generics import DestroyAPIView
from .models import MyModel

class MyModelDestroyView(DestroyAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

3. GenericAPIView

  • تعتبر كصفّ أساسي يمكن توسيعه لإضافة سلوك مخصص.
  • توفر الوظائف الأساسية مثل التعامل مع querysets و serializers.
  • تُستخدم عادة كأساس للـ Generic Views الأخرى.

مثال:

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .models import MyModel
from .serializers import MyModelSerializer

class MyModelCustomView(GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def get(self, request, *args, **kwargs):
        obj = self.get_object()
        serializer = self.get_serializer(obj)
        return Response(serializer.data)

4. ListAPIView

  • تُستخدم لعرض قائمة من الكائنات.
  • تتيح طريقة الطلب HTTP GET.

مثال:

from rest_framework.generics import ListAPIView
from .models import MyModel

class MyModelListView(ListAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

5. ListCreateAPIView

  • تُستخدم لعرض قائمة الكائنات وأيضًا لإنشاء كائن جديد.
  • تتيح طريقتي الطلب HTTP GET و POST.

مثال:

from rest_framework.generics import ListCreateAPIView

class MyModelListCreateView(ListCreateAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

6. RetrieveAPIView

  • تُستخدم لاسترجاع كائن واحد فقط.
  • تتيح طريقة الطلب HTTP GET.

مثال:

from rest_framework.generics import RetrieveAPIView

class MyModelRetrieveView(RetrieveAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

7. RetrieveDestroyAPIView

  • تُستخدم لاسترجاع وحذف كائن واحد.
  • تتيح طريقتي الطلب HTTP GET و DELETE.

مثال:

from rest_framework.generics import RetrieveDestroyAPIView

class MyModelRetrieveDestroyView(RetrieveDestroyAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

8. RetrieveUpdateAPIView

  • تُستخدم لاسترجاع وتحديث كائن واحد.
  • تتيح طريقتي الطلب HTTP GET و PUT/PATCH.

مثال:

from rest_framework.generics import RetrieveUpdateAPIView

class MyModelRetrieveUpdateView(RetrieveUpdateAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

9. RetrieveUpdateDestroyAPIView

  • تُستخدم لاسترجاع، تحديث، وحذف كائن واحد.
  • تتيح طريقت الطلب HTTP GET، وPUT/PATCH، وDELETE.

مثال:

from rest_framework.generics import RetrieveUpdateDestroyAPIView

class MyModelRetrieveUpdateDestroyView(RetrieveUpdateDestroyAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

10. UpdateAPIView

  • تُستخدم لتحديث كائن معين.
  • تتيح طريقتي الطلب HTTP PUT و PATCH.

مثال:

from rest_framework.generics import UpdateAPIView

class MyModelUpdateView(UpdateAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

مزايا Generic Views

  • توفر وقتًا كبيرًا أثناء كتابة الكود.
  • تُستخدم لإنشاء واجهات برمجية قياسية (CRUD APIs) بسهولة.
  • تدعم التخصيص إذا كنت بحاجة إلى وظائف إضافية.

يمكنك اختيار الـ Generic View المناسبة بناءً على الاحتياجات الخاصة بـ API الذي تبنيه.

Mixins:

Django REST Framework توفر mixins وهي مكونات قابلة لإعادة الاستخدام تضيف وظائف محددة لتنفيذ عمليات CRUD. يتم استخدام هذه الميكسينات عادة مع GenericAPIView لإنشاء عروض (Views) مخصصة تجمع بين سلوكيات معينة. إليك شرحًا للميكسينات التي ذكرتها:

1. CreateModelMixin

  • تضيف وظيفة إنشاء كائن جديد باستخدام طريقة الطلب HTTP POST.
  • تُستخدم عادةً مع GenericAPIView.

مثال:

from rest_framework.mixins import CreateModelMixin
from rest_framework.generics import GenericAPIView
from .models import MyModel
from .serializers import MyModelSerializer

class MyModelCreateView(CreateModelMixin, GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

2. DestroyModelMixin

  • تضيف وظيفة حذف الكائن باستخدام طريقة الطلب HTTP DELETE.
  • تُستخدم عادةً مع GenericAPIView.

مثال:

from rest_framework.mixins import DestroyModelMixin

class MyModelDestroyView(DestroyModelMixin, GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

3. ListModelMixin

  • توفر وظيفة عرض قائمة بالكائنات باستخدام طريقة الطلب HTTP GET.
  • تُستخدم غالبًا مع GenericAPIView.

مثال:

from rest_framework.mixins import ListModelMixin

class MyModelListView(ListModelMixin, GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

4. RetrieveModelMixin

  • تضيف وظيفة استرجاع كائن واحد باستخدام طريقة الطلب HTTP GET.
  • تُستخدم بشكل مثالي مع GenericAPIView.

مثال:

from rest_framework.mixins import RetrieveModelMixin

class MyModelRetrieveView(RetrieveModelMixin, GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

5. UpdateModelMixin

  • تضيف وظيفة تحديث الكائنات باستخدام طريقتي الطلب HTTP PUT أو PATCH.
  • تُستخدم عادةً مع GenericAPIView.

مثال:

from rest_framework.mixins import UpdateModelMixin

class MyModelUpdateView(UpdateModelMixin, GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

مثال على الجمع بين الميكسينات

يمكنك دمج عدة ميكسينات لإنشاء عرض مخصص يجمع بين أكثر من وظيفة.

مثال:

from rest_framework.mixins import (
    CreateModelMixin,
    ListModelMixin,
    RetrieveModelMixin,
    UpdateModelMixin,
    DestroyModelMixin,
)
from rest_framework.generics import GenericAPIView

class MyModelCustomView(
    CreateModelMixin,
    ListModelMixin,
    RetrieveModelMixin,
    UpdateModelMixin,
    DestroyModelMixin,
    GenericAPIView,
):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def get(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            return self.retrieve(request, *args, **kwargs)
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

متى تستخدم الميكسينات؟

  • استخدم الميكسينات عندما تحتاج إلى تحكم أكبر وتخصيص للسلوكيات دون كتابة الكود بالكامل يدويًا.
  • إذا كنت تحتاج إلى وظائف CRUD بسيطة، فمن الأفضل استخدام العروض الجاهزة مثل RetrieveUpdateDestroyAPIView لأنها أكثر اختصارًا وأسهل استخدامًا.

Paginations:

تُعد التقسيم (Pagination) في Django REST Framework وسيلة مهمة للتحكم في عدد السجلات التي يتم إرجاعها في استجابات API، مما يساعد على إدارة مجموعات البيانات الكبيرة بسهولة. فيما يلي شرح لأنواع التقسيم الأساسية:

1. BasePagination

  • هي الأساس لجميع أنواع التقسيم في Django REST Framework.
  • يمكنك استخدامها لإنشاء تقسيم مخصص يلبي احتياجاتك.
  • لا تحتوي على وظائف مدمجة مسبقًا؛ عليك تنفيذ الدوال paginate_queryset() وget_paginated_response() يدويًا.

مثال: تقسيم مخصص باستخدام BasePagination

from rest_framework.pagination import BasePagination
from rest_framework.response import Response

class CustomPagination(BasePagination):
    def paginate_queryset(self, queryset, request, view=None):
        page_size = 10
        page = int(request.query_params.get('page', 1))
        start = (page - 1) * page_size
        end = start + page_size
        self.page = queryset[start:end]
        return self.page

    def get_paginated_response(self, data):
        return Response({
            'count': len(self.page),
            'results': data
        })

2. CursorPagination

  • يعتمد على "المؤشر" (Cursor) لتقسيم البيانات.
  • مناسب لمجموعات البيانات الكبيرة لأنه يتجنب الحاجة إلى عد السجلات.
  • يتطلب وجود حقل فريد للترتيب (مثل created_at).

المزايا:

  • يُوفر روابط next وprevious للانتقال بين الصفحات.
  • يضمن تقسيمًا ثابتًا حتى مع تغييرات البيانات الديناميكية.

الإعدادات:

  • cursor_query_param: اسم معلمة الطلب للمؤشر (الافتراضي: cursor).
  • ordering: الحقل الافتراضي للترتيب (مثل -created_at).

مثال:

from rest_framework.pagination import CursorPagination

class MyCursorPagination(CursorPagination):
    page_size = 5
    ordering = '-created_at'  # يجب أن يحتوي النموذج على هذا الحقل.

3. LimitOffsetPagination

  • يُوفر التحكم في عدد السجلات (limit) وموقع البدء (offset).
  • مفيد عندما يحتاج العميل إلى التحكم في حجم الصفحة.

المزايا:

  • معلمات الطلب: limit (الافتراضي: 10)، offset (الافتراضي: 0).

الإعدادات:

  • default_limit: عدد السجلات الافتراضي لكل صفحة.
  • max_limit: الحد الأقصى لعدد السجلات التي يمكن طلبها.

مثال:

from rest_framework.pagination import LimitOffsetPagination

class MyLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 5
    max_limit = 100

4. PageNumberPagination

  • يُقسم السجلات إلى صفحات باستخدام أرقام الصفحات.
  • يُعتبر النوع الأكثر استخدامًا والأكثر سهولة.

المزايا:

  • معلمة الطلب: page (الافتراضي: 1).
  • يُتيح تخصيص page_size (عدد السجلات لكل صفحة).

الإعدادات:

  • page_size: عدد السجلات لكل صفحة.
  • page_query_param: معلمة الطلب للصفحة (الافتراضي: page).
  • page_size_query_param: تتيح للعميل تحديد حجم الصفحة.
  • max_page_size: تحدد الحد الأقصى لحجم الصفحة.

مثال:

from rest_framework.pagination import PageNumberPagination

class MyPageNumberPagination(PageNumberPagination):
    page_size = 5
    page_size_query_param = 'page_size'
    max_page_size = 100

إعداد التقسيم عالميًا

لتطبيق التقسيم على مستوى المشروع بالكامل، يمكن ضبطه في ملف settings.py:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,  # حجم الصفحة الافتراضي
}

كيفية اختيار نوع التقسيم المناسب؟

  • BasePagination: إذا كنت بحاجة إلى تنفيذ تقسيم مخصص تمامًا.
  • CursorPagination: للمجموعات الكبيرة من البيانات أو البيانات الديناميكية التي تحتاج إلى تقسيم مستقر.
  • LimitOffsetPagination: عندما يحتاج العميل إلى التحكم في حجم الصفحة وموقع البدء.
  • PageNumberPagination: للتقسيم البسيط والقائم على أرقام الصفحات، وهو الأسهل للاستخدام.

Serializers:

يوفر Django REST Framework (DRF) فئات مختلفة من الـ Serializers لتحويل أنواع البيانات المعقدة (مثل نماذج Django) إلى أنواع بيانات بايثون الأصلية والعكس صحيح. إليك شرحًا لأنواع الـ Serializers التي ذكرتها:

1. BaseSerializer

  • الفئة الأساسية (الأقل مستوى) لتصميم Serializer مخصص بالكامل.
  • لا تعتمد على نموذج معين أو بنية بيانات، مما يمنحك تحكمًا كاملًا في عمليات التحويل.
  • تُستخدم عند الحاجة إلى Serializer مخصص للغاية.

مثال:

from rest_framework.serializers import BaseSerializer

class CustomSerializer(BaseSerializer):
    def to_representation(self, instance):
        # تحويل الكائن إلى قاموس
        return {
            'id': instance.id,
            'name': instance.name,
            'description': instance.description,
        }

    def to_internal_value(self, data):
        # تحويل البيانات المدخلة إلى كائن أو قاموس مُتحقق منه
        return {
            'name': data.get('name'),
            'description': data.get('description'),
        }

2. HyperlinkedModelSerializer

  • فئة مشتقة من ModelSerializer.
  • تمثل العلاقات باستخدام روابط (URLs) بدلاً من استخدام المفاتيح الأساسية (Primary Keys).
  • مناسبة لـ APIs التي تعتمد على علاقات مفرططة (Hyperlinked).

المزايا:

  • تضيف حقل url تلقائيًا.
  • تتطلب حقل HyperlinkedIdentityField للعلاقات.

مثال:

from rest_framework.serializers import HyperlinkedModelSerializer
from .models import MyModel

class MyModelHyperlinkedSerializer(HyperlinkedModelSerializer):
    class Meta:
        model = MyModel
        fields = ['url', 'id', 'name', 'description']

3. ListSerializer

  • تُستخدم مع قوائم الكائنات (Collections).
  • تُعتبر الفئة الأساسية عند استخدام خيار many=True في Serializers.
  • تتيح تخصيص سلوك العمل مع قوائم الكائنات.

تخصيص ListSerializer:

from rest_framework.serializers import ListSerializer, Serializer

class MyObjectSerializer(Serializer):
    name = serializers.CharField()

class MyListSerializer(ListSerializer):
    def update(self, instance, validated_data):
        # منطق مخصص لتحديث كائنات متعددة
        pass

class CustomListSerializer(Serializer):
    child = MyObjectSerializer(many=True, list_serializer_class=MyListSerializer)

4. ModelSerializer

  • Serializer عالي المستوى مخصص لنماذج Django.
  • يقوم تلقائيًا بإنشاء الحقول بناءً على تعريف النموذج.
  • يُبسط المهام الشائعة مثل عمليات CRUD.

المزايا:

  • إنشاء الحقول تلقائيًا للسمات والعلاقات.
  • يدعم إضافة الحقول والأساليب المخصصة.

مثال:

from rest_framework.serializers import ModelSerializer
from .models import MyModel

class MyModelSerializer(ModelSerializer):
    class Meta:
        model = MyModel
        fields = '__all__'  # تضمين جميع الحقول في النموذج

5. Serializer

  • الفئة الأكثر استخدامًا لتعريف Serializers.
  • تُوفر تحكمًا أكبر مقارنة بـ ModelSerializer.
  • تتطلب تعريف الحقول يدويًا.

المزايا:

  • تُستخدم لبيانات غير مرتبطة بالنماذج أو عند الحاجة إلى تخصيص عالي.
  • تُتيح تعريف أساليب مخصصة لـ create وupdate.

مثال:

from rest_framework import serializers

class MyCustomSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=100)
    description = serializers.CharField()

    def create(self, validated_data):
        # إنشاء كائن جديد وإرجاعه
        return MyModel.objects.create(**validated_data)

    def update(self, instance, validated_data):
        # تحديث كائن موجود وإرجاعه
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.save()
        return instance

مقارنة بين الـ Serializers

Serializer الاستخدام
BaseSerializer عند الحاجة إلى التحكم الكامل في عملية التحويل.
HyperlinkedModelSerializer لواجهات API التي تستخدم روابط لتمثيل العلاقات.
ListSerializer للتعامل مع قوائم الكائنات مع متطلبات مخصصة.
ModelSerializer لتبسيط عمليات التحويل للبيانات المعتمدة على نماذج Django.
Serializer للاستخدامات العامة أو البيانات غير المرتبطة بالنماذج أو عندما تحتاج إلى منطق مخصص.

متى تستخدم كل نوع؟

  • استخدم BaseSerializer للتحكم الكامل عند تصميم Serializers مخصصة للغاية.
  • استخدم ModelSerializer إذا كنت بحاجة إلى واجهات API بسيطة وسريعة تعتمد على النماذج.
  • استخدم Serializer للبيانات غير المرتبطة بالنماذج أو عند الحاجة إلى تخصيص كبير.
  • استخدم HyperlinkedModelSerializer عند الحاجة إلى تمثيل العلاقات باستخدام روابط.
  • استخدم ListSerializer للتعامل مع قوائم الكائنات التي تتطلب سلوكًا مخصصًا.

الخاتمة

سواء كنت تطور واجهات برمجة بيانات بسيطة أو أنظمة API معقدة، فإن Django REST Framework يوفر الأدوات اللازمة لتلبية احتياجاتك. من خلال APIView للمرونة الكاملة، أو ModelViewSet لإنشاء APIs قياسية بسهولة، يمكنك اختيار الفئة المناسبة لكل حالة. إن فهم هذه الفئات واستخدامها بشكل صحيح يساعدك على تحسين كفاءة التطوير وضمان جودة الخدمة التي تقدمها. الآن، حان الوقت لتطبيق هذه المعرفة في مشاريعك القادمة وتحقيق أقصى استفادة من DRF!

حول المحتوى:

تعرف على الفئات المختلفة التي يقدمها Django REST Framework مثل APIView وViewSets وModelViewSet. استكشف كيفية استخدامها لإنشاء RESTful APIs قوية ومرنة مع أمثلة عملية ونصائح مفيدة.