ساخت اپلیکیشن ToDo با Django و React – از صفر تا صد


در این راهنما با شیوه ساخت یک اپلیکیشن ToDo به صورت CRUD با استفاده از فریمورک Django REST برای بکاند و ریاکت و ریداکس برای فرانتاند آشنا خواهیم شد. این راهنما برای افراد مبتدی طراحی شده است و همه مفاهیم به صورت گام به گام و تفصیلی همراه با نمونه کدهای مربوطه توضیح داده شده است. با ما همراه باشید تا روش ساخت اپلیکیشن ToDo با Django و React را بیاموزید.
در انتهای این راهنما یک اپلیکیشن به صورت تصویر زیر خواهیم داشت:
تنظیم Django
در ابتدا باید Django را برای ساخت اپلیکیشن تنظیم کنیم. در این بخش مراحل این تنظیم را با هم مرور خواهیم کرد.
ایجاد محیط مجازی با Pipenv
قبل از هر چیز یک پوشه برای پروژه میسازیم و به آن میرویم:
$ mkdir django-react-todo $ cd django-react-todo
با اجرای دستور زیر نیز یک محیط مجازی ایجاد میکنیم:
$ pipenv --python 3
اگر هنوز Pipenv را روی سیستم خود نصب نکردهاید، ابتدا آن را با اجرای دستور زیر نصب کنید:
$ pip install pipenv
به این ترتیب پکیجهای مورد نیاز را نصب خواهیم کرد:
$ pipenv install django djangorestframework
ایجاد پروژه جدید و برخی اپلیکیشنها
در این راهنما قصد داریم پروژهای به نام todocrud ایجاد کنیم. میتوانیم پوشه اضافی که به صورت خودکار ایجاد شده را با اضافه کردن یک نقطه به انتهای دستور و اجرا کردنش ترک کنیم:
$ django-admin startproject todocrud.
سپس دو اپلیکیشن ایجاد خواهیم کرد. یکی برای بکاند و دیگری برای فرانتاند:
$ python manage.py startapp todos $ python manage.py startapp frontend
فایل settings.py را در دایرکتوری پروژه باز کنید و آن را برای استفاده از اپلیکیشنهایی که ایجاد کردیم و فریمورک Django REST پیکربندی کنید:
ما میتوانیم قالب خروجی تاریخ و زمان را با قرار دادن DATETIME_FORMAT در یک دیکشنری پیکربندی به نام REST_FRAMEWORK تعیین کنیم.
اینک میگریشنها را اعمال کرده و سرور توسعه را آغاز میکنیم:
$ python manage.py migrate $ python manage.py runserver
در مرورگر به آدرس http://127.0.0.1:8000/ بروید. اگر صفحهای دیدید که یک راکت در حال اوج گرفتن است، به این معنی است که همه چیز به درستی کار میکند.
نوشتن ماژولهای بکاند
ابتدا یک مدل ساده ایجاد میکنیم. فایل models.py را باز کرده و کد زیر را در آن بنویسید:
سپس یک API مدل-بکاند ساده با استفاده از فریمورک REST میسازیم. به این منظور یک پوشه به نام api ایجاد میکنیم و فایلهای جدید با نامهای __init__.py, serializers.py, views.py و urls.py را در آن میسازیم.
todos/ api/ __init__.py serializers.py urls.py views.py
از آنجا که api یک ماژول است، باید فایل init__.py__ را نیز include کنیم. در ادامه بازنمایی API را در فایل serializers.py تعریف میکنیم.
کلاس ModelSerializer فیلدهایی را ایجاد میکند که متناظر با فیلدهای مدل هستند. سپس رفتار را در فایل api/views.py تعریف میکنیم:
در نهایت پیکربندی URL را با استفاده از Routers مینویسیم:
ما از سه آرگومان برای متد ()register استفاده میکنیم، اما آرگومان سوم الزامی نیست.
نوشتن ماژولهای فرانتاند
در فرانتاند تنها کاری که باید انجام دهیم، نوشتن نماها-(Views)-ی ساده و الگوهای URL است.
فایل frontend/views.py را باز و دو نما ایجاد کنید:
فایل frontend/index.html را در ادامه ایجاد خواهیم کرد. فعلاً نگران آن نباشید. یک فایل به نام urls.py به همان دایرکتوری اضافه کنید و پیکربندی URL را ایجاد نمایید:
چنان که در فایل فوق میبینید، نمای index برای صفحه اندیس است و TodoDetailView نیز زمانی فراخوانی میشود که یک شیء خاص درخواست شده باشد.
اتصال URL-ها
URL-های بکاند و فرانتاند را در URLconf پروژه میگنجانیم:
با این که بخش مربوط به ادمین سایت جنگو نیز آمده است؛ اما در این راهنما از آن استفاده نخواهیم کرد.
به عنوان آخرین نکته در این بخش یک فایل میگریشن ایجاد میکنیم و با اجرای دستور زیر تغییرها را روی پایگاههای داده اعمال میکنیم:
$ python manage.py makemigrations $ python manage.py migrate
تنظیم ریاکت
در این بخش با مراحل تنظیم اولیه فرانتاند و فریمورک ریاکت آشنا میشویم.
ایجاد دایرکتوریها
قبل از هر چیز باید همه دایرکتوریهای مورد نیاز خود را ایجاد کنیم:
$ mkdir -p./frontend/src/{components,actions,reducers} $ mkdir -p./frontend/{static,templates}/frontend
دستور فوق باید دایرکتوریهایی به شرح زیر ایجاد کند:
frontend/ src/ actions/ components/ reducers/ static/ frontend/ templates/ frontend/
نصب پکیجها
در این بخش باید با اجرای دستور زیر پیش از نصب پکیجها، یک فایل به نام package.json ایجاد کنیم:
$ npm init –y
برای استفاده از npm باید Node.js روی سیستم نصب باشد. سپس باید همه پکیجها را با دستور npm زیر نصب کنیم:
$ npm i -D webpack webpack-cli $ npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties $ npm i react react-dom react-router-dom $ npm i redux react-redux redux-thunk redux-devtools-extension $ npm i redux-form $ npm i axios $ npm i lodash
ایجاد فایلهای پیکربندی
یک فایل به نام .babelrc به دایرکتوری ریشه اضافه کرده و Babel را پیکربندی میکنیم:
میتوانیم با نوشتن کدی مانند فوق، از Async/Await به همراه Babel استفاده کنیم. در وهله دوم یک فایل به نام webpack.config.js به همان دایرکتوری اضافه میکنیم و یک پیکربندی برای webpack مینویسیم:
به علاوه، باید مشخصه "scripts” مربوط به فایل package.json را بازنویسی کنیم:
به این ترتیب دو اسکریپت جدید تعریف شدهاند. این اسکریپتها را میتوانیم با استفاده از دستور npm run dev برای توسعه و یا دستور npm run build برای پروداکشن اجرا کنیم. زمانی که این اسکریپتها اجرا شوند، وبپک ماژولها را بستهبندی کرده و در فایل main.js عرضه میکند.
ایجاد فایلهای اصلی
در این بخش سه فایل اصلی را ایجاد کرده و نخستین کلمه را رندر میکنیم. یک فایل به نام index.js میسازیم که ابتدا زمانی که اپلیکیشن ریاکت اجرا شود، فراخوانی خواهد شد:
سپس یک فایل به نام App.js ایجاد میکنیم که کامپوننت والد است:
در نهایت یک فایل قالب به نام index.html ایجاد میکنیم که در فایل views.py ذکر شده است:
ما در این راهنما از Semantic UI به عنوان فریمورک CSS استفاده میکنیم. یک پوشش برای رندر کردن کامپوننت App قرار میدهیم و اسکریپت باندلشده را درون تگ <body> قرار میدهیم.
بررسی نمایش
در این بخش بررسی میکنیم که آیا شیوه نمایش صحیح است یا نه. به این منظور ترمینال را باز کرده و اسکریپت را اجرا میکنیم:
$ npm run dev
با اجرای دستور فوق، فایل main.js باید در دایرکتوری static/frontend ایجاد شده باشد. سپس سرور توسعه را آغاز کرده و به نشانی http://127.0.0.1:8000/ میرویم:
$ python manage.py runserver
اگر کلمه $ python manage.py runserver روی صفحه نمایش یافته باشد، یعنی همه چیز تا به اینجا به درستی پیش رفته است.
دریافت دادهها از API و نمایش لیست
اینک زمان آن رسیده است که از Redux استفاده کنیم. در این بخش «اکشن» (Action)، «ردیوسر» (Reducer) و «استور» (Store) را ایجاد خواهیم کرد.
اکشنها
ابتدا همه مشخصههای نوع را از قبل تعریف میکنیم. یک فایل جدید به نام types.js به دایرکتوری src/actions اضافه کنید:
برای ایجاد اکشنها باید «اکشنساز» (Action Creator) را مشخص کنیم. یک فایل جدید به نام todos.js به دایرکتوری src/actions اضافه کنید:
ردیوسرها
ردیوسرها شیوه تغییر یافتن «حالت» (State) اپلیکیشن را در پاسخ به اکشنهای ارسالی به استور تعیین میکنند.
یک فایل جدید به نام todos.js به دایرکتوری src/reducers اضافه کرده و یک ردیوسر فرزند را در آن مینویسیم:
Lodash (+) یک کتابخانه کاربردی جاوا اسکریپت است. استفاده از آن الزامی نیست، اما میتواند زمان توسعه را کاهش داده و حجم کدبیس را کوچکتر سازد.
در این بخش یک ردیوسر والد میسازیم تا همه ردیوسرهای فرزند را با استفاده از ()combineReducers گرد هم جمع کنیم. یک فایل جدید به نام index.js به دایرکتوری src/reducers اضافه کنید:
برای استفاده از redux-form باید ردیوسر آن را در تابع combineReducers قرار دهیم.
استور
استور (Store) شیئی است که حالت اپلیکیشن را نگهداری میکند. به علاوه از میانافزار توصیهشده به نام Redux Thunk برای نوشتن منطق ناهمگام استفاده میکنیم که با استور تعامل خواهد داشت. در این بخش یک فایل جدید به نام store.js در دایرکتوری src ایجاد میکنیم:
استفاده از Redux DevTools اختیاری است، اما کاربرد آن بسیار مفید است، زیرا تغییرهای حالت ریداکس را بصریسازی میکند. در این راهنما به توضیح شیوه استفاده از آن نمیپردازیم، اما در کل بسیار مفید است.
کامپوننتها
ابتدا یک پوشه به نام todos در دایرکتوری components ایجاد میکنیم. سپس فایل جدید به نام TodoList.js را به این پوشه ایجادشده اضافه میکنیم:
ما از Semantic UI برای تزیین لیست استفاده میکنیم. تابع ()connect این کامپوننت را به استور وصل میکند. این تابع مقدار mapStateToProps را به عنوان آرگومان نخست میگیرد. اکشنساز نیز آرگومان دوم آن است. میتوانیم از حالت استور به عنوان Props برای تعیین mapStateToProps استفاده کنیم.
به این منظور یک فایل جدید به نام Dashboard.js در همان دایرکتوری ایجاد میکنیم. این فایل صرفاً یک کانتینر برای TodoList و یک فرم است که در بخش بعدی خواهیم ساخت:
فایل App.js را باز کنید و به صورت زیر بهروزرسانی نمایید:
Provider موجب میشود که استور در اختیار کامپوننت تودرتوی درون آن قرار بگیرد.
بررسی نمایش
ابتدا به نشانی http://127.0.0.1:8000/api/todos بروید و چند شیء ایجاد کنید. سپس به نشانی http://127.0.0.1:8000 بروید. اینک باید یک لیست ساده از اشیایی که ایجاد کردید ببینید.
ایجاد فرم و افزودن ToDo جدید
در این بخش مراحل ساخت فرم مربوط به وارد کردن موارد ToDo جدید را با هم مرور میکنیم.
اکشنها
فایل actions/todos.js را باز کرده و یک اکشنساز جدید به آن اضافه کنید:
دیسپچ کردن ('reset('formName موجب پاک شدن فرم پس از تحویل موفق میشود. ما نام فرم را متعاقباً در کامپوننت Form تعیین خواهیم کرد.
ردیوسرها
فایل reducers/todos.js را باز کنید و یک اکشن جدید به ردیوسر اضافه کنید:
کامپوننتها
در این بخش ابتدا یک کامپوننت Form ایجاد میکنیم. یک فرم به صورت مجزا به عنوان کامپوننت با استفاده مجدد ایجاد میکنیم، به طوری که بتوان از آن برای ویرایش کردن نیز استفاده کرد. یک فایل جدید به نام TodoForm.js در دایرکتوری components/todos ایجاد کنید:
با توجه به این که این راهنما نسبتاً طولانی است، در مورد شیوه استفاده از Redux Form توضیح ارائه نمیکنیم. برای درک طرز کار فرمهای ریداکس، بهتر است تلاش کنید با استفاده از مستندات رسمی (+) آن را سفارشیسازی کنید.
نام این فرم 'todoForm' است و آن را در اکشنسازی به نام addTodo استفاده خواهیم کرد.
زمانی که روی کادر متنی کلیک کنید و سپس فوکوس را از روی آن بردارید، یک خطای اعتبارسنجی نمایش میدهد. برای غیر فعال کردن آن باید از تنظیمات touchOnBlur: false استفاده کنیم.
سپس یک کامپوننت برای افزودن ToDo-های جدید اضافه میکنیم. یک فایل جدید به نام TodoCreate.js در دایرکتوری components/todos اضافه میکنیم:
تنها کاری که باید انجام دهیم این است که TodoForm را رندر کنیم. با تعیین مقدار False برای destroyOnUnmount، میتوانیم این که فرم ریداکس به صورت خودکار در زمان unmount شدن کامپوننت، حالت فرم را در استور ریداکس تخریب میکند، غیر فعال کنیم. این کار به منظور نمایش حالت فرم در فرم ویرایشی انجام میگیرد. اگر نیازی به تعیین یک تابع mapStateToProps نباشد، مقدار null برای ()connect وارد میکنیم. در ادامه فرم را مشاهده و امتحان میکنیم. به این منظور فایل Dashboard.js را باز کرده و به صورت زیر بهروزرسانی کنید:
ایجاد یک هدر
در این بخش اقدام به ساخت یک هدر میکنیم. پوشه جدیدی به نام layout بسازید و سپس یک فایل جدید به نام Header.js با محتوای زیر به آن اضافه کنید:
فایل App.js را باز کنید و کامپوننت Header را در آن به صورت تودرتو قرار دهید:
در این راهنما هدر صرفاً جنبه تزیینی دارد.
حذف کردن ToDo-ها
ابتدا یک شیء به نام history با استفاده از پکیج history ایجاد میکنیم. ما میتوانیم از آن برای تغییر دادن مکان کنونی استفاده کنیم. یک فایل جدید به نام history.js در دایرکتوری frontend/src ایجاد کرده و کد زیر را در آن مینویسیم:
اکشنها
فایل actions/todos.js را باز کنید و دو اکشنساز جدید به آن اضافه کنید:
getTodo را برای دریافت یک شیء خاص و getTodo را برای حذف آن شیء ایجاد کردهایم. در ادامه قصد داریم یک پنجره modal بری تأیید حذف نیز بسازیم. متد ('/')history.push به صورت خودکار پس از حذف کردن شیء ما را از پنجره modal به صفحه اندیس میبرد.
ردیوسرها
فایل reducers/todos.js را باز کرده و اکشنها را به ردیوسر اضافه کنید:
اکشن GET_TODO همان اکشن ADD_TODO است و تنها باید مقدار case را تعیین کنیم. برای اکشن DELETE_TODO نیز دوباره از Lodash به عنوان میانبر استفاده میکنیم.
کامپوننتها
در این بخش آن پنجره modal را که در بخش قبل اشاره کردیم میسازیم. ظاهر آن مانند زیر خواهد بود:
فایل جدیدی به نام Modal.js در دایرکتوری components/layout ایجاد کنید و کد زیر را در آن بنویسید:
برای رندر کردن کامپوننت modal در خارج از سلسله مراتب DOM کامپوننت والد، باید یک پورتال با استفاده از ()createPortal ایجاد کنیم. آرگومان نخست یک عنصر فرزند قابل رندر است و آرگومان دوم نیز آن عنصر DOM است که باید رندر شود. سپس فایل index.html را باز کرده و یک کانتینر برای modal درون تگ <bosy> اضافه میکنیم:
در ادامه یک کامپوننت جدید به نام TodoDelete.js در دایرکتوری components/todos ایجاد میکنیم:
این کد کمی طولانی است، اما دشواری خاصی ندارد. تابعهای کمکی که محتوا را نمایش میدهند و دکمههای اکشن روی پنجره modal تعریف شدهاند. سپس آنها را به صورت Props به کامپوننت Modal ارسال میکنیم. onDismiss طوری تنظیم شده است که وقتی روی بخش تاریک پنجره modal کلیک میشود به صفحه اندیس بازگردد.
دادهها را میتوانیم از props خودش با تعیین onDismiss به عنوان آرگومان دوم mapStateToProps بازیابی کنیم. در ادامه فایل TodoList.js را باز کرده و دکمه delete را در آن قرار میدهیم:
در نهایت، باید مسیریابی را با استفاده از React Router پیکربندی کنیم. فایل App.js را بازکرده و مانند زیر پیکربندی کنید:
دلیل استفاده از Router به جای BrowserRouter در مستندات تمرینات ریاکت به صورت زیر توضیح داده شده است:
رایجترین کاربرد استفاده از <Router> سطح پایین، همگامسازی یک تاریخچه سفارشی با یک کتابخانه مدیریت حالت مانند ریداکس یا Mobx است. توجه داشته باشید که این کار برای استفاده از کتابخانههای مدیریت حالت در کنار React Router ضرورتی ندارد و صرفاً به منظور یکپارچگی بیشتر صورت میگیرد. پارامتر exact که در Route تعیین شده است، تنها در صورتی که مسیر بازگشت میدهد که path دقیقاً با URL جاری مطابقت داشته باشد.
بدین ترتیب این بخش خاتمه مییابد. تلاش کنید اشیا را حذف کنید و نتیجه کار را مورد بررسی قرار دهید.
ویرایش کردن ToDo-ها
تا به اینجا اغلب بخشهای اپلیکیشن ما تکمیل شده است. در این بخش آخر، امکان ویرایش کردن ToDo-ها را نیز به اپلیکیشن خود اضافه میکنیم.
اکشنها
فایل actions/todos.js را باز کرده و یک اکشنساز جدید به آن اضافه کنید:
ردیوسرها
فایل reducers/todos.js را باز کنید و اکشن را به ردیوسر اضافه کنید:
کامپوننتها
یک کامپوننت جدید به نام TodoEdit.js در دایرکتوری components/todos بسازید:
یک شیء در initialValues تعیین کنید. ما تنها میتوانیم مقدار task را در تابع _.pick مربوط به Lodash دریافت کنیم. به علاوه مقدار enableReinitialize را روی True قرار دهید تا بتوانیم در زمان بارگذاری مجدد صفحه نیز این مقدار را دریافت کنیم. این مشخصههای اختیاری را به TodoForm ارسال کنید.
اکنون فایل TodoList.js را باز کرده و <a className='header'>{todo.task}</a> را به صورت زیر بهروزرسانی کنید:
یک کامپوننت جدید به فایل App.js اضافه میکنیم:
در نهایت، متن دکمه را روی فرم ادیت از Add به Update تغییر میدهیم. فایل TodoForm.js را باز کرده و به صورت زیر بهروزرسانی میکنیم:
اینک میتوانید روی هر وظیفهای در صفحه اندیس کلیک کرده و تلاش کنید آن را ویرایش کنید:
امکان تغییر دادن مقدار از سمت فرم نیز وجود دارد.
به این ترتیب به انتهای این راهنما میرسیم. سورس کد کامل این پروژه را میتوانید در این ریپوی گیتهاب (+) ملاحظه کنید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای جاوا اسکریپت
- مجموعه آموزشهای برنامهنویسی
- آموزش جنگو (Django) – فریمورک تحت وب با پایتون (Python)
- جنگو (Django) چیست؟ — از صفر تا صد
- ساخت یک CRM مقدماتی با Django و React روی اوبونتو ۱۸.۰۴ — از صفر تا صد
==