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

November 2021

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

 

 #  معرفی

در این آموزش شما با استفاده از Django و React یک برنامه To-Do می سازید.

 

React یک چارچوب جاوا اسکریپت برای توسعه SPA (برنامه های تک صفحه ای) است. دارای اسناد محکم و اکوسیستم پر جنب و جوش در اطراف خود است.

 

جنگو یک چارچوب وب پایتون است که روش های رایج در توسعه وب را ساده می کند. جنگو قابل اعتماد است و همچنین دارای یک اکوسیستم پر جنب و جوش از کتابخانه های پایدار است که از نیازهای توسعه مشترک پشتیبانی می کند.

 

برای این برنامه، React به‌عنوان فریم‌ورک سمت کاربر یا فریم‌ورک سمت کلاینت، مدیریت رابط کاربری و دریافت و تنظیم داده‌ها از طریق ریکوئست‌ها به باطن جنگو، که یک API است که با استفاده از چارچوب Django REST (DRF) ساخته شده است، عمل می‌کند.

 

در پایان این آموزش، یک اپلیکیشن کاملاً کارآمد خواهید داشت:

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

 

سورس کد این پروژه را میتوانید از اینجا دانلود کنید.

 

برای شروع این پروژه باید پایتون و 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 توضیح می دهد:

 

فیلد 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 انجام دهید. کلاس روتر به شما امکان می دهد پرس و جوهای زیر را ایجاد کنید:

 

بیایید سرور را دوباره راه اندازی کنیم:

python manage.py runserver

 

در مرورگر وب خود به http://localhost:8000/api/todos بروید:

دریافت تمام اطلاعات از api جنگو

 

می توانید با استفاده از رابط کاربری، یک مورد جدید Todo ایجاد کنید:

ساخت یک todo جدید از api جنگو

 

اگر مورد Todo با موفقیت ایجاد شود، یک پاسخ موفقیت آمیز به شما نشان داده می شود:

پاسخ موفقیت آمیز از api جنگو

 

همچنین می‌توانید عملیات DELETE و UPDATE را روی موارد خاص Todo با استفاده از کلیدهای اصلی شناسه انجام دهید. از ساختار آدرس /api/todos/{id} استفاده کنید و یک id ارائه دهید.

 

1 را به URL اضافه کنید تا مورد Todo را با شناسه "1" بررسی کنید. در مرورگر وب خود به http://localhost:8000/api/todos/1 بروید:

دریافت یک آیتم خاص از api جنگو

 

این کار ساخت 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 نمایش داده می شود:

ایجاد برنامه react برای ارتباط با جنگو

 

در مرحله بعد، 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، وظایف ناقص نمایش داده می شود.

 

تغییرات خود را ذخیره کنید و برنامه را در مرورگر وب خود مشاهده کنید:

راه اندازی برنامه react

 

برای انجام اقداماتی مانند افزودن و ویرایش کارها، باید یک جزء 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 مشتق شده است، تودرتو می سازد.

 

این کد همچنین سه فیلد را به شکل زیر تعریف کرده است:

 

اینها همان فیلدهایی هستند که به عنوان ویژگی های مدل Todo در backend تعریف کردیم.

 

CustomModal ActiveItem، toggle و onSave را به عنوان props دریافت می کند:

 

سپس، کامپوننت 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;

 

تغییرات خود را ذخیره کنید و برنامه را در مرورگر وب خود مشاهده کنید:

برنامه نهایی react برای ارتباط با جنگو

 

اگر بخواهید یک مورد 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 بازدید می کنید، برنامه شما به شما امکان می دهد وظایف را بخوانید، ایجاد کنید، به روز کنید و حذف کنید.

 

اتمام برنامه ارتباط جنگو با react

 

این قسمت فرانتند و بک‌اند برنامه Todo را تکمیل می‌کند.

 

 

 #  نتیجه گیری

در این مقاله، شما یک برنامه To-Do با استفاده از Django و React ساختید. شما با کتابخانه های djangorestframework، django-cors-headers، axios، bootstrap و reactstrap به این مهم دست یافتید.

 

مقالات مرتبط

GraphQL چیست؟

پایتون در توسعه وب: مزایا و معایب و فریم ورک ها

جنگو چیست و چرا برای ساخت وب‌سایت فوق‌العاده است؟

ده سایت معروفی که با جنگو ساخته شده اند