在web项目构建中有很多框架可供选择,开发人员对项目的使用选择,有很多的影响因素,其中之一就是框架在定义该项目的单独任务时的复杂性。

简介

本文有如下几个部分:

  • 准备
  • 配置后端
  • 配置APIs
  • 配置前端
  • 测试

使用Django和React 编写Todolist程序有如下原因:

  • React框架有广泛的适用范围,遇到错误的问题,可以很快面向Google解决
  • Django 是一个很强大的web框架,从会话到身份认证的功能实现将会节约大量的时间

Django和React配合将可以方便的实现应用程序编写,为了使前后端交互(即检索和存储),为了创建交互界面,我们将使用Django REST框架(DRF)在后端构建API(应用程序编程接口)。

最后实现的TodoList效果如下:

准备

本次环境如下:

 Ubuntu 18.4
python 3.6.8
pip
pipenv

之前一直是用的pip + 手动创建虚拟环境。pipenv 是生产就绪的工具,正如其名,他将pip和virtualenv整合在一起

配置 backend

按照如下步骤将建立一个比较好的项目目录

 mkdir django-todo-react
cd django-todo-react

使用pip安装pipenv,并激活一个虚拟环境

 pip install pipenv
pipenv shell

(配置国内镜像跟新文件和pip源,清华,以提高依赖安装速度)
使用pipenv安装django,并新修建一个项目叫backend

 pipenv install django
django-admin startproject backend

进入backend文件夹 ,新创建一个todo的应用,并迁移数据库

(如果你的机器里有python2.x和python3.x在后面使用python,pip命令时请分别使用python3,pip3)

 cd backend
python3 manage.py startapp todo
python3 manage.py migrate
python3 manage.py runserver

如上步骤输入都正确的话,输入地址http://127.0.0.1:8000
会出现下图

好了现在开始配置Todo应用

注册应用Todo

在上面已经完成了后端的基苯配置,现在可以将todo应用程序注册为已安装的应用程序,以便Django识别。打开/backend/settings.py文件,更新INSTALLED_APPS

 # 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' # 此处
]
定义 Todo model

打开文件todo/model.py,编写模型

 # 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

在以上Todo类中描述了三个属性:

  • Title
  • Description
  • Completed

由于改变了Todo model,现在需要对数据做一个迁移,以确保数据库改变。

 python3 manage.py makemigrations todo
python manage.py migrate todo

对todo应用进行配置,修改文件todo/admin.py

 # 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

创建账户

 python3 manage.py createsuperuser

启动服务器,访问地址-172.0.0.1:8000/admin

 python3 manage.py runserver

登陆后,如下图所示,

进入添加如下:

配置 APIs

使用 ctr + c 停止服务器,然后使用pipenv安装djangorestframework 和django-cors-headers

 pipenv install djangorestframework django-cors-headers

现在需要把rest_framework和corsheaders 注册,

 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.common.CommonMiddleware', # 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',
]
CORS_ALLOW_CREDENTIALS = True # add this
CORS_ORIGIN_ALLOW_ALL = True # add this

网上教程在backend/settings.py最后添加以下,一直报错,后来只有不添加如下即可运行:

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

在CORS_ORIGIN_WHITELIST片段中,我们将localhost:3000列入白名单,因为我们希望应用程序的前端(将在该端口上提供)与API进行交互

Todo model 序列化

序列化相关参见此文

我们需要序列化程序将模型实例转换为JSON,以便前端可以轻松处理接收到的数据。
新建文件todo/serializers.py

 touch todo/serializers.py

编辑文件像如下代码:

 # 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')

在上面代码片段中,我们指定了要使用的模型以及我们要转换为JSON的字段。

编写View视图

在todo/views.py新建Todoview类,

 # 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

视图集基类默认提供CRUD操作的实现,我们要做的是指定序列化程序类和查询集。
编写后端backend/urls.py文件

 # 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
]

这是完成API构建的最后一步,我们现在可以在Todo模型上执行CRUD操作。路由器类允许我们进行以下查询:

  • /todos/ 这将返回所有Todo项的列表(可以在此处完成Create和Read操作)。
  • /todos/id 这将使用id主键返回单个Todo项(可以在此处执行Update和Delete操作)。

运行下列地址访问服务器127.0.0.1:8000/api/todos

 python3 manage.py runserver

如下图所示:

可以新建TodoList,以测试:

配置前端

在一个新的命令行终端上执行命令

 cd django-todo-react
npm install -g create-react-app
create-react-app frontend
cd frontend
yarn start

访问127.0.0.1:3000将会看见如下:

引入bootstrap和reactstrap来增加UI的功能:

 yarn add bootstrap reactstrap

编辑文件src/index.css

 /__ frontend/src/index.css __/
body {
margin:;
padding:;
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 {
16 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;
}

在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: https://bit.ly/CRA-PWA
serviceWorker.unregister();
安装axios
 yarn add axios

把axios 添加进入package.json文件

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

要处理添加和编辑任务等操作,我们将使用模态,所以让我们在components文件夹中创建一个Modal组件。先在src目录下新建文件夹components,

 mkdir src/components
touch src/components/Modal.js

编辑Modal.js文件

 // 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>
);
}
}

我们创建了一个CustomModal类,它嵌套了从reactstrap库派生的Modal组件。我们还在表单中定义了三个字段

  • Title
  • Description
  • Completed

接下来就是 修改文件src/Appjs

 // 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;

这时基本的代码已经写完,可以进行测试了

测试

分别在刚才两个命令行运行APIs端和前端

 # django-todo-react/backend
python3 manage.py runserver # 运行DRF
 yarn start # 运行react

最后访问 127.0.0.1:3000/

访问127.0.0.1:8000/api

参考

原作者个人感觉写的很详细了

DRF + react 实现TodoList的更多相关文章

  1. React笔记03——React实现TodoList

    1 什么是JSX语法? 原生JS中,要向页面中挂载html标签,标签一定是被引号''包起来的:document.getElementById('root').append('<div>he ...

  2. 【转载】React入门-Todolist制作学习

    我直接看的这个React TodoList的例子(非常好!): http://www.reqianduan.com/2297.html 文中示例的代码访问路径:http://127.0.0.1:708 ...

  3. React-TodoList

    React入门最好的学习实例-TodoList 前言 React 的核心思想是:封装组件,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件. 最近前端界闹的沸沸扬扬的技术当属react ...

  4. React之todo-list

    基于React的一个简单Todo-list 先赌为快:在线DEMO,感觉还不错点一下star  -_- ~ 源码地址: 一.已经完成的功能 1.新增选项(默认未完成) 2.完成状态可以切换 3.当前选 ...

  5. react——一个todolist的demo

    代码如下: function ToDoListHeader(props) { return <h1 className={props.className}>ToDoList</h1& ...

  6. 使用React并做一个简单的to-do-list

    1. 前言 说到React,我从一年之前就开始试着了解并且看了相关的入门教程,而且还买过一本<React:引领未来的用户界面开发框架 >拜读.React的轻量组件化的思想及其virtual ...

  7. React入门——制作一个TodoList App

    源码 import React, { Component, Fragment } from "react"; class TodoList extends Component { ...

  8. react 的安装和案列Todolist

    react 的安装和案列Todolist 1.react的安装和环境的配置 首先检查有没有安装node.js和npm node -v npm -v 查看相关版本 2.安装脚手架工具 2.构建:crea ...

  9. React 入门-写个 TodoList 实例

    React 是一个用于构建用户界面的 JavaScript 库,主要特点有: 声明式渲染:设计好数据和视图的关系,数据变化 React 自动渲染,不必亲自操作DOM 组件化:页面切分成多个小部件,通过 ...

随机推荐

  1. 如何将编码转为自己想要的编码 -- gbk utf-8

    /**  * 数组转码  * @param array $arr 要转码的数组  * @param string $in_charset 输入的字符集  * @param string $out_ch ...

  2. RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密

    原文:RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密 C#在using System.Security.Cryptograph ...

  3. 支持chrome30下载文件

    function downloadX(url ,fileName){ const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr ...

  4. 生成view的描述字段列表

    ); declare @field_list nvarchar(max); set @table = N'vwMaterial'; set @field_list = N''; SELECT u.na ...

  5. Win10《芒果TV - Preview》更新v3.1.31.0,全新播放页蜕变,预加载提速技术

    Win10<芒果TV - Preview>(商店内测版) v3.1.31.0 于2016年11月21日星期一晚上九点半登陆商店 主要是全面升级改造桌面播放页,新增观看互动评论.猜你喜欢功能 ...

  6. 使用内核对象Mutex可以防止同一个进程运行两次

    用互斥法实现防止程序重复运行,使用内核对象Mutex可以防止同一个进程运行两次.注意:是名称相同的进程,而不是exe,因为exe程序可以改名. using System.Threading; publ ...

  7. Advanced Installer 安装前卸载旧版本的办法

    原文:Advanced Installer 安装前卸载旧版本的办法 Advanced Installer这个工具百度出来的资料太少了. 在我们平常打包的工作中,经常遇到的一个问题是,如何能在安装新版本 ...

  8. Android零基础入门第40节:自定义ArrayAdapter

    原文:Android零基础入门第40节:自定义ArrayAdapter ListView用起来还是比较简单的,也是Android应用程序中最重要的一个组件,但其他ListView可以随你所愿,能够完成 ...

  9. eclipse 插件编写(四)

    前言 前面几篇文章讲了下如果编写简单的eclipse插件,如创建插件项目.编写右键弹出菜单等功能,接下来主要写一下如何生成代码的功能,这一片的功能跟插件本身的编写关联不太大,主要处理插件之后的业务内容 ...

  10. 向github提交本地项目

    首先你需要一个github账号,所有还没有的话先去注册吧! https://github.com/ 我们使用git需要先安装git工具,这里给出下载地址,下载后一路直接安装即可: https://gi ...