كيف تبني RESTful API باستخدام إطار البايثون Django

كيف تبني RESTful API باستخدام إطار البايثون Django

أهلًا وسهلًا بكم في هذا المقال الجديد الذي سنتناول فيه موضوعًا جديدًا من مواضيع لغة البرمجة بايثون وهو موضوع بناء واجهة برمجية RESTful API باستخدام إطار عمل جانغو Django.

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

ما هي RESTful API؟

تُعتبر REST (اختصارًا لـ REpresentational State Transfer) معيارًا خاصا لبناء خدمات الويب والواجهات البرمجية API الخاصة بالويب، ويتم استخدامها من قبل تطبيقات ويب أو موبايل وحتى تطبيقات سطح المكتب، ولنكون دقيقين أكثر، فإن أي برنامج أو نظام يمتلك الإمكانية للتعامل مع بروتوكول HTTP ووظائفه فهو مخول لاستخدام ,والتعامل مع Restful API.

REST is essentially a set of useful conventions for structuring a web API

خصائص RESTful API

لابد لأي نظام RESTful الالتزام ببعض القيود والشروط التي تُعطيه صفة REST وهذه بعضها:

  • نظام خادم-عميل: لابد من وجود فصل بين مُقدم الخدمة (Server) وبين المُستهلك الذي يستخدم الخدمة (Client).
  • عدمية متابعة الحالة (Stateless): لابد لكل طلب (Request) يتم ارساله الى الخادم أن يحتوي على كافة المعلومات اللازمة لتنفيذ الطلب، ولا يمكن للخادم الاحتفاظ بأي بيانات تخص طلب مُعين لاستخدامها في تنفيذ طلب أخر.
  • التخبئة (Cacheable): لابد للخادم من إعلام الجهة الطالبة للطلب بإمكانية تخبئة الطلبات أم لا.
  • نظام الطبقات: لابد من بناء الإتصال بين الخادم والعميل بطريقة معيارية بحيث لو تطلب وجود طرف ثالث بين الخادم والعميل لتنفيذ الطلبات عوضًا عن الخادم ألا يحتاج العميل أن يقوم بشيء مختلف.

تم تصميم هيكلية REST لكي توائم بروتوكول HTTP، ويُعتبر “المصدر (Resource)” هو اللب الأساسي لـ Restful API ويتم تمثيل المصدر بمُعرف المورد النظامي (URI)، ويقوم العميل بإرسال الطلبات لهذه URIs باستخدام أحد وظائف بروتوكول HTTP وقد ينتج عن ذلك تغيير في المصدر الذي استقبل الطلب.

وظائف خدمات RESTful API

يُمكنك من خلال خدمات RESTful من تطوير نظام ويب يحتوي على كافة العمليات الأساسية للتعامل مع البيانات وهو ما نُطلق عليه عمليات CURD اختصارًا لـ (CREATE، UPDATE، RETRIEVE، DELETE) ويُمكننا الحصول على ذلك من خلال استخدام وظائف HTTP الأساسية التالية:

  • GET: لجلب بيانات.
  • POST: لإنشاء أو إضافة بيانات جديدة.
  • PUT: لتعديل أو استبدال بيانات.
  • DELETE: لحذف بيانات.

يوجد وظائف أخرى مثل OPTION، PATCH، CONNECT، TRACE ولها استخداماتها الخاصة.

أشهر أطر البايثون التي تتيح بناء RESTful API

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

https://wiki.python.org/moin/WebFrameworks

من أشهر أُطر الويب الموجودة في البايثون والتي تُقدم إمكانية بناء خدمات وواجهات RESTful هو إطار Django والذي يُسهل من عملية بناء الخدمات والواجهات عبر كتابة شيفرة برمجية قليلة، بالإضافة لإطار Flask الشهير.

سنتناول في شرحنا في هذا المقال كيفية بناء واجهة برمجية RESTful باستخدام اطار العمل Django.

الجانب العملي

سوف نبدأ العمل ببناء واجهة برمجية RESTful باستخدام اطار Django ومكتبة Django REST Framework وستقوم هذه الواجهة بإجراء عمليات CRUD على قاعدة بيانات بسيطة من نوع SQLite.

نظام التشغيل المُستخدم في هذا المقال هو Windows 10 64 bit.

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

يوجد طريقتان رئيسيتان في بناء الواجهة البرمجية RESTful في إطار العمل Django وهما:

  • Function Based : عند طلب أي واجهة Restful في هذا النوع، فإنه يتم تنفيذ دالة أو وظيفة مُعينة، وهذا النوع كان الأشهر سابقًا وهو ما سنتحدث عنه لسهولته في الشرح والتوضيح.
  • Class Based : في هذا النوع يتم التعامل مع فئات (Classes) وليس دوال، وهو الذي يُنصح به وسنحاول أن نُفرد له مقالا خاصا.

تصميم واجهات برمجية خاصة ببيانات طلاب

سنفترض أننا نريد بناء واجهات برمجية للتعامل مع بيانات طلاب، وستكون هذه البيانات محفوظة في جدول بإسم Students في قاعدة بيانات SQLite بالأعمدة التالية:

  • Student_Name (اسم الطالب)
  • Student_College (الكلية)
  • Student_Avg (المعدل)
  • Admission_Date (تاريخ القبول)

الجدول التالي يوضح الوظائف الأربعة الرئيسية في RESTful API الخاصة بالطلاب والواجهات البرمجية التي سنبنيها:

MethodActionURL
GETجلب بيانات جميع الطلابhttp://example.com/api/students
GETجلب بيانات طالب مُحددhttp://example.com/api/students/1234
POSTإنشاء بيانات طالب جديدhttp://example.com/api/students
PUTتعديل بيانات طالبhttp://example.com/api/students/1234
DELETEحذف بيانات طالبhttp://example.com/api/students/1234

تثبيت إطار العمل والمكتبات اللازمة

لتثبيت إطار العمل Django نقوم بتنفيذ الأمر التالي في الطرفية:

pip install django

سنحتاج لتثبيت المكتبة الخاصة بواجهات RESTful والتي تُسمى Django REST Framework ولذلك نقوم بتنفيذ الأمر التالي:

pip install djangorestframework

لاحظ أن اسم المكتبة طويل ومن كلمة واحدة.

ملاحظة/ يُفضل أن تقوم باستخدام أداة virtualenv أثناء العمل، وتستطيع الإطلاع على بعض تفاصيل هذه الأداة في هذا مقال أداة virtualenv كأداة لتسهيل البرمجة والعمل بلغة البايثون

بناء المشروع ومجلد العمل

بعد أن قمنا بتثبيت الإطار والمكتبة اللازمة للعمل، نقوم بتنفيذ الأمر التالي في مسار العمل الذي نريده:

python django-admin.py startproject students_api

الأمر السابق سينشئ مجلد جديد في مسار العمل الحالي باسم students_api وهذا المجلد يُمثل مجلد المشروع ويحتوي على ملفات بايثون متعددة.

نقوم بالدخول في المجلد، ومن ثم نقوم بتنفيذ الأمر التالي:

python manage.py startapp students

الأمر السابق يقوم بإنشاء مجلد جديد باسم students داخل مجلد students_api، وهذا المجلد الجديد يُمثل التطبيق الذي سنقوم ببناء الواجهات البرمجية داخله.

يحتوي مجلد students على ملفات بايثون بالإضافة لمجلد باسم migrations والذي سيحتوي على ملفات خاصة بتهجير قاعدة البيانات.

قم بفتح الملف settings.py الموجود داخل المجلد students_api الداخلي (وليس الرئيسي) وقم بإضافة ‘rest_framework’ و ‘students.app.StudentsConfig’ الى المصفوفة INSTALLED_APPS الموجودة في الملف لتصبح كالتالي:

 

INSTALLED_APPS = [

    'django.contrib.admin',

    'django.contrib.auth',

    'django.contrib.contenttypes',

    'django.contrib.sessions',

    'django.contrib.messages',

    'django.contrib.staticfiles',

    'rest_framework',

    'students.apps.StudentsConfig',

]

بذلك نكون أضفنا التطبيق students الى المشروع الرئيسي students_api.

إنشاء النماذج (Models)

يحتوي ملف models.py الموجود في مجلد students على الفئات التي تُمثل الجداول في قاعدة البيانات، ومن خلال هذا الملف نستطيع إنشاء الجداول في قاعدة البيانات والتعديل على هيكليتها وإعداداتها.

سنقوم بإنشاء فئة باسم Students (ترث من الفئة models.Model) وسنحدد خصائص هذه الفئة.

خصائص الفئة هنا تُمثل أعمدة جدول students.

قم بفتح ملف models.py واكتب الشيفرة التالية ثم احفظه:

from django.db import models

class Students(models.Model):

    student_name = models.CharField(max_length=200)

    student_college = models.CharField(max_length=200)

    student_avg = models.IntegerField()

    admission_date = models.DateTimeField()

    class Meta:

        ordering = ('student_name',)

قمنا خلال الشيفرة السابقة بإنشاء فئة باسم Students وعرفنا داخلها الخصائص التي تمثل أعمدة الجدول في قاعدة البيانات. تمثل ordering خيارًا لجعل النتائج مُرتبة ترتيبًا تصاعديا عند جلبها.

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

لإنشاء ملف التهجير نُنفذ الأمر التالي:

python manage.py makemigrations

بعد تنفيذ الأمر السابق، سيتم إنشاء ملف التهجير باسم 0001_initial.py داخل مجلد migrations.

لتنفيذ عملية التهجير وبناء قاعدة البيانات وجدول بيانات الطلاب، نُنفذ الأمر التالي:

python manage.py migrate 

الأمر السابق سيقوم بإنشاء قاعدة البيانات باسم db.sqlite3 في المجلد الرئيسي students_api وسينشئ الجدول بداخلها بالإضافة لمجموعة أخرى من الجداول والتي ستلزم في العمل مستقبلا.

تستطيع استكشاف قاعدة البيانات db.sqlite3 من خلال استخدام البرنامج  SQLite Browser والذي تستطيع الحصول عليه من الموقع التالي : http://sqlitebrowser.org

قد يتبادر الى ذهنك كيف تم تحديد نوع قاعدة البيانات SQLite؟ وماذا لو أردت أن أستخدم نوعًا أخرًا من قواعد البيانات؟

الإجابة تكمن في ملف settings.py والذي يحتوي على إعدادات قاعدة البيانات المرتبطة بالمشروع والموجودة في المتغير DATABASES والذي يأخذ قيمة تلقائية كالتالي:

DATABASES = {

'default': {

               'ENGINE': 'django.db.backends.sqlite3',

               'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

           }

      }

طبيعة وشكل البيانات

عندما نتحدث عن الوجهات البرمجية RESTful فإننا نتكلم (من جهة معينة) عن طرفين يتم انتقال البيانات بينهما، لذلك، فإن موضوع شكل البيانات وهيئتها من المواضيع المهمة في هذا الأمر، فكيف من الممكن أن نتفق على هيئة واحدة يستطيع كل طرف التعامل معها؟ الإجابة على ذلك هو استخدام ونقل البيانات على شكل بيانات JSON، وهو ما سنتكلم عنه فيما يلي.

لابد على الواجهات البرمجية التي سنبنيها أن تكون قادرة على إجراء عمليات Serialization و Deserialization لبيانات الطلاب الموجودة على هيئة JSON، لذلك سنقوم ببناء وسيط (Mediator) بين العناصر (Instances) من فئة الطلاب، وبين المتغيرات الأولية للبايثون (Primitives). هذا الوسيط عبارة عن فئة ترث خصائصها من الفئة serializers.Serializer.

سنقوم بإنشاء ملف جديد باسم serializers.py في مجلد students، وسنكتب بداخله الشيفرة التالية:

 

from rest_framework import serializers

from students.models import Students

class StudentsSerializer(serializers.Serializer):

        pk = serializers.IntegerField(read_only=True)

        student_name = serializers.CharField(max_length=200)

        student_college = serializers.CharField(max_length=200)

        student_avg = serializers.IntegerField()

        admission_date = serializers.DateTimeField()

        def create(self, validated_data):

            return Students.objects.create(**validated_data)

        def update(self, instance, validated_data):

            instance.student_name = validated_data.get('student_name',instance.student_name)

            instnace.student_college = validated_data.get('student_college',instance.student_college)

            instance.student_avg = validated_data.get('student_avg',instance.student_avg)

            instance.admission_date = validated_data.get('admission_date',instance.admission_date)

الفئة StudentsSerializer هي الوسيط الذي تكلمنا عنه، ويجب أن يحتوي على البيانات التي سيتم تحويلها من/الى JSON، وبالإضافة لذلك، يجب أن يتم عمل implementation للوظائف create و update الموروثة من الفئة serializers.Serializer بالشكل الموضح في الشيفرة.

الواجهات البرمجية RESTful API

يتم كتابة الشيفرة البرمجية الخاصة بالواجهات في ملف views.py داخل مجلد students.

قم بفتح الملف واحفظ بداخله الشيفرة التالية:

 

from django.http import HttpResponse

from django.views.decorators.csrf import csrf_exempt

from rest_framework.renderers import JSONRenderer

from rest_framework.parsers import JSONParser

from rest_framework import status

from students.models import Students

from students.serializers import StudentsSerializer

class JSONResponse(HttpResponse):

    def __init__(self, data, **kwargs):

        content = JSONRenderer().render(data)

        kwargs['content_type'] = 'application/json'

        super(JSONResponse, self).__init__(content, **kwargs)

@csrf_exempt

def students_list(request):

    if request.method == 'GET':

        students = Students.objects.all()

        students_serializer = StudentsSerializer(students, many=True)

        return JSONResponse(students_serializer.data)

    elif request.method == 'POST':

        students_data = JSONParser().parse(request)

        students_serializer = StudentsSerializer(data=students_data)

        if students_serializer.is_valid():

            students_serializer.save()

            return JSONResponse(students_serializer.data,status=status.HTTP_201_CREATED)

        return JSONResponse(students_serializer.errors,status=status.HTTP_400_BAD_REQUEST)

@csrf_exempt

def student_detail(request, pk):

    try:

        student = Students.objects.get(pk=pk)

    except Game.DoesNotExist:

        return HttpResponse(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':

        student_serializer = StudentsSerializer(student)

        return JSONResponse(student_serializer.data)

    elif request.method == 'PUT':

        student_data = JSONParser().parse(request)

        student_serializer = StudentsSerializer(student, data=student_data)

        if student_serializer.is_valid():

            student_serializer.save()

            return JSONResponse(student_serializer.data)

        return JSONResponse(student_serializer.errors,status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':

        student.delete()

        return HttpResponse(status=status.HTTP_204_NO_CONTENT)

الشيفرة السابقة تُمثل الواجهات البرمجية التي سيتم تنفيذها عند طلبها من قبل العميل او المستهلك، وتحتوي بشكل رئيسي على ثلاثة عناصر رئيسية هي الفئة JSONResponse ووظيفتين students_list و student_detail.

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

الوظيفة students_list هي الواجهة الأولى والتي تقوم بمهمتين عند ورود الطلب.

المهمة الأولى في حالة كان نوع الطلب GET سيتم إرجاع بيانات كافة الطلاب.

المهمة الثانية في حالة كان نوع الطلب POST سيتم استخراج بيانات الطالب الجديدة من الطلب عبر إجراء عملية Parsing للطلب ومن ثم حفظ هذه البيانات في قاعدة البيانات بعد التأكد من سلامتها.

الوظيفة student_detail تقوم بتنفيذ ثلاث مهمات حسب نوع الطلب، ففي حالة كان الطلب GET يتم إرجاع بيانات طالب مُحدد حسب رقم pk الخاص به، وإذا كان الطلب من نوع PUT فسيتم تحديث أو تعديل بيانات طالب وفق البيانات المرفقة مع الطلب ووفق رقم pk الخاص بالطالب.

وفي حالة كان نوع الطلب DELETE فسيتم حذف بيانات طالب مُحدد وفق رقم pk المرفق مع الطلب.

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

إضافة روابط  RESTful API

الأن وبعد أن قمنا بكتابة الشيفرة الخاصة بالواجهات البرمجية، يجب علينا أن نقوم بإنشاء ملف urls.py داخل مجلد students.

وظيفة هذا الملف هو تعريف روابط الواجهات البرمجية عبر استخدام تعبيرات Regular Expression وربطها بالوظائف التي تمثل الواجهات البرمجية.

قم بإنشاء الملف urls.py وقم بوضع الشيفرة البرمجية التالية فيه وأحفظه:

 

from django.conf.urls import url

from students import views

urlpatterns = [

url(r'^students/$', views.students_list),

url(r'^students/(?P<pk>[0-9]+)/$', views.student_detail),

]

يجب علينا تعديل ملف urls.py الموجود في المجلد students_api بحيث يتم تعريف root url والتي ستحتوي على مجموعة الروابط المُعرفة مُسبقا في ملف urls.py السابق الموجود في مجلد students.

قم بفتح الملف urls.py الموجود في المجلد students_api وعدله ليصبح كالتالي:

from django.conf.urls import url, include

urlpatterns = [

    url(r'^', include('games.urls')),

]

تشغيل RESTful API وتجريبها

الان نستطيع تشغيل الخادم الخاص بالواجهات البرمجية (الغير امنة بعد) وتجريبه عبر تنفيذ الأمر التالي:

python manage.py runserver

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

System check identified no issues (0 silenced).

January 27, 2018 – 11:15:57

Django version 2.0, using settings ‘students_api.settings’

Starting development server at http://127.0.0.1:8000/

Quit the server with CTRL-BREAK.

الان يوجد عدة خيارات لإختبار الواجهات البرمجية التي أعددناها، حيث يمكننا استخدام أدوات وبرامج جاهزة لذلك، فمثلًا، نستطيع استخدام أداة  curl أو httpie (مكتبة بايثون) في الطرفية لإرسال طلبات للواجهات البرمجية واستقبال الرد ورؤية النتيجة.

الأمر التالي يقوم بإرسال طلب من نوع GET للواجهة البرمجية الأولى students_list باستخدام أداة httpie. وسيقوم هذا الطلب بإرجاع بيانات كافة الطلاب الموجودين في قاعدة البيانات:

http GET :8000/students/

وبما أنه لا يوجد بيانات لدينا، فستكون النتيجة بالشكل التالي:

11

الأمر التالي يقوم بإرسال طلب من نوع POST للواجهة البرمجية students_list والتي ستقوم بحفظ البيانات المرفقة مع الطلب في جدول الطلاب بعد التأكد من صحتها:

http POST :8000/students/  student_name="Ibrahim Elbouhissi" student_college="Computer Engineering" student_avg=85 admission_date="2018-01-27T03:02:00.776594Z"

سيكون ناتج الأمر السابق كالتالي:

22

قد يكون التعامل مع هكذا أدوات صعبًا للبعض نوعًا ما، لذلك فإنه من الممكن استخدام أدوات أخرى ذات واجهات رسومية وسهلة الاستخدام مثل برنامج Postman.

خاتمة

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

الشيفرة البرمجية في هذا المقال وبعض النقاط مأخوذة بتصرف عن الباب الأول في كتاب Building RESTful Python Web Services للمؤلف  Gastón C. Hillar وهذا هو رابط الكتاب على موقع أمازون:

 

 

 

One thought

اترك تعليقاً

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