مقدمة
يستخدم الناس أجهزة متنوعة للاتصال بالإنترنت وتصفحه. ولذلك، يجب أن تكون التطبيقات متاحة من مواقع متنوعة. بالنسبة للمواقع التقليدية، عادةً ما يكفي وجود واجهة مستخدم سريعة الاستجابة، لكن التطبيقات الأكثر تعقيدًا غالبًا ما تتطلب استخدام تقنيات وهياكل أخرى. ويشمل ذلك وجود تطبيقات REST منفصلة للواجهة الأمامية والخلفية، يمكن تشغيلها كتطبيقات ويب من جانب العميل، أو تطبيقات ويب تقدمية (PWAs)، أو تطبيقات جوال أصلية.
تتضمن بعض الأدوات التي يمكنك استخدامها عند إنشاء تطبيقات أكثر تعقيدًا ما يلي:
- React هو إطار عمل JavaScript يسمح للمطورين ببناء صفحات ويب وصفحات أصلية لواجهات برمجة التطبيقات REST الخاصة بهم.
- Django هو إطار عمل ويب Python مجاني ومفتوح المصدر يتبع نمط بنية البرنامج Model-View-Controller (MVC).
- إطار عمل Django REST، مجموعة أدوات قوية ومرنة لبناء واجهات برمجة التطبيقات REST في Django.
في هذا البرنامج التعليمي، ستنشئ تطبيق ويب عصريًا بواجهة برمجة تطبيقات REST خلفية وأمامية منفصلة باستخدام React وDjango وإطار عمل Django REST. باستخدام React مع Django، يمكنك الاستفادة من أحدث التطورات في JavaScript وتطوير الواجهات الأمامية. بدلاً من بناء تطبيق Django باستخدام محرك قوالب مدمج، ستستخدم React كمكتبة واجهة مستخدم تستخدم نموذج كائن المستند الافتراضي (DOM)، ونهجًا تعريفيًا، ومكونات تُجري تغييرات على البيانات بسرعة.
سيُخزّن تطبيق الويب الذي ستُنشئه سجلات العملاء في قاعدة بيانات، ويمكنك استخدامها كنقطة انطلاق لتطبيق إدارة علاقات العملاء. عند الانتهاء، يمكنك القراءة والتحديث والحذف باستخدام واجهة React مُصممة باستخدام Bootstrap 4.
المتطلبات الأساسية
لإكمال هذا البرنامج التعليمي، ستحتاج إلى:
- جهاز تطوير مع Ubuntu 18.04.
- يتم تثبيت Python 3 وpip وvenv على جهازك باتباع الخطوتين 1 و2 من كيفية تثبيت Python 3 وإعداد بيئة تطوير محلية على Ubuntu 18.04.
- تم تثبيت Node.js الإصدار 6 أو أعلى وnpm الإصدار 5.2 أو أعلى على جهازك. يمكنك تثبيت كليهما باتباع التعليمات الواردة في "كيفية تثبيت Node.js على Ubuntu 18.04" ضمن قسم "التثبيت من PPA".
الخطوة 1 - إنشاء بيئة افتراضية لـ Python وتثبيت التبعيات
في هذه الخطوة، نقوم بإنشاء بيئة افتراضية وتثبيت التبعيات المطلوبة لتطبيقنا، بما في ذلك Django، وإطار عمل Django REST، وdjango-cors-headers.
سيستخدم تطبيقنا خادمي تطوير مختلفين لـ Django وReact. سيعملان على منافذ مختلفة ويعملان كنطاقين منفصلين. لهذا السبب، نحتاج إلى تفعيل مشاركة الموارد المتبادلة (CORS) لإرسال طلبات HTTP من React إلى Django دون حظرها بواسطة المتصفح.
انتقل إلى دليل منزلك وقم بإنشاء بيئة افتراضية باستخدام وحدة Python 3 venv:
cd ~
python3 -m venv ./envقم بتفعيل البيئة الافتراضية التي تم إنشاؤها باستخدام المصدر:
source env/bin/activateبعد ذلك، ثبّت تبعيات المشروع باستخدام pip. ستشمل هذه التبعيات ما يلي:
- Django: إطار عمل الويب للمشروع.
- إطار عمل Django REST: تطبيق تابع لجهة خارجية يقوم ببناء واجهات برمجة التطبيقات REST باستخدام Django.
- django-cors-headers: حزمة تعمل على تمكين CORS.
تثبيت إطار عمل Django:
pip install django djangorestframework django-cors-headersمن خلال تثبيت تبعيات المشروع، يمكنك إنشاء مشروع Django وواجهة React الأمامية.
الخطوة 2 – إنشاء مشروع Django
في هذه الخطوة، نقوم بإنشاء مشروع Django باستخدام الأوامر والأدوات التالية:
اسم مشروع بدء تشغيل django-admin: django-admin هي أداة سطر أوامر تُستخدم لتنفيذ مهام باستخدام Django. يُنشئ أمر بدء التشغيل مشروع Django جديدًا.
بايثون manager.py startapp myapp: manager.py هو سكربت يُضاف تلقائيًا إلى كل مشروع Django، ويُنفّذ عددًا من المهام الإدارية: إنشاء تطبيقات جديدة، ونقل قواعد البيانات، وخدمة مشروع Django محليًا. يُنشئ أمر startapp تطبيق Django داخل مشروع Django. في Django، يُشير مصطلح "تطبيق" إلى حزمة بايثون تُوفّر مجموعة من الميزات للمشروع.
للبدء، أنشئ مشروع Django باستخدام الأمر django-admin startproject. سنسمي مشروعنا djangoreactproject:
django-admin startproject djangoreactprojectقبل المضي قدمًا، دعنا نلقي نظرة على بنية الدليل لمشروع Django باستخدام أمر الشجرة.
انتقل إلى مجلد djangoreactproject في جذر مشروعك وقم بتشغيل أمر الشجرة:
cd ~/djangoreactproject
treeسوف ترى الناتج التالي:
Output
├── djangoreactproject
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.pyمجلد ~/djangoreactproject هو جذر المشروع. يحتوي هذا المجلد على العديد من الملفات المهمة لعملك:
- manager.py: برنامج نصي مساعد يقوم بتنفيذ عدد من المهام الإدارية.
- settings.py: ملف التكوين الرئيسي لمشروع Django، حيث يمكنك تغيير إعدادات المشروع. تتضمن هذه الإعدادات متغيرات مثل INSTALLED_APPS، وهي قائمة من السلاسل النصية التي تحدد التطبيقات النشطة لمشروعك. تحتوي وثائق Django على مزيد من المعلومات حول الإعدادات المتاحة.
- urls.py: يحتوي هذا الملف على قائمة بأنماط عناوين URL والعروض المرتبطة بها. يُظهر كل نمط علاقة بين عنوان URL والدالة التي يجب استدعاؤها لهذا العنوان. لمزيد من المعلومات حول عناوين URL والعروض، يُرجى الاطلاع على درسنا التعليمي حول كيفية إنشاء عروض Django.
خطوتنا الأولى في العمل على المشروع هي تهيئة الحزم التي ثبّتها في الخطوة السابقة، بما في ذلك إطار عمل Django REST وحزمة Django CORS، وذلك بإضافتها إلى ملف settings.py. افتح الملف باستخدام nano أو محرر النصوص المفضل لديك:
nano ~/djangoreactproject/djangoreactproject/settings.pyانتقل إلى إعدادات INSTALLED_APPS وأضف تطبيقات rest_framework وcorsheaders إلى نهاية القائمة:
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders'
]بعد ذلك، أضف البرنامج الوسيط corsheaders.middleware.CorsMiddleware من حزمة CORS المُثبّتة سابقًا إلى تهيئة MIDDLEWARE. هذه التهيئة عبارة عن قائمة بالبرامج الوسيطة، وهي فئة بايثون تحتوي على شيفرة تُعالَج في كل مرة يُجري فيها تطبيق الويب طلبًا أو استجابة.
...
MIDDLEWARE = [
...
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware'
]بعد ذلك، يمكنك تفعيل CORS. يُحدد إعداد CORS_ORIGIN_ALLOW_ALL ما إذا كنت تريد السماح بـ CORS لجميع النطاقات، وCORS_ORIGIN_WHITELIST عبارة عن قائمة بايثون تحتوي على عناوين URL المسموح بها. في حالتنا، بما أن خادم تطوير React سيعمل على http://localhost:3000، فسنضيف الإعدادين الجديدين CORS_ORIGIN_ALLOW_ALL = False وCORS_ORIGIN_WHITELIST('localhost:3000',) إلى ملف settings.py. أضف هذه الإعدادات في أي مكان في الملف:
...
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
'localhost:3000',
)
...يمكنك العثور على المزيد من خيارات التكوين في وثائق django-cors-headers.
احفظ الملف ثم اخرج من المحرر عند الانتهاء.
لا تزال في دليل ~/djangoreactproject، قم بإنشاء تطبيق Django جديد يسمى Customers:
python manage.py startapp customersيتضمن ذلك نماذج وعروض إدارة العملاء. تُعرّف النماذج حقول بيانات تطبيقنا وسلوكياتها، بينما تُمكّن العروض تطبيقنا من معالجة طلبات الويب بشكل صحيح وإرجاع الاستجابات المطلوبة.
بعد ذلك، أضف هذا التطبيق إلى قائمة التطبيقات المثبتة في ملف settings.py الخاص بمشروعك ليتعرف عليه Django كجزء من المشروع. افتح ملف settings.py مرة أخرى:
nano ~/djangoreactproject/djangoreactproject/settings.pyأضف تطبيق العميل:
...
INSTALLED_APPS = [
...
'rest_framework',
'corsheaders',
'customers'
]
...بعد ذلك، انقل قاعدة البيانات وابدأ تشغيل خادم التطوير المحلي. عمليات الترحيل هي طريقة Django لنشر التغييرات التي تُجريها على نماذجك في مخطط قاعدة البيانات. يمكن أن تشمل هذه التغييرات أشياءً مثل إضافة حقل أو حذف نموذج. لمزيد من المعلومات حول النماذج وعمليات الترحيل، راجع كيفية إنشاء نماذج Django.
نقل قاعدة البيانات:
python manage.py migrateبدء تشغيل خادم التطوير المحلي:
python manage.py runserverسوف ترى إخراجًا مشابهًا لما يلي:
Output
Performing system checks...
System check identified no issues (0 silenced).
October 22, 2018 - 15:14:50
Django version 2.1.2, using settings 'djangoreactproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.سيبدأ تطبيق الويب الخاص بك العمل من http://127.0.0.1:8000. عند زيارة هذا العنوان في متصفح الويب، ستظهر لك الصفحة التالية:
في هذه المرحلة، اترك البرنامج قيد التشغيل وافتح محطة طرفية جديدة لمواصلة تطوير المشروع.
الخطوة 3 – إنشاء واجهة React الأمامية
في هذا القسم، سنقوم بإنشاء تطبيق الواجهة الأمامية لمشروعنا باستخدام React.
لدى React أداة رسمية تُمكّنك من إنشاء مشاريع React بسرعة دون الحاجة إلى تهيئة Webpack مباشرةً. Webpack هو مُجمّع وحدات يُستخدم لتجميع أصول الويب، مثل أكواد JavaScript وCSS والصور. عادةً، ستحتاج إلى ضبط خيارات تهيئة مُختلفة قبل استخدام Webpack، ولكن بفضل أداة Create-react-app، لن تحتاج إلى التعامل مع Webpack مباشرةً إلا إذا كنت ترغب في مزيد من التحكم. لتشغيل Create-react-app، يمكنك استخدام npx، وهي أداة تُشغّل ملفات ثنائية مُجمّعة من npm.
في محطتك الثانية، تأكد من وجودك في دليل المشروع الخاص بك:
cd ~/djangoreactprojectقم بإنشاء مشروع React يسمى frontend باستخدام create-react-app و npx:
npx create-react-app frontendبعد ذلك، انتقل داخل تطبيق React الخاص بك وابدأ تشغيل خادم التطوير:
cd ~/djangoreactproject/frontend
npm startسيتم تشغيل تطبيقك من http://localhost:3000/:
اترك خادم تطوير React قيد التشغيل وافتح نافذة طرفية أخرى للمتابعة.
لرؤية هيكل الدليل للمشروع بأكمله في هذه المرحلة، انتقل إلى المجلد الجذر وقم بتشغيل الشجرة مرة أخرى:
cd ~/djangoreactproject
treeسوف ترى هيكلًا مثل هذا:
Output
├── customers
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── djangoreactproject
│ ├── __init__.py
│ ├── __pycache__
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── frontend
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── README.md
│ ├── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ └── registerServiceWorker.js
│ └── yarn.lock
└── manage.pyيستخدم تطبيقنا Bootstrap 4 لتصميم واجهة React، لذا سنضعه في ملف frontend/src/App.css، الذي سيُدير إعدادات CSS. افتح الملف:
nano ~/djangoreactproject/frontend/src/App.cssأضف الإدخال التالي إلى بداية الملف. يمكنك حذف محتويات الملف الحالية، مع أن هذا ليس ضروريًا:
@import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';هنا، @import هو تعليمة CSS تستخدم لاستيراد قواعد الأسلوب من أوراق الأنماط الأخرى.
الآن بعد أن قمنا بإنشاء كل من التطبيقات الخلفية والأمامية، فلنبدأ في إنشاء نموذج العميل وبعض البيانات التجريبية.
الخطوة 4 - إنشاء نموذج العميل والبيانات الأولية
بعد إنشاء تطبيق Django ونسخة React، ستكون خطوتنا التالية إنشاء نموذج العميل، الذي يمثل جدول قاعدة البيانات الذي يحتوي على معلومات العميل. لا تحتاج إلى أي SQL لأن Django Object Relational Mapper (ORM) يُجري عمليات قاعدة البيانات عن طريق ربط فئات ومتغيرات Python بجداول وأعمدة SQL. بهذه الطريقة، يُلخص Django ORM تفاعلات SQL مع قاعدة البيانات من خلال واجهة Python.
أعد تمكين بيئتك الافتراضية:
cd ~
source env/bin/activateانتقل إلى دليل العملاء وافتح models.py، وهو ملف Python يحتوي على نماذج تطبيقك:
cd ~/djangoreactproject/customers/
nano models.pyسيحتوي الملف على المحتوى التالي:
from django.db import models
# Create your models here.تم تضمين واجهة برمجة تطبيقات نموذج العميل في الملف بفضل عبارة استيراد النماذج من django.db. الآن، ستضيف فئة العميل، التي تمتد إلى models.Model. كل نموذج في Django هو فئة بايثون تمتد إلى django.db.models.Model.
سيحتوي نموذج العميل على حقول قاعدة البيانات التالية:
الاسم الأول- اسم العميل الأول.اسم العائلة- اسم العميل الأخير.بريد إلكتروني- عنوان البريد الإلكتروني للعميل.هاتف- رقم هاتف العميل.عنوان- عنوان العميل.وصف- وصف العميل.إنشاء في- تاريخ إضافة العميل.
نضيف أيضًا الدالة __str__()، التي تُحدد كيفية عرض النموذج. في حالتنا، سيكون الاسم الأول للعميل. لمزيد من المعلومات حول إنشاء الفئات وتعريف الكائنات، يُرجى مراجعة كيفية إنشاء الفئات وتعريف الكائنات في بايثون 3.
أضف الكود التالي إلى الملف:
from django.db import models
class Customer(models.Model):
first_name = models.CharField("First name", max_length=255)
last_name = models.CharField("Last name", max_length=255)
email = models.EmailField()
phone = models.CharField(max_length=20)
address = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
createdAt = models.DateTimeField("Created At", auto_now_add=True)
def __str__(self):
return self.first_nameبعد ذلك، هجّر قاعدة البيانات لإنشاء جداولها. يُنشئ أمر makemigrations ملفات الترحيل التي تُضاف إليها تغييرات النموذج، ويُطبّق الأمر migrate هذه التغييرات على قاعدة البيانات.
العودة إلى مجلد جذر المشروع:
cd ~/djangoreactprojectلإنشاء ملفات الهجرة، قم بتشغيل الأمر التالي:
python manage.py makemigrationsسوف تحصل على الناتج مثل هذا:
Output
customers/migrations/0001_initial.py
- Create model Customerتطبيق هذه التغييرات على قاعدة البيانات:
python manage.py migrateسوف ترى إخراجًا يشير إلى نجاح النقل:
Output
Operations to perform:
Apply all migrations: admin, auth, contenttypes, customers, sessions
Running migrations:
Applying customers.0001_initial... OKبعد ذلك، ستستخدم ملف نقل بيانات لإنشاء بيانات العميل الأولية. ملف نقل البيانات هو عملية ترحيل لإضافة أو تعديل البيانات إلى قاعدة البيانات. أنشئ ملف نقل بيانات فارغًا لتطبيق العملاء:
python manage.py makemigrations --empty --name customers customersستشاهد التأكيد التالي مع اسم ملف الهجرة الخاص بك:
Output
Migrations for 'customers':
customers/migrations/0002_customers.pyلاحظ أن اسم ملف الهجرة الخاص بك هو 0002_customers.py.
بعد ذلك، انتقل إلى مجلد الهجرة في تطبيق العميل:
cd ~/djangoreactproject/customers/migrationsافتح ملف الهجرة الذي تم إنشاؤه:
nano 0002_customers.pyهذا هو المحتوى الأولي للملف:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('customers', '0001_initial'),
]
operations = [
]تستورد عبارة الاستيراد واجهة برمجة التطبيقات Migrations، وهي واجهة برمجة تطبيقات Django لإنشاء عمليات الترحيل، من django.db، وهي حزمة مضمنة تحتوي على فئات للعمل مع قواعد البيانات.
فئة Migration هي فئة بايثون تصف العمليات التي تُجرى عند ترحيل قاعدة بيانات. وهي تمتد إلى migrations.Migration، وتحتوي على قائمتين:
التبعيات:يتضمن الهجرات التابعة.العمليات:يحتوي على العمليات التي يتم تنفيذها من خلال تطبيق عمليات الترحيل.
بعد ذلك، أضف طريقة لإنشاء بيانات عميل الاختبار. أضف الطريقة التالية قبل تعريف فئة الترحيل:
...
def create_data(apps, schema_editor):
Customer = apps.get_model('customers', 'Customer')
Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
...في هذه الطريقة، نأخذ فئة العميل الخاصة بتطبيق العميل الخاص بنا وننشئ عميل اختبار لإدراجه في قاعدة البيانات.
للحصول على فئة العميل، التي تسمح لنا بإنشاء عملاء جدد، نستخدم دالة get_model() لكائن التطبيقات. يمثل كائن التطبيقات سجل التطبيقات المثبتة ونماذج قواعد بياناتها.
عند استخدام دالة RunPython() لتنفيذ دالة create_data()، يتم تمرير كائن التطبيقات. أضف دالة migrations.RunPython() إلى قائمة العمليات الفارغة:
...
operations = [
migrations.RunPython(create_data),
]RunPython() جزء من واجهة برمجة تطبيقات الترحيل، يتيح لك تشغيل شيفرة بايثون مخصصة أثناء الترحيل. تُحدد قائمة عملياتنا أنه سيتم تنفيذ هذه الطريقة عند تطبيق الترحيل.
وهنا الملف الكامل:
from django.db import migrations
def create_data(apps, schema_editor):
Customer = apps.get_model('customers', 'Customer')
Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
class Migration(migrations.Migration):
dependencies = [
('customers', '0001_initial'),
]
operations = [
migrations.RunPython(create_data),
]لمزيد من المعلومات حول ترحيل البيانات، راجع وثائق Django Data Migration.
لنقل قاعدة البيانات الخاصة بك، ارجع أولاً إلى المجلد الرئيسي للمشروع:
cd ~/djangoreactprojectنقل قاعدة البيانات الخاصة بك لإنشاء بيانات الاختبار:
python manage.py migrateسوف ترى إخراجًا يؤكد الهجرة:
Output
Operations to perform:
Apply all migrations: admin, auth, contenttypes, customers, sessions
Running migrations:
Applying customers.0002_customers... OKبعد إنشاء نموذج العميل وبيانات التمثيل، يمكننا الانتقال إلى بناء واجهة برمجة التطبيقات REST.
الخطوة 5 - إنشاء واجهة برمجة تطبيقات REST
في هذه الخطوة، سننشئ واجهة برمجة تطبيقات REST باستخدام إطار عمل Django REST. سننشئ عدة عروض مختلفة لواجهات برمجة التطبيقات. عرض API هو دالة تعالج طلب أو استدعاء API، بينما نقطة نهاية API هي عنوان URL فريد يمثل نقطة اتصال مع نظام REST. على سبيل المثال، عندما يرسل مستخدم طلب GET إلى نقطة نهاية API، سيستدعي Django الدالة أو عرض API المقابل لمعالجة الطلب وإرجاع النتائج المحتملة.
سنستخدم أيضًا المُسلسلات. يسمح المُسلسل في إطار عمل Django REST بتحويل نماذج مُعقدة ومجموعات استعلام إلى صيغة JSON لاستخدامها في واجهة برمجة التطبيقات. يمكن لفئة المُسلسلات أيضًا العمل في الاتجاه المعاكس، حيث توفر آليات لتحليل البيانات وفصلها في نماذج Django ومجموعات استعلامها.
ستتضمن نقاط نهاية واجهة برمجة التطبيقات الخاصة بنا ما يلي:
- api/customers: يتم استخدام نقطة النهاية هذه لإنشاء العملاء وإرجاع مجموعات مقسمة إلى صفحات للعملاء.
- api/العملاء/ :يتم استخدام نقطة النهاية هذه للحصول على العملاء الفرديين وتحديثهم وحذفهم من خلال المفتاح الأساسي أو المعرف.
نقوم أيضًا بإنشاء عناوين URL في ملف urls.py الخاص بالمشروع لنقاط النهاية ذات الصلة (أي api/customers وapi/customers/ ).
لنبدأ بإنشاء فئة التسلسل لنموذج العميل الخاص بنا.
إضافة فئة تسلسلية
إنشاء فئة مُسلسل لنموذج العميل ضروري لتحويل مثيلات العميل ومجموعات الاستعلام من وإلى JSON. لإنشاء فئة المُسلسل، أنشئ أولًا ملف serializers.py داخل تطبيق العملاء:
cd ~/djangoreactproject/customers/
nano serializers.pyأضف الكود التالي لاستيراد النموذج التسلسلي ونموذج العميل لواجهة برمجة التطبيقات:
from rest_framework import serializers
from .models import Customerبعد ذلك، قم بإنشاء فئة مسلسلة تمتد إلى Serializers.ModelSerializer وتحدد الحقول التي سيتم تسلسلها:
...
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')تحدد فئة Meta النموذج والحقول للتسلسل: pk، first_name، last_name، email، phone، address، description.
وهنا المحتوى الكامل للملف:
from rest_framework import serializers
from .models import Customer
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')الآن بعد أن قمنا بإنشاء فئة المسلسل الخاصة بنا، يمكننا إضافة عروض API.
إضافة عروض API
في هذا القسم، سنقوم بإنشاء عروض API لتطبيقنا والتي سيتم استدعاؤها بواسطة Django عندما يزور المستخدم نقطة النهاية المقابلة لوظيفة العرض.
~/djangoreactproject/customers/views.py يفتح:
nano ~/djangoreactproject/customers/views.pyاحذف الواردات الموجودة وأضف الواردات التالية:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Customer
from .serializers import *نقوم باستيراد التسلسل الذي أنشأناه، إلى جانب نموذج العميل وواجهات برمجة التطبيقات الخاصة بإطار عمل Django REST.
بعد ذلك، أضف العرض لمعالجة طلبات HTTP POST وGET:
...
@api_view(['GET', 'POST'])
def customers_list(request):
"""
List customers, or create a new customer.
"""
if request.method == 'GET':
data = []
nextPage = 1
previousPage = 1
customers = Customer.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(customers, 10)
try:
data = paginator.page(page)
except PageNotAnInteger:
data = paginator.page(1)
except EmptyPage:
data = paginator.page(paginator.num_pages)
serializer = CustomerSerializer(data,context={'request': request} ,many=True)
if data.has_next():
nextPage = data.next_page_number()
if data.has_previous():
previousPage = data.previous_page_number()
return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
elif request.method == 'POST':
serializer = CustomerSerializer(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)أولاً، نستخدم مُزيِّن @api_view(['GET', 'POST']) لإنشاء عرض واجهة برمجة تطبيقات يقبل طلبات GET وPOST. المُزيِّن هو دالة تأخذ دالة أخرى وتوسِّعها ديناميكيًا.
في نص الطريقة، نستخدم المتغير request.method للتحقق من طريقة HTTP الحالية وتنفيذ المنطق المناسب وفقًا لنوع الطلب:
- إذا كان الطلب GET، فإن هذه الطريقة تُرتِّب البيانات باستخدام مُرَقِّم صفحات Django، وتُعيد الصفحة الأولى من البيانات بعد التسلسل، وعدد العملاء المتاحين، وعدد الصفحات المتاحة، وروابط الصفحات السابقة والتالية. مُرَقِّم الصفحات هو فئة مُدمجة في Django تُرتِّب قائمة البيانات في صفحات، وتُوفِّر طرقًا للوصول إلى عناصر كل صفحة.
- إذا كان الطلب POST، تُسلسل هذه الطريقة بيانات العميل المُستلَمة، ثم تستدعي دالة save() لكائن المُسلسل. تُرجع هذه الدالة كائن Response، وهو مثيل لـ HttpResponse، برمز حالة 201. كل ملف تعريف تُنشئه مسؤول عن إرجاع كائن HttpResponse. تحفظ دالة save() البيانات المُسلسلة في قاعدة البيانات.
لمزيد من المعلومات حول HttpResponse والعروض، راجع هذه المناقشة حول إنشاء وظائف العرض.
أضف الآن عرض API المسؤول عن معالجة طلبات GET وPUT وDELETE للحصول على العملاء وتحديثهم وحذفهم بواسطة pk (المفتاح الأساسي):
...
@api_view(['GET', 'PUT', 'DELETE'])
def customers_detail(request, pk):
"""
Retrieve, update or delete a customer by id/pk.
"""
try:
customer = Customer.objects.get(pk=pk)
except Customer.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = CustomerSerializer(customer,context={'request': request})
return Response(serializer.data)
elif request.method == 'PUT':
serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
customer.delete()
return Response(status=status.HTTP_204_NO_CONTENT)تم تزيين هذه الطريقة بـ @api_view(['GET', 'PUT', 'DELETE']) للإشارة إلى أنها طريقة عرض API يمكنها قبول طلبات GET وPUT وDELETE.
يتحقق الفحص الموجود في حقل request.method من طريقة الطلب ويستدعي المنطق الصحيح بناءً على قيمتها:
- إذا كان الطلب هو GET، يتم تسلسل بيانات العميل وإرسالها باستخدام كائن الاستجابة.
- إذا كان طلب PUT، تُنشئ هذه الطريقة مُسلسلاً لبيانات العميل الجديدة. ثم تستدعي طريقة save() كائن المُسلسل المُنشأ. وأخيرًا، تُرسل كائن استجابة مع العميل المُحدّث.
- إذا كان طلب DELETE، فإن طريقة delete() تستدعي طريقة delete() الخاصة بكائن العميل لحذفه، ثم تقوم بإرجاع كائن استجابة بدون بيانات.
يبدو الملف المكتمل بهذا الشكل:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Customer
from .serializers import *
@api_view(['GET', 'POST'])
def customers_list(request):
"""
List customers, or create a new customer.
"""
if request.method == 'GET':
data = []
nextPage = 1
previousPage = 1
customers = Customer.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(customers, 5)
try:
data = paginator.page(page)
except PageNotAnInteger:
data = paginator.page(1)
except EmptyPage:
data = paginator.page(paginator.num_pages)
serializer = CustomerSerializer(data,context={'request': request} ,many=True)
if data.has_next():
nextPage = data.next_page_number()
if data.has_previous():
previousPage = data.previous_page_number()
return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
elif request.method == 'POST':
serializer = CustomerSerializer(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)
@api_view(['GET', 'PUT', 'DELETE'])
def customers_detail(request, pk):
"""
Retrieve, update or delete a customer by id/pk.
"""
try:
customer = Customer.objects.get(pk=pk)
except Customer.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = CustomerSerializer(customer,context={'request': request})
return Response(serializer.data)
elif request.method == 'PUT':
serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
customer.delete()
return Response(status=status.HTTP_204_NO_CONTENT)الآن يمكننا الاستمرار في إنشاء نقاط النهاية الخاصة بنا.
إضافة نقاط نهاية API
الآن سنقوم بإنشاء نقاط نهاية واجهة برمجة التطبيقات: api/customers/، للاستعلام عن العملاء وإنشائهم، وapi/customers/ ، للحصول على عملاء فرديين أو تحديثهم أو حذفهم من خلال pk.
افتح ~/djangoreactproject/djangoreactproject/urls.py:
nano ~/djangoreactproject/djangoreactproject/urls.pyاترك ما هو موجود هناك، ولكن قم بإضافة الاستيراد إلى عرض العملاء في أعلى الملف:
from django.contrib import admin
from django.urls import path
from customers import views
from django.conf.urls import urlثم عناوين URL api/customers/ و api/customers/ أضف إلى قائمة أنماط عناوين URL التي تحتوي على عناوين URL للتطبيق:
...
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api/customers/$', views.customers_list),
url(r'^api/customers/(?P<pk>[0-9]+)$', views.customers_detail),
]بعد إنشاء نقاط نهاية REST، دعنا نرى كيف يمكننا استهلاكها.
الخطوة 6 - استخدام واجهة برمجة التطبيقات REST مع Axios
في هذه الخطوة، سنثبّت Axios، عميل HTTP الذي سنستخدمه لإجراء مكالمات API. سننشئ أيضًا فئةً لاستخدام نقاط نهاية API التي أنشأناها.
أولاً، قم بإلغاء تنشيط بيئتك الافتراضية:
deactivateثم انتقل إلى مجلد الواجهة الأمامية الخاص بك:
cd ~/djangoreactproject/frontendقم بتثبيت axios من npm باستخدام:
npm install axios --saveيضيف خيار –save تبعية axios إلى ملف package.json الخاص بالتطبيق الخاص بك.
بعد ذلك، أنشئ ملف JavaScript باسم CustomersService.js، والذي يحتوي على الكود اللازم لاستدعاء واجهات برمجة تطبيقات REST. سننشئه في مجلد src، حيث يوجد كود تطبيق مشروعنا:
cd src
nano CustomersService.jsأضف الكود التالي، الذي يحتوي على طرق للاتصال بـ Django REST API:
import axios from 'axios';
const API_URL = 'http://localhost:8000';
export default class CustomersService{
constructor(){}
getCustomers() {
const url = `${API_URL}/api/customers/`;
return axios.get(url).then(response => response.data);
}
getCustomersByURL(link){
const url = `${API_URL}${link}`;
return axios.get(url).then(response => response.data);
}
getCustomer(pk) {
const url = `${API_URL}/api/customers/${pk}`;
return axios.get(url).then(response => response.data);
}
deleteCustomer(customer){
const url = `${API_URL}/api/customers/${customer.pk}`;
return axios.delete(url);
}
createCustomer(customer){
const url = `${API_URL}/api/customers/`;
return axios.post(url,customer);
}
updateCustomer(customer){
const url = `${API_URL}/api/customers/${customer.pk}`;
return axios.put(url,customer);
}
}تستدعي فئة CustomersService طرق Axios التالية:
- getCustomers(): يحصل على الصفحة الأولى للعملاء.
- getCustomersByURL(): احصل على العملاء عبر عنوان URL. يتيح لك هذا الوصول إلى صفحات العملاء اللاحقة عبر تمرير روابط مثل /api/customers/?page=2.
- getCustomer(): يحصل على العميل حسب المفتاح الأساسي.
- createCustomer(): يقوم بإنشاء عميل.
- updateCustomer(): يقوم بتحديث العميل.
- deleteCustomer(): يحذف العميل.
يمكننا الآن عرض بيانات واجهة برمجة التطبيقات الخاصة بنا في واجهة مستخدم React من خلال إنشاء مكون CustomersList.
الخطوة 7 - عرض البيانات من واجهة برمجة التطبيقات في تطبيق React
في هذه الخطوة، سننشئ مكون React CustomersList. يُمثل مكون React جزءًا من واجهة المستخدم، ويتيح لك تقسيمها إلى أجزاء مستقلة قابلة لإعادة الاستخدام.
ابدأ بإنشاء CustomersList.js في frontend/src:
nano ~/djangoreactproject/frontend/src/CustomersList.jsابدأ باستيراد React وComponent لإنشاء مكون React:
import React, { Component } from 'react';بعد ذلك، قم باستيراد وإنشاء وحدة CustomersService التي أنشأتها في الخطوة السابقة، والتي توفر طرقًا للتواصل مع واجهة برمجة التطبيقات REST الخلفية:
...
import CustomersService from './CustomersService';
const customersService = new CustomersService();بعد ذلك، أنشئ مكون CustomersList الذي يمتد إلى Component لاستدعاء واجهة برمجة تطبيقات REST. يجب أن يمتد مكون React إلى فئة Component أو يُنشئ فئة فرعية منها. لمزيد من المعلومات حول فئات E6 والوراثة، يُرجى الاطلاع على درسنا حول فهم الفئات في JavaScript.
أضف الكود التالي لإنشاء مكون React يمتد إلى react.Component:
...
class CustomersList extends Component {
constructor(props) {
super(props);
this.state = {
customers: [],
nextPageURL: ''
};
this.nextPage = this.nextPage.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
}
export default CustomersList;داخل المُنشئ، نُهيئ كائن الحالة. يحتوي هذا الكائن على متغيرات حالة مُكوّننا باستخدام مصفوفة فارغة من العملاء. تحتوي هذه المصفوفة على العملاء وعنوان URL للصفحة التالية المطلوب استرجاعها من واجهة برمجة التطبيقات الداعمة. نُضيف أيضًا دالتَيْ nextPage() وhandleDelete() إلى هذا الكائن ليسهل الوصول إليهما من خلال كود HTML.
بعد ذلك، أضف طريقة componentDidMount() واستدعاءً لـ getCustomers() في فئة CustomersList، قبل الأقواس المتعرجة المغلقة.
طريقة componentDidMount() هي طريقة دورة حياة مكون يتم استدعاؤها عند إنشاء المكون وإدراجه في DOM. تستدعي getCustomers() كائن خدمة العملاء للحصول على الصفحة الأولى من البيانات ورابط الصفحة التالية من واجهة Django الخلفية:
...
componentDidMount() {
var self = this;
customersService.getCustomers().then(function (result) {
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}أضف الآن طريقة handleDelete()، التي تتعامل مع حذف العميل، أسفل componentDidMount():
...
handleDelete(e,pk){
var self = this;
customersService.deleteCustomer({pk : pk}).then(()=>{
var newArr = self.state.customers.filter(function(obj) {
return obj.pk !== pk;
});
self.setState({customers: newArr})
});
}تستدعي دالة handleDelete دالة deleteCustomer() لحذف العميل باستخدام المفتاح الأساسي (pk). في حال نجاح العملية، تُرشّح مصفوفة العملاء بحثًا عن العميل المحذوف.
بعد ذلك، أضف طريقة nextPage() للحصول على البيانات الخاصة بالصفحة التالية وتحديث رابط الصفحة التالية:
...
nextPage(){
var self = this;
customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}تستدعي طريقة nextPage() طريقة getCustomersByURL() التي تحصل على عنوان URL للصفحة التالية من كائن الحالة، this.state.nextPageURL، وتحديث مجموعة العملاء بالبيانات المرتجعة.
أخيرًا، أضف طريقة render() الخاصة بالمكون، والتي تقوم بعرض جدول العملاء من حالة المكون:
...
render() {
return (
<div className="customers--list">
<table className="table">
<thead key="thead">
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Phone</th>
<th>Email</th>
<th>Address</th>
<th>Description</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{this.state.customers.map( c =>
<tr key={c.pk}>
<td>{c.pk} </td>
<td>{c.first_name}</td>
<td>{c.last_name}</td>
<td>{c.phone}</td>
<td>{c.email}</td>
<td>{c.address}</td>
<td>{c.description}</td>
<td>
<button onClick={(e)=> this.handleDelete(e,c.pk) }> Delete</button>
<a href={"/customer/" + c.pk}> Update</a>
</td>
</tr>)}
</tbody>
</table>
<button className="btn btn-primary" onClick= { this.nextPage }>Next</button>
</div>
);
}وهنا المحتوى الكامل للملف:
import React, { Component } from 'react';
import CustomersService from './CustomersService';
const customersService = new CustomersService();
class CustomersList extends Component {
constructor(props) {
super(props);
this.state = {
customers: [],
nextPageURL: ''
};
this.nextPage = this.nextPage.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
componentDidMount() {
var self = this;
customersService.getCustomers().then(function (result) {
console.log(result);
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}
handleDelete(e,pk){
var self = this;
customersService.deleteCustomer({pk : pk}).then(()=>{
var newArr = self.state.customers.filter(function(obj) {
return obj.pk !== pk;
});
self.setState({customers: newArr})
});
}
nextPage(){
var self = this;
console.log(this.state.nextPageURL);
customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
self.setState({ customers: result.data, nextPageURL: result.nextlink})
});
}
render() {
return (
<div className="customers--list">
<table className="table">
<thead key="thead">
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Phone</th>
<th>Email</th>
<th>Address</th>
<th>Description</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{this.state.customers.map( c =>
<tr key={c.pk}>
<td>{c.pk} </td>
<td>{c.first_name}</td>
<td>{c.last_name}</td>
<td>{c.phone}</td>
<td>{c.email}</td>
<td>{c.address}</td>
<td>{c.description}</td>
<td>
<button onClick={(e)=> this.handleDelete(e,c.pk) }> Delete</button>
<a href={"/customer/" + c.pk}> Update</a>
</td>
</tr>)}
</tbody>
</table>
<button className="btn btn-primary" onClick= { this.nextPage }>Next</button>
</div>
);
}
}
export default CustomersList;الآن بعد أن قمنا بإنشاء مكون CustomersList لعرض قائمة العملاء، يمكننا إضافة مكون يتعامل مع إنشاء العملاء وتحديثهم.
الخطوة 8 - إضافة مكون إنشاء React وتحديث العميل
في هذه الخطوة، نُنشئ مُكوّن CustomerCreateUpdate، الذي يُعنى بإنشاء وتحديث بيانات العملاء. يتم ذلك من خلال توفير نموذج يُمكن للمستخدمين استخدامه لإدخال بيانات عملاء جدد أو تحديث بياناتهم الحالية.
في frontend/src، قم بإنشاء ملف CustomerCreateUpdate.js:
nano ~/djangoreactproject/frontend/src/CustomerCreateUpdate.jsأضف الكود التالي لإنشاء مكون React عن طريق استيراد React وComponent:
import React, { Component } from 'react';يمكننا أيضًا استيراد وإنشاء مثيل لفئة CustomersService التي أنشأناها في الخطوة السابقة، والتي توفر طرقًا تتواصل مع واجهة برمجة التطبيقات REST الخلفية:
...
import CustomersService from './CustomersService';
const customersService = new CustomersService();بعد ذلك، قم بإنشاء مكون CustomerCreateUpdate الذي يمتد إلى Component لإنشاء العملاء وتحديثهم:
...
class CustomerCreateUpdate extends Component {
constructor(props) {
super(props);
}
}
export default CustomerCreateUpdate;في تعريف الفصل، أضف طريقة render() الخاصة بالمكون، والتي تقوم بعرض نموذج HTML يلتقط معلومات العميل:
...
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>
First Name:</label>
<input className="form-control" type="text" ref='firstName' />
<label>
Last Name:</label>
<input className="form-control" type="text" ref='lastName'/>
<label>
Phone:</label>
<input className="form-control" type="text" ref='phone' />
<label>
Email:</label>
<input className="form-control" type="text" ref='email' />
<label>
Address:</label>
<input className="form-control" type="text" ref='address' />
<label>
Description:</label>
<textarea className="form-control" ref='description' ></textarea>
<input className="btn btn-primary" type="submit" value="Submit" />
</div>
</form>
);
}بالنسبة لكل عنصر إدخال نموذج، تضيف الطريقة سمة مرجع للوصول إلى قيمة عنصر النموذج وتعيينها.
بعد ذلك، قم بتعريف طريقة handleSubmit(event) أعلى طريقة render() للحصول على السلوك المناسب عندما ينقر المستخدم على زر الإرسال:
...
handleSubmit(event) {
const { match: { params } } = this.props;
if(params && params.pk){
this.handleUpdate(params.pk);
}
else
{
this.handleCreate();
}
event.preventDefault();
}
...تتولى دالة handleSubmit(event) معالجة إرسال النموذج، وبناءً على المسار، تستدعي إما دالة handleUpdate(pk) لتحديث العميل بالـ pk المُرسَل، أو دالة handleCreate() لإنشاء عميل جديد. سنُعرّف هاتين الدالتين قريبًا.
بالعودة إلى منشئ المكون، قم بربط طريقة handleSubmit() المضافة حديثًا بهذا حتى تتمكن من الوصول إليها في النموذج الخاص بك:
...
class CustomerCreateUpdate extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
...ثم عرّف دالة handleCreate() لإنشاء العميل من بيانات النموذج. أضف الكود التالي فوق دالة handleSubmit(event):
...
handleCreate(){
customersService.createCustomer(
{
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
}).then((result)=>{
alert("Customer created!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}
...تُستخدم دالة handleCreate() لإنشاء عميل من بيانات الإدخال. تستدعي هذه الدالة دالة CustomersService.createCustomer() المقابلة، والتي تستدعي بدورها واجهة برمجة التطبيقات (API) الفعلية إلى الواجهة الخلفية لإنشاء العميل.
بعد ذلك، أسفل طريقة handleCreate، قم بتعريف طريقة handleUpdate(pk) لتنفيذ التحديثات:
...
handleUpdate(pk){
customersService.updateCustomer(
{
"pk": pk,
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
}
).then((result)=>{
alert("Customer updated!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}تُحدِّث دالة updateCustomer() العميل باستخدام معلومات جديدة من نموذج معلومات العميل. وتستدعي دالة customersService.updateCustomer().
ثم أضف دالة componentDidMount(). إذا زار المستخدم المسار customer/:pk، فسنملأ النموذج بمعلومات العميل باستخدام المفتاح الأساسي من عنوان URL. للقيام بذلك، يمكننا إضافة دالة getCustomer(pk) بعد تثبيت المكون في حدث دورة حياة componentDidMount(). لإضافة هذه الدالة، أضف الكود التالي أسفل مُنشئ المكون:
...
componentDidMount(){
const { match: { params } } = this.props;
if(params && params.pk)
{
customersService.getCustomer(params.pk).then((c)=>{
this.refs.firstName.value = c.first_name;
this.refs.lastName.value = c.last_name;
this.refs.email.value = c.email;
this.refs.phone.value = c.phone;
this.refs.address.value = c.address;
this.refs.description.value = c.description;
})
}
}وهنا المحتوى الكامل للملف:
import React, { Component } from 'react';
import CustomersService from './CustomersService';
const customersService = new CustomersService();
class CustomerCreateUpdate extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount(){
const { match: { params } } = this.props;
if(params && params.pk)
{
customersService.getCustomer(params.pk).then((c)=>{
this.refs.firstName.value = c.first_name;
this.refs.lastName.value = c.last_name;
this.refs.email.value = c.email;
this.refs.phone.value = c.phone;
this.refs.address.value = c.address;
this.refs.description.value = c.description;
})
}
}
handleCreate(){
customersService.createCustomer(
{
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
}
).then((result)=>{
alert("Customer created!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}
handleUpdate(pk){
customersService.updateCustomer(
{
"pk": pk,
"first_name": this.refs.firstName.value,
"last_name": this.refs.lastName.value,
"email": this.refs.email.value,
"phone": this.refs.phone.value,
"address": this.refs.address.value,
"description": this.refs.description.value
}
).then((result)=>{
console.log(result);
alert("Customer updated!");
}).catch(()=>{
alert('There was an error! Please re-check your form.');
});
}
handleSubmit(event) {
const { match: { params } } = this.props;
if(params && params.pk){
this.handleUpdate(params.pk);
}
else
{
this.handleCreate();
}
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>
First Name:</label>
<input className="form-control" type="text" ref='firstName' />
<label>
Last Name:</label>
<input className="form-control" type="text" ref='lastName'/>
<label>
Phone:</label>
<input className="form-control" type="text" ref='phone' />
<label>
Email:</label>
<input className="form-control" type="text" ref='email' />
<label>
Address:</label>
<input className="form-control" type="text" ref='address' />
<label>
Description:</label>
<textarea className="form-control" ref='description' ></textarea>
<input className="btn btn-primary" type="submit" value="Submit" />
</div>
</form>
);
}
}
export default CustomerCreateUpdate;من خلال إنشاء مكون CustomerCreateUpdate، يمكننا تحديث مكون التطبيق الرئيسي لإضافة روابط إلى المكونات المختلفة التي أنشأناها.
الخطوة 9 - تحديث مكون التطبيق الرئيسي
في هذا القسم، سنقوم بتحديث مكون التطبيق الخاص بنا للارتباط بالمكونات التي أنشأناها في الخطوات السابقة.
من مجلد الواجهة الأمامية، قم بتشغيل الأمر التالي لتثبيت جهاز توجيه React، والذي يسمح لك بإضافة التوجيه والتنقل بين مكونات React المختلفة:
cd ~/djangoreactproject/frontend
npm install --save react-router-domبعد ذلك، افتح ~/djangoreactproject/frontend/src/App.js:
nano ~/djangoreactproject/frontend/src/App.jsاحذف جميع العناصر الموجودة وأضف الكود التالي لاستيراد الفئات اللازمة لإضافة التوجيه. هما BrowserRouter الذي يُنشئ مكون Router، وRoute الذي يُنشئ مكون Route:
import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom'
import { Route, Link } from 'react-router-dom'
import CustomersList from './CustomersList'
import CustomerCreateUpdate from './CustomerCreateUpdate'
import './App.css';يحافظ BrowserRouter على مزامنة واجهة المستخدم مع عنوان URL باستخدام واجهة برمجة تطبيقات سجل HTML5.
بعد ذلك، قم بإنشاء تخطيط أساسي يوفر المكون الأساسي الذي سيتم تغليفه بواسطة مكون BrowserRouter:
...
const BaseLayout = () => (
<div className="container-fluid">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand" href="#">Django React Demo</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<a className="nav-item nav-link" href="/">CUSTOMERS</a>
<a className="nav-item nav-link" href="/customer">CREATE CUSTOMER</a>
</div>
</div>
</nav>
<div className="content">
<Route path="/" exact component={CustomersList} />
<Route path="/customer/:pk" component={CustomerCreateUpdate} />
<Route path="/customer/" exact component={CustomerCreateUpdate} />
</div>
</div>
)نستخدم مكون "المسار" لتحديد مسارات تطبيقنا. وهو المكون الذي يجب على جهاز التوجيه تحميله فور العثور على تطابق. يتطلب كل مسار مسارًا لتحديد المسار المطلوب مطابقته، ومكونًا لتحديد المكون المطلوب مطابقته. تُحدد السمة "الدقيقة" جهاز التوجيه بمطابقة المسار بدقة.
أخيرًا، قم بإنشاء مكون التطبيق، وهو المكون الرئيسي أو المكون الأعلى مستوى لتطبيق React الخاص بنا:
...
class App extends Component {
render() {
return (
<BrowserRouter>
<BaseLayout/>
</BrowserRouter>
);
}
}
export default App;لقد قمنا بتغليف مكون BaseLayout مع مكون BrowserRouter لأن تطبيقنا سيتم تشغيله في المتصفح.
يبدو الملف المكتمل بهذا الشكل:
import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom'
import { Route, Link } from 'react-router-dom'
import CustomersList from './CustomersList'
import CustomerCreateUpdate from './CustomerCreateUpdate'
import './App.css';
const BaseLayout = () => (
<div className="container-fluid">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand" href="#">Django React Demo</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<a className="nav-item nav-link" href="/">CUSTOMERS</a>
<a className="nav-item nav-link" href="/customer">CREATE CUSTOMER</a>
</div>
</div>
</nav>
<div className="content">
<Route path="/" exact component={CustomersList} />
<Route path="/customer/:pk" component={CustomerCreateUpdate} />
<Route path="/customer/" exact component={CustomerCreateUpdate} />
</div>
</div>
)
class App extends Component {
render() {
return (
<BrowserRouter>
<BaseLayout/>
</BrowserRouter>
);
}
}
export default App;بعد إضافة التوجيه إلى تطبيقنا، أصبحنا الآن جاهزين لاختبار التطبيق. انتقل إلى http://localhost:3000. ستظهر لك الصفحة الرئيسية للتطبيق:
مع هذا البرنامج، أصبح لديك الآن الأساس لبرنامج CRM.
نتيجة
في هذا البرنامج التعليمي، أنشأتَ تطبيقًا تجريبيًا باستخدام Django وReact. استخدمتَ إطار عمل Django REST لبناء واجهة برمجة التطبيقات REST، وAxios لإدارة واجهة برمجة التطبيقات، وBootstrap 4 لتصميم CSS. يمكنكَ العثور على الكود المصدري لهذا المشروع في مستودع GitHub.












