Thứ ba, 15/09/2020 | 00:00 GMT+7

Xây dựng ứng dụng Việc cần làm bằng Django và React

Trong hướng dẫn này, ta xây dựng ứng dụng Todo bằng Django và React.

React là một khuôn khổ JS tuyệt vời để phát triển các SPA ( s ingle p age a pplication) và nó có tài liệu vững chắc và một hệ sinh thái sôi động xung quanh nó.

Django là một khung công tác web Python giúp đơn giản hóa các phương pháp phổ biến trong phát triển web. Django đã xuất hiện được một thời gian, nghĩa là hầu hết các vấn đề và lỗi của gotcha đã được giải quyết, đồng thời có một bộ thư viện ổn định hỗ trợ các nhu cầu phát triển chung.

Đối với ứng dụng này, React đóng role là front-end hoặc client side framework, xử lý giao diện user và nhận và cài đặt dữ liệu thông qua các yêu cầu đến Django back-end , đây là một API được xây dựng bằng cách sử dụng Django REST framework (DRF).

Ở cuối hướng dẫn này, ta sẽ có ứng dụng cuối cùng trông giống như sau:

Mã nguồn cho hướng dẫn này có sẵn tại đây trên GitHub.

Yêu cầu

Để làm theo hướng dẫn này, bạn cần :

  1. Cài đặt và cài đặt môi trường lập trình local cho Python 3
  2. Cài đặt Node.js và Tạo môi trường phát triển local

Cài đặt chương trình backend

Trong phần này, ta sẽ cài đặt phần backend và tạo tất cả các folder mà ta cần để cài đặt và chạy mọi thứ, vì vậy hãy chạy một version mới của terminal và tạo folder của dự án bằng cách chạy lệnh sau:

$ mkdir django-todo-react 

Tiếp theo, ta sẽ chuyển vào folder :

 $ cd django-todo-react 

Bây giờ ta sẽ cài đặt Pipenv bằng cách sử dụng pip và kích hoạt một môi trường ảo mới:

$ pip install pipenv $ pipenv shell 

Lưu ý: Bạn nên bỏ qua lệnh đầu tiên nếu bạn đã cài đặt Pipenv.

Hãy cài đặt Django bằng Pipenv sau đó tạo một dự án mới có tên là backend :

$ pipenv install django $ django-admin startproject backend 

Tiếp theo, ta sẽ chuyển vào folder backend mới được tạo và bắt đầu một ứng dụng mới có tên là todo. Ta cũng sẽ chạy di chuyển và khởi động server :

$ cd backend $ python manage.py startapp todo $ python manage.py migrate $ python manage.py runserver 

Đến đây, nếu tất cả các lệnh đã được nhập chính xác, ta sẽ thấy một version của ứng dụng Django đang chạy trên địa chỉ này - http: // localhost: 8000

Đăng ký ứng dụng Todo

Ta đã hoàn tất việc cài đặt cơ bản cho phần backend , hãy bắt đầu với những thứ nâng cao hơn như đăng ký ứng dụng todo làm ứng dụng đã cài đặt để Django có thể nhận ra nó. Mở file backend/settings.py và cập nhật phần INSTALLED_APPS như sau:

    # backend/settings.py      # Application definition     INSTALLED_APPS = [         'django.contrib.admin',         'django.contrib.auth',         'django.contrib.contenttypes',         'django.contrib.sessions',         'django.contrib.messages',         'django.contrib.staticfiles',         'todo' # add this        ] 

Xác định mô hình Todo

Hãy tạo một mô hình để xác định cách các mục Todo sẽ được lưu trữ trong database , mở file todo/models.py và cập nhật nó bằng đoạn mã này:

    # todo/models.py      from django.db import models     # Create your models here.      # add this     class Todo(models.Model):       title = models.CharField(max_length=120)       description = models.TextField()       completed = models.BooleanField(default=False)        def _str_(self):         return self.title 

Đoạn mã ở trên mô tả ba thuộc tính trên mô hình Todo:

  • Tiêu đề

  • Sự miêu tả

  • Đã hoàn thành

Thuộc tính đã hoàn thành là trạng thái của một nhiệm vụ; một nhiệm vụ sẽ được hoàn thành hoặc không được hoàn thành bất cứ lúc nào. Vì ta đã tạo mô hình Todo, ta cần tạo file di chuyển và áp dụng các thay đổi cho database , vì vậy hãy chạy các lệnh sau:

$ python manage.py makemigrations todo $ python manage.py migrate todo 

Ta có thể kiểm tra để thấy rằng các hoạt động CRUD hoạt động trên mô hình Todo mà ta đã tạo bằng giao diện quản trị mà Django cung cấp ngay lập tức, nhưng trước tiên, ta sẽ thực hiện một chút cấu hình.

Mở file todo/admin.py và cập nhật nó cho phù hợp:

    # todo/admin.py      from django.contrib import admin     from .models import Todo # add this      class TodoAdmin(admin.ModelAdmin):  # add this       list_display = ('title', 'description', 'completed') # add this      # Register your models here.     admin.site.register(Todo, TodoAdmin) # add this 

Ta sẽ tạo một account superuser để truy cập giao diện quản trị bằng lệnh này:

$ python manage.py createsuperuser 

Bạn sẽ được yêu cầu nhập tên user , email và password cho superuser. Đảm bảo nhập các chi tiết mà bạn có thể nhớ vì bạn cần chúng để đăng nhập vào console quản trị trong thời gian ngắn.

Hãy khởi động server và đăng nhập vào địa chỉ - http: // localhost: 8000 / admin:

$ python manage.py runserver 

Ta có thể tạo, chỉnh sửa và xóa các mục Todo bằng giao diện này. Hãy tiếp tục và tạo một số:

Công việc tuyệt vời cho đến nay, hãy tự hào về những gì bạn đã làm! Trong phần tiếp theo, ta sẽ xem cách ta có thể tạo API bằng cách sử dụng khuôn khổ Django REST.

Cài đặt các API

Bây giờ, ta sẽ thoát khỏi server (CONTROL-C) sau đó cài đặt djangorestframeworkdjango-cors-headers bằng Pipenv:

$ pipenv install djangorestframework django-cors-headers 

Ta cần thêm rest_frameworkcorsheaders vào danh sách ứng dụng đã cài đặt, vì vậy hãy mở file backend/settings.py và cập nhật các phần INSTALLED_APPSMIDDLEWARE phù hợp:

    # backend/settings.py      # Application definition     INSTALLED_APPS = [         'django.contrib.admin',         'django.contrib.auth',         'django.contrib.contenttypes',         'django.contrib.sessions',         'django.contrib.messages',         'django.contrib.staticfiles',         'corsheaders',            # add this         'rest_framework',         # add this          'todo',       ]      MIDDLEWARE = [         'corsheaders.middleware.CorsMiddleware',    # add this         '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',     ] 

Thêm đoạn mã này vào cuối file backend/settings.py :

    # we whitelist localhost:3000 because that's where frontend will be served     CORS_ORIGIN_WHITELIST = (          'localhost:3000/'      ) 

Django-cors-headers là một thư viện python sẽ giúp ngăn ngừa các lỗi mà ta thường mắc phải do CORS. luật . Trong đoạn mã CORS_ORIGIN_WHITELIST , ta đưa localhost:3000 vào danh sách trắng vì ta muốn giao diện user (sẽ được phân phối trên cổng đó) của ứng dụng tương tác với API.

Tạo bộ tuần tự cho mô hình Todo

Ta cần trình tuần tự hóa để chuyển đổi các version mô hình thành JSON để giao diện user có thể làm việc với dữ liệu đã nhận một cách dễ dàng. Ta sẽ tạo một file todo/serializers.py :

$ touch todo/serializers.py 

Mở file serializers.py và cập nhật nó bằng mã sau.

    # todo/serializers.py      from rest_framework import serializers     from .models import Todo      class TodoSerializer(serializers.ModelSerializer):       class Meta:         model = Todo         fields = ('id', 'title', 'description', 'completed') 

Trong đoạn mã ở trên, ta đã chỉ định mô hình hoạt động và các trường ta muốn chuyển đổi thành JSON.

Tạo chế độ xem

Ta sẽ tạo một lớp TodoView trong file todo/views.py , vì vậy hãy cập nhật nó bằng mã sau:

    # todo/views.py      from django.shortcuts import render     from rest_framework import viewsets          # add this     from .serializers import TodoSerializer      # add this     from .models import Todo                     # add this      class TodoView(viewsets.ModelViewSet):       # add this       serializer_class = TodoSerializer          # add this       queryset = Todo.objects.all()              # add this 

Lớp cơ sở của bộ viewsets cung cấp việc triển khai các hoạt động CRUD theo mặc định, những gì ta phải làm là chỉ định lớp bộ tuần tự hóa và bộ truy vấn.

Đi tới file backend/urls.py và thay thế hoàn toàn bằng mã bên dưới. Mã này chỉ định đường dẫn URL cho API:

    # backend/urls.py      from django.contrib import admin     from django.urls import path, include                 # add this     from rest_framework import routers                    # add this     from todo import views                            # add this      router = routers.DefaultRouter()                      # add this     router.register(r'todos', views.TodoView, 'todo')     # add this      urlpatterns = [         path('admin/', admin.site.urls),         path('api/', include(router.urls))                # add this     ] 

Đây là bước cuối cùng hoàn thành việc xây dựng API, bây giờ ta có thể thực hiện các hoạt động CRUD trên mô hình Todo. Lớp bộ định tuyến cho phép ta thực hiện các truy vấn sau:

  • /todos/ - Điều này trả về danh sách tất cả các mục Todo (Bạn có thể thực hiện các thao tác Tạo và Đọc tại đây).

  • /todos/id - nó trả về một mục Todo duy nhất bằng cách sử dụng khóa chính id (Bạn có thể thực hiện thao tác Cập nhật và Xóa tại đây).

Hãy khởi động lại server và truy cập địa chỉ này - http: // localhost: 8000 / api / todos :

$ python manage.py runserver 

Ta có thể tạo một mục việc cần làm mới bằng giao diện:

Nếu mục Todo được tạo thành công, bạn sẽ thấy màn hình như sau:

Ta cũng có thể thực hiện các thao tác XÓA và CẬP NHẬT trên các mục Todo cụ thể bằng cách sử dụng khóa chính id của chúng. Để làm điều này, ta sẽ truy cập một địa chỉ có cấu trúc này /api/todos/id. Hãy thử với địa chỉ này - http: // localhost: 8000 / api / todos / 1 :

Đó là tất cả cho phần backend của ứng dụng, bây giờ ta có thể chuyển sang phần bổ sung cho giao diện user .

Cài đặt giao diện user

Ta có phần backend của ta đang chạy như bình thường, bây giờ ta sẽ tạo giao diện user của bạn và làm cho nó giao tiếp với phần backend qua giao diện mà ta đã tạo.

Vì ta đang xây dựng giao diện user của bạn bằng React, ta muốn sử dụng công cụ CLI create-react-app vì nó đăng ký các cài đặt tối ưu và một số lợi ích như Reload nóng và Công nhân dịch vụ. Ta sẽ cài đặt công cụ create-react-app CLI (giao diện dòng lệnh) trên phạm vi global bằng lệnh này:

$ npm install -g create-react-app 

Hãy chuyển trở lại folder làm việc chính - django-todo-react - của ứng dụng của ta và tạo một ứng dụng React mới có tên là frontend:

$ create-react-app frontend 

Có thể sẽ mất một lúc để cài đặt tất cả các phụ thuộc, khi quá trình này kết thúc, terminal của bạn sẽ trông giống như sau:

Chạy các lệnh sau để chuyển vào folder làm việc và khởi động server giao diện user

$ cd frontend $ yarn start 

Lưu ý: Nếu bạn chưa cài đặt Yarn, bạn có thể tìm hướng dẫn cài đặt tại đây.

Bây giờ ta có thể truy cập địa chỉ này - http: // localhost: 3000 - và ta sẽ thấy màn hình React mặc định:

Ta sẽ sử dụng bootstrapreactstrap để reactstrap giao diện user một chút:

$ yarn add bootstrap reactstrap 

Hãy mở file src/index.css và thay thế các kiểu ở đó bằng kiểu này:

  /__ frontend/src/index.css  __/      body {       margin: 0;       padding: 0;       font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",         "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",         sans-serif;       -webkit-font-smoothing: antialiased;       -moz-osx-font-smoothing: grayscale;       background-color: #282c34;     }     .todo-title {       cursor: pointer;     }     .completed-todo {       text-decoration: line-through;     }     .tab-list > span {       padding: 5px 8px;       border: 1px solid #282c34;       border-radius: 10px;       margin-right: 5px;       cursor: pointer;     }     .tab-list > span.active {       background-color: #282c34;       color: #ffffff;     } 

Ta sẽ nhập biểu định kiểu của Bootstrap trong src/index.js để ta có thể sử dụng các lớp của Bootstrap:

       // frontend/src/index.js        import React from 'react';       import ReactDOM from 'react-dom';       import 'bootstrap/dist/css/bootstrap.min.css';       // add this       import './index.css';       import App from './App';       import * as serviceWorker from './serviceWorker';        ReactDOM.render(<App />, document.getElementById('root'));       // If you want your app to work offline and load faster, you can change       // unregister() to register() below. Note this comes with some pitfalls.       // Learn more about service workers: http://bit.ly/CRA-PWA       serviceWorker.unregister(); 

Hãy thay thế mã trong src/App.js bằng mã này:

  // frontend/src/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 upcoming test",         completed: false       },       {         id: 3,         title: "Sally's books",         description: "Go to library to rent sally'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="my-5 tab-list">             <span               onClick={() => this.displayCompleted(true)}               className={this.state.viewCompleted ? "active" : ""}             >               complete             </span>             <span               onClick={() => this.displayCompleted(false)}               className={this.state.viewCompleted ? "" : "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"> Edit </button>               <button className="btn btn-danger">Delete </button>             </span>           </li>         ));       };       render() {         return (           <main className="content">             <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="">                     <button className="btn btn-primary">Add task</button>                   </div>                   {this.renderTabList()}                   <ul className="list-group list-group-flush">                     {this.renderItems()}                   </ul>                 </div>               </div>             </div>           </main>         );       }     }     export default App; 

Được rồi, đó là rất nhiều mã ?, nhưng không cần phải sợ bây giờ, ta chưa bắt đầu tương tác với API backend , vì vậy ta đã bao gồm các giá trị mặc định để điền vào danh sách Todo. Hàm `renderTabList ()` hiển thị hai nhịp giúp kiểm soát tập hợp các mục nào được hiển thị, tức là nhấp vào tab đã hoàn thành sẽ hiển thị các nhiệm vụ đã hoàn thành và tương tự đối với tab chưa hoàn thành.

Nếu ta truy cập vào ứng dụng React frontend bây giờ, nó sẽ giống như sau:

Để xử lý các hành động như thêm và chỉnh sửa các việc , ta sẽ sử dụng một phương thức, vì vậy hãy tạo một thành phần Phương thức trong một folder components .

Tạo một folder components trong folder src :

$ mkdir src/components 

Tạo Modal.js file trong folder thành phần:

$ touch src/components/Modal.js 

Mở file Modal.js và điền nó bằng đoạn mã bên dưới:

 // frontend/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="title">Title</Label>                   <Input                     type="text"                     name="title"                     value={this.state.activeItem.title}                     onChange={this.handleChange}                     placeholder="Enter Todo Title"                   />                 </FormGroup>                 <FormGroup>                   <Label for="description">Description</Label>                   <Input                     type="text"                     name="description"                     value={this.state.activeItem.description}                     onChange={this.handleChange}                     placeholder="Enter Todo description"                   />                 </FormGroup>                 <FormGroup check>                   <Label for="completed">                     <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>         );       }     } 

Ta đã tạo một lớp CustomModal và nó tổ chức thành phần Phương thức có nguồn root từ thư viện reactstrap . Ta cũng đã xác định ba trường trong biểu mẫu:

  • Tiêu đề

  • Sự miêu tả

  • Đã hoàn thành

Đây là những trường tương tự mà ta đã xác định là thuộc tính trên mô hình Todo trong phần backend .

Đây là cách CustomModal hoạt động, nó nhận activeItem , toggle và onSave dưới dạng đạo cụ.

  1. activeItem đại diện cho mục Todo được chỉnh sửa.
  2. toggle là một chức năng được sử dụng để kiểm soát trạng thái của Phương thức tức là mở hoặc đóng phương thức.
  3. onSave là một hàm được gọi để lưu các giá trị đã chỉnh sửa của mục Todo.

Tiếp theo, ta sẽ nhập thành phần CustomModal vào file App.js Đi tới file src/App.js và thay thế nó hoàn toàn bằng version cập nhật này:

  // frontend/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 upcoming test",         completed: false       },       {         id: 3,         title: "Sally's books",         description: "Go to library to rent sally'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 = {           modal: false,           viewCompleted: false,           activeItem: {             title: "",             description: "",             completed: false           },           todoList: todoItems         };       }       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="my-5 tab-list">             <span               onClick={() => this.displayCompleted(true)}               className={this.state.viewCompleted ? "active" : ""}             >               complete             </span>             <span               onClick={() => this.displayCompleted(false)}               className={this.state.viewCompleted ? "" : "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                 onClick={() => this.editItem(item)}                 className="btn btn-secondary mr-2"               >                 Edit               </button>               <button                 onClick={() => this.handleDelete(item)}                 className="btn btn-danger"               >                 Delete               </button>             </span>           </li>         ));       };       render() {         return (           <main className="content">             <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="">                     <button onClick={this.createItem} className="btn btn-primary">                       Add task                     </button>                   </div>                   {this.renderTabList()}                   <ul className="list-group list-group-flush">                     {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; 

Bây giờ ta có thể truy cập lại giao diện user của React, Đây là kết quả ứng dụng sẽ giống tại thời điểm này:

Nếu ta cố gắng chỉnh sửa và lưu một mục Todo, ta sẽ nhận được cảnh báo hiển thị đối tượng của mục Todo. Nhấp vào lưu và xóa sẽ thực hiện các thao tác điều chỉnh trên mục Todo.

Bây giờ ta sẽ sửa đổi ứng dụng để nó tương tác với API Django mà ta đã xây dựng trong phần trước. Hãy bắt đầu bằng cách khởi động server backend (trên một version khác của terminal ) nếu nó chưa chạy:

$ python manage.py runserver 

Lưu ý: Lệnh này phải được chạy trong folder ` backend ` trong shell Pipenv ảo.

Để ta thực hiện các yêu cầu đến các điểm cuối API trên server backend , ta sẽ cài đặt một thư viện JavaScript có tên là axios. Hãy kéo vào `axios bằng Yarn:

$ yarn add axios 

Khi axios được cài đặt thành công, hãy truy cập file frontend/package.json và thêm một proxy như sau:

      // 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"       },       [...] 

Proxy sẽ giúp chuyển các yêu cầu API tới http: // localhost: 8000 nơi ứng dụng Django sẽ xử lý chúng, vì vậy ta có thể viết các yêu cầu như thế này trong giao diện user :

axios.get("/api/todos/") 

Thay vì điều này:

axios.get("http://localhost:8000/api/todos/") 

Lưu ý: Bạn có thể cần phải khởi động lại server phát triển để proxy đăng ký với ứng dụng.

Ta sẽ sửa đổi frontend/src/App.js lần cuối để nó không sử dụng các mục mã cứng từ mảng nữa mà yêu cầu dữ liệu từ server backend và thay vào đó liệt kê chúng. Ta cũng muốn đảm bảo tất cả các hoạt động CRUD đều gửi yêu cầu đến server backend thay vì tương tác với dữ liệu giả.

Mở file và thay thế bằng version cuối cùng này:

 // frontend/src/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,           activeItem: {             title: "",             description: "",             completed: false           },           todoList: []         };       }       componentDidMount() {         this.refreshList();       }       refreshList = () => {         axios           .get("http://localhost:8000/api/todos/")           .then(res => this.setState({ todoList: res.data }))           .catch(err => console.log(err));       };       displayCompleted = status => {         if (status) {           return this.setState({ viewCompleted: true });         }         return this.setState({ viewCompleted: false });       };       renderTabList = () => {         return (           <div className="my-5 tab-list">             <span               onClick={() => this.displayCompleted(true)}               className={this.state.viewCompleted ? "active" : ""}             >               complete             </span>             <span               onClick={() => this.displayCompleted(false)}               className={this.state.viewCompleted ? "" : "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                 onClick={() => this.editItem(item)}                 className="btn btn-secondary mr-2"               >                 {" "}                 Edit{" "}               </button>               <button                 onClick={() => this.handleDelete(item)}                 className="btn btn-danger"               >                 Delete{" "}               </button>             </span>           </li>         ));       };       toggle = () => {         this.setState({ modal: !this.state.modal });       };       handleSubmit = item => {         this.toggle();         if (item.id) {           axios             .put(`http://localhost:8000/api/todos/${item.id}/`, item)             .then(res => this.refreshList());           return;         }         axios           .post("http://localhost:8000/api/todos/", item)           .then(res => this.refreshList());       };       handleDelete = item => {         axios           .delete(`http://localhost:8000/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 });       };       render() {         return (           <main className="content">             <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="">                     <button onClick={this.createItem} className="btn btn-primary">                       Add task                     </button>                   </div>                   {this.renderTabList()}                   <ul className="list-group list-group-flush">                     {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; 

Hàm refreshList() có thể sử dụng lại được gọi mỗi khi hoàn thành một yêu cầu API. Nó cập nhật danh sách Todo để hiển thị danh sách các mục được thêm mới nhất .

Hàm handleSubmit() lý cả hoạt động tạo và cập nhật. Nếu mục được truyền dưới dạng tham số không có id, thì có thể nó chưa được tạo, vì vậy hàm sẽ tạo nó.

Xin chúc mừng! Ta vừa xây dựng thành công fontend.

Kiểm tra ứng dụng

Hãy khởi động server backend trên một version terminal có nguồn root trong shell ảo Pipenv và trỏ đến folder backend :

$ python manage.py runserver 

Ta cũng cần khởi động server phát triển giao diện user :

$ yarn start 

Ta có thể truy cập ứng dụng trên địa chỉ này - http: // localhost: 3000 - để xem nó hoạt động:

Ta đã đến phần cuối của hướng dẫn này và tìm hiểu cách cấu hình Django và React để tương tác chính xác với nhau. Ta cũng đã thấy một số lợi ích đi kèm với việc khởi động ứng dụng React bằng cách sử create-react-app công cụ create-react-app , chẳng hạn như Reload nóng về cơ bản là tính năng giúp ứng dụng web có thể tự reload khi nào có thay đổi. được phát hiện.

Mã nguồn cho hướng dẫn này có sẵn tại đây trên GitHub.


Tags:

Các tin liên quan