دليل المبتدئين لإطار العمل Django REST

في هذا الدرس سنتعلم إيطار العمل Django Rest والذي يعمل مع كل من نظامي التشغيل Windows وmacOS.

الإعدادات الأولية

ستحتاج إلى تثبيت أحدث إصدار من Python. و بمجرد الانتهاء من ذلك، قم بإنشاء دليل على سطح المكتب أو أي مكان أخر يسهل الوصول إليه ثم قم بإعداد البيئة الافتراضية.

# Windows
$ cd onedrive\desktop\code
$ mkdir drf
$ cd drf
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
(.venv) $ 

داخل الدليل الجديد، قم بتثبيت djangodjangorestframework و  pygments .

(.venv) $ python -m pip install django
(.venv) $ python -m pip install djangorestframework
(.venv) $ python -m pip install pygments

إذا قمت بعد ذلك بكتابة الأمر pip freeze، فسترى جميع الحزم المثبتة على البيئة الافتراضية الخاصة بك :

(.venv) $ pip freeze
asgiref==3.6.0
Django==4.1.5
djangorestframework==3.14.0
Pygments==2.14.0
pytz==2022.7.1
sqlparse==0.4.3

سنطلق على مشروعنا الجديد اسم  tutorial ويوجد بداخله تطبيق يسمى  snippets لواجهة برمجة تطبيقات الويب الخاصة بنا.

يحب إضافة  .  في نهاية الأمر اختياري ولكن يوصى به وإلا فسيقوم Django بإنشاء دليل إضافي باستخدام الأمر  startproject.

(.venv) $ django-admin startproject tutorial .
(.venv) $ python manage.py startapp snippets

أضف تطبيق  snippets و  rest_framework إلى  INSTALLED_APPS  في ملفنا  tutorial/settings.py.

# tutorial/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",  # جديد
    "snippets",  # جديد
]

النماذج

يعد النموذج مكانًا جيدًا للبدء في أي مشروع جديد. في ملف  snippets/models.py قم بإنشاء نموذج جديد يسمى  Snippet.

# snippets/models.py
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default="")
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(
        choices=LANGUAGE_CHOICES, default="python", max_length=100
    )
    style = models.CharField(choices=STYLE_CHOICES, default="friendly", max_length=100)

    class Meta:
        ordering = ["created"]

    def __str__(self):
        return self.title

ثم قم بإنشاء ملف ترحيل أولي وقم بمزامنة قاعدة البيانات لأول مرة.

(.venv) $ python manage.py makemigrations snippets
(.venv) $ python manage.py migrate

نحتاج إلى إضافة بعض البيانات إلى نموذجنا حتى يبدو “حقيقيًا”.

نحتاج أولاً إلى تحديث  snippets/admin.py حتى يظهر التطبيق لوحة التحكم! تمامًا كما هو الحال مع إعداد  INSTALLED_APPS، يجب إضافة التطبيقات بشكل صريح إلى admin.

# snippets/admin.py
from django.contrib import admin
from .models import Snippet

admin.site.register(Snippet)

الآن قم بإنشاء حساب مستخدم متميز لتسجيل الدخول. اتبع جميع التعليمات لإعداد اسم المستخدم والبريد الإلكتروني وكلمة المرور. لقد استخدمت  adminadmin@email.com, و testpass123.

(.venv) $ python manage.py createsuperuser

بعد ذلك نقوم بتشغيل خادم الويب المحلي لدينا لأول مرة.

(.venv) $ python manage.py runserver

انتقل إلى صفحة Django الرئيسية على http://127.0.0.1:8000/ للتأكد من أن كل شيء يعمل.

الصفحة الترحيبية لجانغو
الصفحة الترحيبية لجانغو

بعد ذلك نتوجه إلى صفحة المسؤول على http://127.0.0.1:8000/admin/. قم بتسجيل الدخول باستخدام حساب المستخدم المتميز الخاص بك.

صفحة مسؤول جانغو

انقر فوق الزر “+ إضافة” بجوار المقتطفات. وإنشاء مقتطفين جديدين.

إضافة موضوع جديد جانغو
إضافة موضوع جديد جانغو

انقر فوق زر “حفظ” في الجزء السفلي الأيسر لكل مقتطف. سيكون كلاهما مرئيًا على صفحة المقتطفات الرئيسية.

صفحة المقتطفات الرئيسية.

التسلسل

يقوم Serializer بتحويل حالات النموذج إلى JSON، و هذا هو “السحر” الذي يوفره لنا Django Rest Framework.

المواقع التقليدي هي صفحات HTML وCSS ومحتوى من قاعدة البيانات. لكن واجهة برمجة التطبيقات (API) لا تهتم بذلك: فهي مجرد بيانات أولية عند نقاط النهاية، وهو ما يعني JSON، و خصائص HTTP المصاحبة هي التي تخبر واجهة برمجة التطبيقات (API) بالإجراءات التي يمكن اتخاذها (المزيد حول هذا قريبًا).

قم بإنشاء ملف  snippets/serializers.py جديد وقم بتحديثه على النحو التالي.

يمكننا توسيع ModelSerializer الخاص بـ DRF لإنشاء فئة  SnippetSerializer التي تستخدم نموذجنا وتخرج حقول الجدول.

# snippets/serializers
from rest_framework import serializers
from .models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = (
            "id",
            "title",
            "code",
            "linenos",
            "language",
            "style",
        )

نحتاج بعد ذلك إلى عرض يتعامل مع منطق دمج النموذج والمُسلسل وعنوان URL معًا في النهاية.

تمامًا كما يأتي Django التقليدي مع العديد من العروض العامة المستندة إلى الفئات للتعامل مع الوظائف الشائعة، كذلك فإن Django Rest Framework لديه مجموعته الخاصة من العروض العامة القوية المستندة إلى الفئات والتي يمكننا استخدامها.

على وجه التحديد، سنستخدم ListCreateAPIView لإنشاء نقطة نهاية للقراءة والكتابة تسرد جميع مثيلات Snippet المتاحة ثم RetrieveUpdateDestroyAPIView لنقطة نهاية القراءة والكتابة والحذف لكل مقتطف فردي.

# snippets/views.py
from rest_framework import generics
from .models import Snippet
from .serializers import SnippetSerializer


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

عناوين URL

الخطوة الأخيرة هي إعداد عناوين URL الخاصة بنا.

في الجزء العلوي من ملف tutorial/urls.py على مستوى المشروع نكتب مايلي :

# tutorial/urls.py
from django.contrib import admin
from django.urls import include, path  # new

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("snippets.urls")),  # new
]

ثم قم بإنشاء ملف  snippets/urls.py وأضف الكود التالي:

# snippets/urls.py
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    path("snippets/", views.SnippetList.as_view()),
    path("snippets/<int:pk>/", views.SnippetDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

يعد تضمين format_suffix_patterns خيارًا اختياريًا يوفر طريقة بسيطة للإشارة إلى تنسيق ملف محدد لنقطة نهاية URL.

وهذا يعني أن واجهة برمجة التطبيقات الخاصة بنا ستكون قادرة على التعامل مع عناوين URL مثل http://example.com/api/items/4.json و http://example.com/api/items/4.

Browsable API

يأتي Django Rest Framework مزودًا بواجهة برمجة تطبيقات قابلة للتصفح يمكننا استخدامها الآن. تأكد من تشغيل الخادم المحلي.

(.venv) $ python manage.py runserver

نقوم بفتح المتصفح على http://127.0.0.1:8000/snippets/.

يمكننا أيضًا الانتقال إلى عرض تفاصيل لكل مقتطف. على سبيل المثال، المقتطف الأول موجود على http://127.0.0.1:8000/snippets/1/.

للتذكير، يتم تعيين  id  تلقائيًا بواسطة Django عند كل إدخال في قاعدة البيانات.

الطلبات والردود

حاليًا، لا يوجد لدى واجهة برمجة التطبيقات الخاصة بنا أي قيود على من يمكنه تعديل المقتطفات أو حذفها. في هذا القسم سوف نتأكد من ذلك:

  • ترتبط المقتطفات دائمًا بمنشئ المحتوى.
  • يمكن للمستخدمين المصادق عليهم فقط إنشاء مقتطفات.
  • لا يجوز لأحد سوى منشئ المقتطف تحديثه أو حذفه.

إضافة معلومات إلى نموذجنا

دعونا أولاً نضيف حقلين إلى فئة نموذج  Snippet الموجودة لدينا:  owner الذي سيمثل المستخدم الذي أنشأ مقتطف التعليمات البرمجية و highlighted  لتخزين تمثيل HTML المميز للتعليمات البرمجية.

نريد أيضًا التأكد من أنه عند حفظ النموذج، نستخدم مكتبة تمييز كود  pygments لملء الحقل المميز لدينا.

لذلك سنحتاج إلى إستدعاء بعض الحزم الإضافية بالإضافة إلى طريقة .save().

# snippets/models.py
from django.db import models
from pygments import highlight  # جديد
from pygments.formatters.html import HtmlFormatter  # جديد
from pygments.lexers import get_all_lexers, get_lexer_by_name  # جديد
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default="")
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(
        choices=LANGUAGE_CHOICES, default="python", max_length=100
    )
    style = models.CharField(choices=STYLE_CHOICES, default="friendly", max_length=100)
    owner = models.ForeignKey(
        "auth.User", related_name="snippets", on_delete=models.CASCADE
    )  # جديد
    highlighted = models.TextField()  # جديد

    class Meta:
        ordering = ("created",)

    def save(self, *args, **kwargs):  # جديد
        """
        Use the `pygments` library to create a highlighted HTML
        representation of the code snippet.
        """
        lexer = get_lexer_by_name(self.language)
        linenos = "table" if self.linenos else False
        options = {"title": self.title} if self.title else {}
        formatter = HtmlFormatter(
            style=self.style, linenos=linenos, full=True, **options
        )
        self.highlighted = highlight(self.code, lexer, formatter)
        super(Snippet, self).save(*args, **kwargs)

    def __str__(self):
        return self.title

عادةً ما نقوم بإنشاء عملية ترحيل ومزامنتها لتحديث جداول قاعدة البيانات الخاصة بنا. و نظرًا لأننا أضفنا  owner ولدينا محتوى موجود، فمن السهل حذف قاعدة البيانات والبدء من جديد.

تأكد من إيقاف الخادم المحلي باستخدام  Control+c.

(.venv) $ rm db.sqlite3
(.venv) $ rm -r snippets/migrations
(.venv) $ python manage.py makemigrations snippets
(.venv) $ python manage.py migrate

أعد إنشاء الخطوة السابقة لإنشاء حساب مستخدم متميز جديد، سنحتاج أيضًا إلى حساب مستخدم متميز ثانٍ.

لذا قم بتشغيل  createsuperuser مرتين ثم قم بتشغيل الخادم المحلي.

(.venv) $ python manage.py createsuperuser
(.venv) $ python manage.py createsuperuser
(.venv) $ python manage.py runserver

إفتح صفحة مسؤول Django على http://127.0.0.1:8000/admin/ وقم بتسجيل الدخول باستخدام حساب  admin.

إذا نقرت على رابط Users، ستتم إعادة توجيهك إلى صفحة المستخدمين و التي ينبغي أن تظهر مستخدمين.

صفحة المستخدمين جانغو

نحن بحاجة إلى إعادة إنشاء مقتطفاتنا أيضًا بعد أن قمنا بحذف قاعدة البيانات الأولية.

أنشئ مقتطفًا جديدًا وحدد  Owner كأحد مستخدمينا. سأقوم بإختيار testuser.

ولكن هناك مشكلة عندما نحاول “الحفظ”.

add_snippet_error

لقد حصلنا على خطأ  ValidationError . في الدروس الرسمية يتم استخدام Django Shell لإدخال البيانات، أما نحن فإستخدمنا صفحة admin. لذا فإن الكود الموجود لا يعمل كما هو.

تذكر أن  highlighted يتم تعيينه تلقائيًا بواسطة طريقة save() المخصصة الخاصة بنا في النموذج، لكن المسؤول لا يعرف ذلك. و هو يتوقع منا إدخال قيمة هنا. و لحل المشكلة قم بتحديث ملف  admin.py الخاص بنا وقم بتعيين  highlighted كحقل للقراءة فقط.

# snippets/admin.py
from django.contrib import admin
from .models import Snippet


class SnippetAdmin(admin.ModelAdmin):
    readonly_fields = ("highlighted",)


admin.site.register(Snippet, SnippetAdmin)

حاول النقر على الزر “حفظ” مرة أخرى. و ينبغي أن تعمل هذه المرة.

الخطوة الأخيرة هي النقر على رابط تسجيل الخروج في الركن الأيمن العلوي من صفحة المسؤول.

تسجيل الخروج من صفحة django admin

سنقوم قريبًا بإضافة أذونات إلى واجهة برمجة التطبيقات الخاصة بنا بحيث لا يتمكن سوى المستخدمين المصادقين (المسجلين الدخول) من الوصول.

إضافة نقاط النهاية إلى نماذج المستخدم لدينا

الآن بعد أن أصبح لدينا بعض المستخدمين للعمل معهم، فلنضيف نقاط النهاية لهم إلى واجهة برمجة التطبيقات (API) الخاصة بنا. أضف فئة  UserSerializer جديدة إلى ملف  snippets/serializers.py.

# snippets/serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = (
            "id",
            "title",
            "code",
            "linenos",
            "language",
            "style",
        )


class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(
        many=True, queryset=Snippet.objects.all()
    )

    class Meta:
        model = User
        fields = ("id", "username", "snippets")

و نظرًا لأن  snippets هي علاقة عكسية في نموذج  User، فلن يتم تضمينها افتراضيًا باستخدام فئة  ModelSerializer، و لهذا نحتاج إلى إضافة حقل صريح لها.

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

لاحظ أننا نستخدم  RetrieveAPIView  العام القائم على الفئة لعرض التفاصيل للقراءة فقط. وأننا نقوم باستيراد كل من  User  و  UserSerializer في الأعلى.

# snippets/views.py
from django.contrib.auth.models import User  # جديد
from rest_framework import generics

from .models import Snippet
from .serializers import SnippetSerializer, UserSerializer  # جديد


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class UserList(generics.ListAPIView):  # جديد
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):  # جديد
    queryset = User.objects.all()
    serializer_class = UserSerializer

وأخيرًا، نحتاج إلى إضافة العروض الجديدة إلى واجهة برمجة التطبيقات (API) عن طريق إعداد مسارات URL الخاصة بها. أضف النمط التالي إلى  snippets/urls.py.

# snippets/urls.py
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    path("snippets/", views.SnippetList.as_view()),
    path("snippets/<int:pk>/", views.SnippetDetail.as_view()),
    path("users/", views.UserList.as_view()),  # جديد
    path("users/<int:pk>/", views.UserDetail.as_view()),  # جديد
]

urlpatterns = format_suffix_patterns(urlpatterns)

ربط المقتطفات بالمستخدمين

لا توجد حاليًا طريقة لربط المستخدم الذي قام بتسجيل الدخول تلقائيًا والذي أنشأ مقتطفًا. يمكننا ضبط هذا تلقائيًا عن طريق تجاوز  .perform_create() في عروض المقتطف الخاصة بنا والتي تتيح لنا تعديل كيفية الحفظ .

أضف الطريقة التالية إلى فئة عرض  SnippetList الموجودة لدينا.

# snippets/views.py
class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def perform_create(self, serializer):  # جديد
        serializer.save(owner=self.request.user)

تحديث التسلسل

الآن بعد أن أصبحت المقتطفات مرتبطة بالمستخدم الذي أنشأها، فلنقم بتحديث  SnippetSerializer ب  owner ليعكس ذلك. تأكد أيضًا من تضمين  owner في قائمة الحقول أيضًا.

# snippets/serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source="owner.username")  # جديد

    class Meta:
        model = Snippet
        fields = (
            "id",
            "title",
            "code",
            "linenos",
            "language",
            "style",
            "owner",
        )  # new


class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(
        many=True, queryset=Snippet.objects.all()
    )

    class Meta:
        model = User
        fields = ("id", "username", "snippets")

تتحكم وسيطة  source المستخدمة هنا في السمة المستخدمة لملء الحقل ويمكن أن تشير إلى أي سمة في التسلسل. لاحظ أيضًا أننا نستخدم  ReadOnlyField الذي يكون دائمًا للقراءة فقط؛ ولا يمكن استخدامه لتحديث النموذج عندما يتم تسلسله.

كان بإمكاننا أيضًا استخدامCharField(read_only=True) لإنجاز نفس الشيء.

إضافة الأذونات المطلوبة للعروض

الآن بعد أن أصبحت مقتطفات التعليمات البرمجية مرتبطة بالمستخدمين، نريد التأكد من أن المستخدمين الذين تمت مصادقتهم فقط هم القادرون على إنشاء مقتطفات التعليمات البرمجية وتحديثها وحذفها.

يأتي Django Rest Framework مزودًا بعدد من فئات الأذونات التي يمكننا استخدامها لتقييد الوصول إلى عرض معين.

سنستخدم هنا  IsAuthenticatedOrReadOnly للتأكد من أن الطلبات المصادق عليها تتمتع بحق الوصول للقراءة والكتابة وأن الطلبات غير المصادق عليها لها حق الوصول للقراءة فقط.

# snippets/views.py
from django.contrib.auth.models import User
from rest_framework import generics, permissions  # جديد

from .models import Snippet
from .serializers import SnippetSerializer, UserSerializer


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)  # جديد

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)  # جديد


class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

إضافة تسجيل الدخول إلى واجهة برمجة التطبيقات القابلة للتصفح

انتقل الآن إلى واجهة برمجة التطبيقات القابلة للتصفح لدينا على http://127.0.0.1:8000/snippets/.

api_list_loggedout

و نظرًا لأنه تم تسجيل خروجنا، لاحظ أنك لم تعد قادرًا على إنشاء مقتطفات برمجية جديدة. للقيام بذلك، يجب عليك تسجيل الدخول كمستخدم.

يمكننا إضافة عرض تسجيل الدخول إلى واجهة برمجة التطبيقات القابلة للتصفح عن طريق تحرير URLconf في ملف  tutorial/urls.py على مستوى المشروع. أضف  rest_framework.urls إلى مسار  api-auth/.

# tutorial/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api-auth/", include("rest_framework.urls")),  # جديد
    path("", include("snippets.urls")),
]

لاحظ أن المسار الفعلي للمستخدم لا يهم و بدلا من  api-auth/ كان بإمكاننا أيضًا استخدام  something-else/

الشيء المهم هو أنه تم تضمين  rest_framework.urls.

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

login_link

قم بتسجيل الدخول باستخدام حساب المستخدم الخاص بك. ثم انتقل إلى http://127.0.0.1:8000/users/ ولاحظ أن معرفات المقتطفات مرتبطة بكل مستخدم.

users_list

لدينا مقتطف واحد فقط، تم إنشاؤه باستخدام حساب المستخدم testuser ويحتوي على المعرف الأساسي 1. إذا أضفنا مقتطفات إضافية لكل مستخدم، فستظهر هنا أيضًا. لذا فإن الأمور تسير على ما يرام.

أكتفي يهذا القدر ليكون لنا جزء أخر نكمل فيه البقية


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top

اكتشاف المزيد من بايثون العربي

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

Continue reading