ساخت برنامه todo با جنگو و react
November 2021

# معرفی
در این آموزش شما با استفاده از Django و React یک برنامه To-Do می سازید.
React یک چارچوب جاوا اسکریپت برای توسعه SPA (برنامه های تک صفحه ای) است. دارای اسناد محکم و اکوسیستم پر جنب و جوش در اطراف خود است.
جنگو یک چارچوب وب پایتون است که روش های رایج در توسعه وب را ساده می کند. جنگو قابل اعتماد است و همچنین دارای یک اکوسیستم پر جنب و جوش از کتابخانه های پایدار است که از نیازهای توسعه مشترک پشتیبانی می کند.
برای این برنامه، React بهعنوان فریمورک سمت کاربر یا فریمورک سمت کلاینت، مدیریت رابط کاربری و دریافت و تنظیم دادهها از طریق ریکوئستها به باطن جنگو، که یک API است که با استفاده از چارچوب Django REST (DRF) ساخته شده است، عمل میکند.
در پایان این آموزش، یک اپلیکیشن کاملاً کارآمد خواهید داشت:
سورس کد این پروژه را میتوانید از اینجا دانلود کنید.
برای شروع این پروژه باید پایتون و nodejs را در سیستمتان نصب داشته باشید.
دوره مرتبط: دوره اول آموزش جنگو(django)
# راه اندازی backend
در این بخش، یک فهرست پروژه جدید ایجاد کرده و جنگو را نصب خواهید کرد.
یک پنجره ترمینال جدید باز کنید و دستور زیر را برای ایجاد دایرکتوری پروژه جدید اجرا کنید:
mkdir django-todo-react
بعد، به دایرکتوری بروید:
cd django-todo-react
اکنون Pipenv را با استفاده از pip نصب کنید:
pip install pipenv
و یک محیط مجازی جدید را فعال کنید:
pipenv shell
Django را با استفاده از Pipenv نصب کنید:
pipenv install django
سپس یک پروژه جدید به نام backend ایجاد کنید:
django-admin startproject backend
در مرحله بعد، به دایرکتوری backend جدید ایجاد شده بروید:
cd backend
یک برنامه جدید به نام todo راه اندازی کنید:
python manage.py startapp todo
مایگریشنها را اجرا کنید:
python manage.py migrate
و سرور را راه اندازی کنید:
python manage.py runserver
در مرورگر وب خود به http://localhost:8000 بروید:
در این مرحله، نمونه ای از برنامه جنگو را مشاهده خواهید کرد که با موفقیت اجرا می شود. پس از اتمام کار، می توانید سرور را متوقف کنید (CONTROL+C یا CTRL+C).
دوره مرتبط: دوره آموزش django rest framework
+ رجیستر کردن اپ todo
اکنون که تنظیمات بکاند را تکمیل کردهاید، میتوانید برنامه todo را بهعنوان یک برنامه نصبشده ثبت کنید تا جنگو بتواند آن را تشخیص دهد.
فایل backend/settings.py را در ویرایشگر کد خود باز کنید و todo را به INSTALLED_APPS اضافه کنید:
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todo', # <---- new
]
+ ساخت مدل todo
بیایید یک مدل برای تعریف نحوه ذخیره موارد Todo در پایگاه داده ایجاد کنیم.
فایل todo/models.py را در ویرایشگر کد خود باز کنید و خطوط کد زیر را اضافه کنید:
from django.db import models
# Create your models here.
class Todo(models.Model):
title = models.CharField(max_length=120)
description = models.TextField()
completed = models.BooleanField(default=False)
def _str_(self):
return self.title
قطعه کد بالا سه ویژگی را در مدل Todo توضیح می دهد:
- title
- description
- completed
فیلد completed وضعیت یک task را مشخص میکند. یک کار یا در هر زمانی تکمیل می شود یا تکمیل نمی شود. از آنجا که شما یک مدل Todo ایجاد کرده اید، باید یک فایل مایگریت ایجاد کنید:
python manage.py makemigrations todo
و تغییرات را در پایگاه داده اعمال کنید:
python manage.py migrate todo
میتوانید آزمایش کنید تا ببینید که عملیات CRUD روی مدل Todo که ایجاد کردهاید با استفاده از رابط مدیریتی که جنگو بهطور پیشفرض ارائه میکند، کار میکند.
فایل todo/admin.py را با ویرایشگر کد خود باز کنید و خطوط کد زیر را اضافه کنید:
from django.contrib import admin
from .models import Todo
class TodoAdmin(admin.ModelAdmin):
list_display = ('title', 'description', 'completed')
# Register your models here.
admin.site.register(Todo, TodoAdmin)
برای دسترسی به ادمین پنل باید یک حساب کاربری “superuser” ایجاد کنید. دستور زیر را در ترمینال خود اجرا کنید:
python manage.py createsuperuser
از شما خواسته می شود نام کاربری، ایمیل و رمز عبور superuser را وارد کنید. حتما جزئیاتی را وارد کنید که بتوانید به خاطر بسپارید زیرا برای ورود به داشبورد مدیریت به آنها نیاز خواهید داشت.
یک بار دیگر سرور را راه اندازی کنید:
python manage.py runserver
در مرورگر وب خود به http://localhost:8000/admin بروید. و با نام کاربری و رمز عبوری که قبلا ایجاد شده بود وارد شوید:
با استفاده از این رابط می توانید موارد Todo را ایجاد، ویرایش و حذف کنید:
پس از کار با این رابط، می توانید سرور را متوقف کنید (CONTROL+C یا CTRL+C).
مقاله پیشنهادی: اجرای جنگو با پستگرس، nginx و gunicorn
# ساخت api
در این بخش با استفاده از فریمورک Django REST یک API ایجاد خواهید کرد.
djangorestframework و django-cors-headers را با استفاده از Pipenv نصب کنید:
pipenv install djangorestframework django-cors-headers
شما باید rest_framework و corsheaders را به لیست برنامه های نصب شده اضافه کنید. فایل backend/settings.py را در ویرایشگر کد خود باز کنید و بخش های INSTALLED_APPS و MIDDLEWARE را به روز کنید:
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders', # <--- new
'rest_framework', # <--- new
'todo',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware', # <--- new
]
سپس، این خطوط کد را به پایین فایل backend/settings.py اضافه کنید:
CORS_ORIGIN_WHITELIST = [
'http://localhost:3000'
]
django-cors-headers یک کتابخانه پایتون است که از خطاهایی که معمولاً به دلیل قوانین CORS دریافت می کنید جلوگیری می کند. در کد CORS_ORIGIN_WHITELIST، مقدار localhost:3000 را در لیست سفید قرار دادید، زیرا میخواهید قسمت جلویی (که در آن پورت ارائه میشود) برنامه با API تعامل داشته باشد.
مقاله پیشنهادی: یونیکد و رمزگذاری کاراکتر در پایتون
+ ساخت سریالایزر
برای تبدیل نمونههای مدل به JSON به سریالسازهایی نیاز دارید تا frontend بتواند با دادههای دریافتی کار کند.
با ویرایشگر کد خود یک فایل todo/serializers.py ایجاد کنید. فایل serializers.py را باز کرده و با خطوط کد زیر به روز کنید:
from rest_framework import serializers
from .models import Todo
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ('id', 'title', 'description', 'completed')
این کد مدلی برای کار و فیلدهایی که باید به JSON تبدیل شوند را مشخص می کند.
+ ساخت view
شما باید یک کلاس TodoView در فایل todo/views.py ایجاد کنید.
فایل todo/views.py را با ویرایشگر کد خود باز کنید و خطوط کد زیر را اضافه کنید:
from django.shortcuts import render
from rest_framework import viewsets # <--- new
from .serializers import TodoSerializer # <--- new
from .models import Todo # <--- new
# Create your views here.
class TodoView(viewsets.ModelViewSet): # <--- new
serializer_class = TodoSerializer # <--- new
queryset = Todo.objects.all() # <--- new
کلاس viewsets پایه اجرای عملیات CRUD را به طور پیش فرض فراهم می کند. این کد serializer_class و queryset را مشخص می کند.
فایل backend/urls.py را با ویرایشگر کد خود باز کنید و محتوای آن را با خطوط کد زیر جایگزین کنید:
from django.contrib import admin
from django.urls import path, include # <--- new
from rest_framework import routers # <--- new
from todo import views # <--- new
router = routers.DefaultRouter() # <--- new
router.register(r'todos', views.TodoView, 'todo') # <--- new
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)), # <--- new
]
این کد مسیر URL را برای API مشخص می کند. این آخرین مرحله ای بود که ساخت API را تکمیل کرد.
اکنون می توانید عملیات CRUD را روی مدل Todo انجام دهید. کلاس روتر به شما امکان می دهد پرس و جوهای زیر را ایجاد کنید:
- /todos/ : لیستی از تمام موارد Todo را برمی گرداند. عملیات CREATE و READ را می توان در اینجا انجام داد.
- /todos/id/ : یک مورد Todo را با استفاده از کلید اصلی id برمی گرداند. عملیات UPDATE و DELETE را می توان در اینجا انجام داد.
بیایید سرور را دوباره راه اندازی کنیم:
python manage.py runserver
در مرورگر وب خود به http://localhost:8000/api/todos بروید:
می توانید با استفاده از رابط کاربری، یک مورد جدید Todo ایجاد کنید:
اگر مورد Todo با موفقیت ایجاد شود، یک پاسخ موفقیت آمیز به شما نشان داده می شود:
همچنین میتوانید عملیات DELETE و UPDATE را روی موارد خاص Todo با استفاده از کلیدهای اصلی شناسه انجام دهید. از ساختار آدرس /api/todos/{id} استفاده کنید و یک id ارائه دهید.
1 را به URL اضافه کنید تا مورد Todo را با شناسه "1" بررسی کنید. در مرورگر وب خود به http://localhost:8000/api/todos/1 بروید:
این کار ساخت backend برنامه را تکمیل می کند.
مقاله پیشنهادی: آموزش کار با mysql در پایتون
# راه اندازی front-end
اکنون که بکاند برنامه را کامل کردهاید، میتوانید فرانتاند را ایجاد کنید و از طریق api که ایجاد کردهاید با بکاند ارتباط برقرار کنید.
ابتدا یک پنجره ترمینال جدید باز کنید و به دایرکتوری پروژه django-todo-react بروید.
برای راه اندازی frontend، این آموزش بر روی Create React App تکیه می کند. چندین روش برای استفاده از Creative-react-app وجود دارد. یک روش استفاده از npx برای اجرای بسته و ایجاد پروژه است:
npx create-react-app frontend
پس از ایجاد پروژه، می توانید به دایرکتوری frontend تازه ایجاد شده تغییر دهید:
cd frontend
سپس، برنامه را شروع کنید:
npm start
مرورگر وب شما http://localhost:3000 را باز می کند و با صفحه پیش فرض Create React App نمایش داده می شود:
در مرحله بعد، bootstrap و reactstrap را برای ارائه ابزارهای رابط کاربری نصب کنید.
npm install bootstrap@4.6.0 reactstrap@8.9.0 --legacy-peer-deps
index.js را در ویرایشگر کد خود باز کنید و bootstrap.min.css را اضافه کنید:
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css'; <--- new
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
اگر در این مرحله مشکل دارید، می توانید به اسناد رسمی برای اضافه کردن بوت استرپ مراجعه کنید.
App.js را در ویرایشگر کد خود باز کنید و خطوط کد زیر را اضافه کنید:
import React, { Component } from "react";
const todoItems = [
{
id: 1,
title: "Go to Market",
description: "Buy ingredients to prepare dinner",
completed: true,
},
{
id: 2,
title: "Study",
description: "Read Algebra and History textbook for the upcoming test",
completed: false,
},
{
id: 3,
title: "Sammy's books",
description: "Go to library to return Sammy's books",
completed: true,
},
{
id: 4,
title: "Article",
description: "Write article on how to use Django with React",
completed: false,
},
];
class App extends Component {
constructor(props) {
super(props);
this.state = {
viewCompleted: false,
todoList: todoItems,
};
}
displayCompleted = (status) => {
if (status) {
return this.setState({ viewCompleted: true });
}
return this.setState({ viewCompleted: false });
};
renderTabList = () => {
return (
<div className="nav nav-tabs">
<span
className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
onClick={() => this.displayCompleted(true)}
>
Complete
</span>
<span
className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
onClick={() => this.displayCompleted(false)}
>
Incomplete
</span>
</div>
);
};
renderItems = () => {
const { viewCompleted } = this.state;
const newItems = this.state.todoList.filter(
(item) => item.completed == viewCompleted
);
return newItems.map((item) => (
<li
key={item.id}
className="list-group-item d-flex justify-content-between align-items-center"
>
<span
className={`todo-title mr-2 ${
this.state.viewCompleted ? "completed-todo" : ""
}`}
title={item.description}
>
{item.title}
</span>
<span>
<button
className="btn btn-secondary mr-2"
>
Edit
</button>
<button
className="btn btn-danger"
>
Delete
</button>
</span>
</li>
));
};
render() {
return (
<main className="container">
<h1 className="text-white text-uppercase text-center my-4">Todo app</h1>
<div className="row">
<div className="col-md-6 col-sm-10 mx-auto p-0">
<div className="card p-3">
<div className="mb-4">
<button
className="btn btn-primary"
>
Add task
</button>
</div>
{this.renderTabList()}
<ul className="list-group list-group-flush border-top-0">
{this.renderItems()}
</ul>
</div>
</div>
</div>
</main>
);
}
}
export default App;
این کد شامل مقادیر کدگذاری شده برای چهار مورد است. این مقادیر موقتی خواهند بود تا زمانی که آیتمها از backend واکشی شوند.
تابع renderTabList () دو دهانه را ارائه می کند که به کنترل مجموعه ای از آیتم ها کمک می کند. با کلیک بر روی تب Completed کارهای انجام شده نمایش داده می شود. با کلیک بر روی تب Incomplete، وظایف ناقص نمایش داده می شود.
تغییرات خود را ذخیره کنید و برنامه را در مرورگر وب خود مشاهده کنید:
برای انجام اقداماتی مانند افزودن و ویرایش کارها، باید یک جزء modal ایجاد کنید.
ابتدا یک پوشه کامپوننت در دایرکتوری src ایجاد کنید:
mkdir src/components
سپس، یک فایل Modal.js ایجاد کنید و آن را با ویرایشگر کد خود باز کنید. خطوط کد زیر را اضافه کنید:
import React, { Component } from "react";
import {
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter,
Form,
FormGroup,
Input,
Label,
} from "reactstrap";
export default class CustomModal extends Component {
constructor(props) {
super(props);
this.state = {
activeItem: this.props.activeItem,
};
}
handleChange = (e) => {
let { name, value } = e.target;
if (e.target.type === "checkbox") {
value = e.target.checked;
}
const activeItem = { ...this.state.activeItem, [name]: value };
this.setState({ activeItem });
};
render() {
const { toggle, onSave } = this.props;
return (
<Modal isOpen={true} toggle={toggle}>
<ModalHeader toggle={toggle}>Todo Item</ModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="todo-title">Title</Label>
<Input
type="text"
id="todo-title"
name="title"
value={this.state.activeItem.title}
onChange={this.handleChange}
placeholder="Enter Todo Title"
/>
</FormGroup>
<FormGroup>
<Label for="todo-description">Description</Label>
<Input
type="text"
id="todo-description"
name="description"
value={this.state.activeItem.description}
onChange={this.handleChange}
placeholder="Enter Todo description"
/>
</FormGroup>
<FormGroup check>
<Label check>
<Input
type="checkbox"
name="completed"
checked={this.state.activeItem.completed}
onChange={this.handleChange}
/>
Completed
</Label>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button
color="success"
onClick={() => onSave(this.state.activeItem)}
>
Save
</Button>
</ModalFooter>
</Modal>
);
}
}
این کد یک کلاس CustomModal ایجاد می کند و مؤلفه Modal را که از کتابخانه reactstrap مشتق شده است، تودرتو می سازد.
این کد همچنین سه فیلد را به شکل زیر تعریف کرده است:
- title
- description
- completed
اینها همان فیلدهایی هستند که به عنوان ویژگی های مدل Todo در backend تعریف کردیم.
CustomModal ActiveItem، toggle و onSave را به عنوان props دریافت می کند:
- activeItem نشان دهنده مورد Todo برای ویرایش است.
- toggle تابعی است که برای کنترل وضعیت Modal استفاده می شود.
- onSave تابعی است که برای ذخیره مقادیر ویرایش شده مورد Todo فراخوانی می شود.
سپس، کامپوننت CustomModal را به فایل App.js وارد خواهید کرد.
فایل src/App.js را با ویرایشگر کد خود مجدداً مشاهده کنید و کل محتویات را با خطوط کد زیر جایگزین کنید:
import React, { Component } from "react";
import Modal from "./components/Modal";
const todoItems = [
{
id: 1,
title: "Go to Market",
description: "Buy ingredients to prepare dinner",
completed: true,
},
{
id: 2,
title: "Study",
description: "Read Algebra and History textbook for the upcoming test",
completed: false,
},
{
id: 3,
title: "Sammy's books",
description: "Go to library to return Sammy's books",
completed: true,
},
{
id: 4,
title: "Article",
description: "Write article on how to use Django with React",
completed: false,
},
];
class App extends Component {
constructor(props) {
super(props);
this.state = {
viewCompleted: false,
todoList: todoItems,
modal: false,
activeItem: {
title: "",
description: "",
completed: false,
},
};
}
toggle = () => {
this.setState({ modal: !this.state.modal });
};
handleSubmit = (item) => {
this.toggle();
alert("save" + JSON.stringify(item));
};
handleDelete = (item) => {
alert("delete" + JSON.stringify(item));
};
createItem = () => {
const item = { title: "", description: "", completed: false };
this.setState({ activeItem: item, modal: !this.state.modal });
};
editItem = (item) => {
this.setState({ activeItem: item, modal: !this.state.modal });
};
displayCompleted = (status) => {
if (status) {
return this.setState({ viewCompleted: true });
}
return this.setState({ viewCompleted: false });
};
renderTabList = () => {
return (
<div className="nav nav-tabs">
<span
className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
onClick={() => this.displayCompleted(true)}
>
Complete
</span>
<span
className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
onClick={() => this.displayCompleted(false)}
>
Incomplete
</span>
</div>
);
};
renderItems = () => {
const { viewCompleted } = this.state;
const newItems = this.state.todoList.filter(
(item) => item.completed === viewCompleted
);
return newItems.map((item) => (
<li
key={item.id}
className="list-group-item d-flex justify-content-between align-items-center"
>
<span
className={`todo-title mr-2 ${
this.state.viewCompleted ? "completed-todo" : ""
}`}
title={item.description}
>
{item.title}
</span>
<span>
<button
className="btn btn-secondary mr-2"
onClick={() => this.editItem(item)}
>
Edit
</button>
<button
className="btn btn-danger"
onClick={() => this.handleDelete(item)}
>
Delete
</button>
</span>
</li>
));
};
render() {
return (
<main className="container">
<h1 className="text-white text-uppercase text-center my-4">Todo app</h1>
<div className="row">
<div className="col-md-6 col-sm-10 mx-auto p-0">
<div className="card p-3">
<div className="mb-4">
<button
className="btn btn-primary"
onClick={this.createItem}
>
Add task
</button>
</div>
{this.renderTabList()}
<ul className="list-group list-group-flush border-top-0">
{this.renderItems()}
</ul>
</div>
</div>
</div>
{this.state.modal ? (
<Modal
activeItem={this.state.activeItem}
toggle={this.toggle}
onSave={this.handleSubmit}
/>
) : null}
</main>
);
}
}
export default App;
تغییرات خود را ذخیره کنید و برنامه را در مرورگر وب خود مشاهده کنید:
اگر بخواهید یک مورد Todo را ویرایش و ذخیره کنید، یک هشدار دریافت خواهید کرد که شی مورد Todo را نشان می دهد. با کلیک بر روی ذخیره یا حذف، اقدامات مربوطه در مورد Todo انجام می شود.
اکنون، برنامه را طوری تغییر میدهید که با API جنگو که در بخش قبلی ساختهاید، تعامل داشته باشد. اولین پنجره ترمینال را مجدداً مشاهده کنید و مطمئن شوید که سرور در حال اجرا است. اگر اجرا نمی شود از دستور زیر استفاده کنید:
python manage.py runserver
برای درخواست به API در سرور backend، یک کتابخانه جاوا اسکریپت به نام axios نصب خواهید کرد.
در پنجره ترمینال دوم، مطمئن شوید که در دایرکتوری frontend هستید و axios را نصب کنید:
npm install axios@0.21.1
سپس فایل frontend/package.json را در ویرایشگر کد خود باز کنید و یک پروکسی اضافه کنید:
[...]
"name": "frontend",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:8000",
"dependencies": {
"axios": "^0.18.0",
"bootstrap": "^4.1.3",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-scripts": "2.0.5",
"reactstrap": "^6.5.0"
},
[...]
پروکسی به تونل کردن درخواستهای API به http://localhost:8000 کمک میکند، جایی که برنامه جنگو آنها را مدیریت میکند. بدون این پروکسی، باید مسیرهای کامل را مشخص کنید:
axios.get("http://localhost:8000/api/todos/")
با پروکسی می توانید مسیرهای نسبی را ارائه دهید:
axios.get("/api/todos/")
فایل frontend/src/App.js را مجدداً مشاهده کنید و آن را با ویرایشگر کد خود باز کنید. در این مرحله، todoItems هاردکد شده را حذف میکنید و از دادههای درخواستها به سرور backend استفاده میکنید. handleSubmit و handleDelete
فایل App.js را باز کنید و آن را با این نسخه نهایی جایگزین کنید:
import React, { Component } from "react";
import Modal from "./components/Modal";
import axios from "axios";
class App extends Component {
constructor(props) {
super(props);
this.state = {
viewCompleted: false,
todoList: [],
modal: false,
activeItem: {
title: "",
description: "",
completed: false,
},
};
}
componentDidMount() {
this.refreshList();
}
refreshList = () => {
axios
.get("/api/todos/")
.then((res) => this.setState({ todoList: res.data }))
.catch((err) => console.log(err));
};
toggle = () => {
this.setState({ modal: !this.state.modal });
};
handleSubmit = (item) => {
this.toggle();
if (item.id) {
axios
.put(`/api/todos/${item.id}/`, item)
.then((res) => this.refreshList());
return;
}
axios
.post("/api/todos/", item)
.then((res) => this.refreshList());
};
handleDelete = (item) => {
axios
.delete(`/api/todos/${item.id}/`)
.then((res) => this.refreshList());
};
createItem = () => {
const item = { title: "", description: "", completed: false };
this.setState({ activeItem: item, modal: !this.state.modal });
};
editItem = (item) => {
this.setState({ activeItem: item, modal: !this.state.modal });
};
displayCompleted = (status) => {
if (status) {
return this.setState({ viewCompleted: true });
}
return this.setState({ viewCompleted: false });
};
renderTabList = () => {
return (
<div className="nav nav-tabs">
<span
onClick={() => this.displayCompleted(true)}
className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
>
Complete
</span>
<span
onClick={() => this.displayCompleted(false)}
className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
>
Incomplete
</span>
</div>
);
};
renderItems = () => {
const { viewCompleted } = this.state;
const newItems = this.state.todoList.filter(
(item) => item.completed === viewCompleted
);
return newItems.map((item) => (
<li
key={item.id}
className="list-group-item d-flex justify-content-between align-items-center"
>
<span
className={`todo-title mr-2 ${
this.state.viewCompleted ? "completed-todo" : ""
}`}
title={item.description}
>
{item.title}
</span>
<span>
<button
className="btn btn-secondary mr-2"
onClick={() => this.editItem(item)}
>
Edit
</button>
<button
className="btn btn-danger"
onClick={() => this.handleDelete(item)}
>
Delete
</button>
</span>
</li>
));
};
render() {
return (
<main className="container">
<h1 className="text-white text-uppercase text-center my-4">Todo app</h1>
<div className="row">
<div className="col-md-6 col-sm-10 mx-auto p-0">
<div className="card p-3">
<div className="mb-4">
<button
className="btn btn-primary"
onClick={this.createItem}
>
Add task
</button>
</div>
{this.renderTabList()}
<ul className="list-group list-group-flush border-top-0">
{this.renderItems()}
</ul>
</div>
</div>
</div>
{this.state.modal ? (
<Modal
activeItem={this.state.activeItem}
toggle={this.toggle}
onSave={this.handleSubmit}
/>
) : null}
</main>
);
}
}
export default App;
تابع refreshList() قابل استفاده مجدد است که هر بار که درخواست API تکمیل می شود فراخوانی می شود. لیست Todo را برای نمایش جدیدترین لیست موارد اضافه شده به روز می کند.
تابع handleSubmit () هم از عملیات ایجاد و هم بهروزرسانی مراقبت میکند. اگر مورد ارسال شده به عنوان پارامتر دارای id نباشد، احتمالاً ایجاد نشده است، بنابراین تابع آن را ایجاد می کند.
در این مرحله، بررسی کنید که سرور backend شما در اولین پنجره ترمینال شما اجرا می شود:
python manage.py runserver
و در پنجره ترمینال دوم خود، اطمینان حاصل کنید که در دایرکتوری frontend هستید و برنامه frontend خود را شروع کنید:
npm start
اکنون وقتی با مرورگر وب خود از http://localhost:3000 بازدید می کنید، برنامه شما به شما امکان می دهد وظایف را بخوانید، ایجاد کنید، به روز کنید و حذف کنید.
این قسمت فرانتند و بکاند برنامه Todo را تکمیل میکند.
# نتیجه گیری
در این مقاله، شما یک برنامه To-Do با استفاده از Django و React ساختید. شما با کتابخانه های djangorestframework، django-cors-headers، axios، bootstrap و reactstrap به این مهم دست یافتید.