DRF + react 实现TodoList
在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的更多相关文章
- React笔记03——React实现TodoList
1 什么是JSX语法? 原生JS中,要向页面中挂载html标签,标签一定是被引号''包起来的:document.getElementById('root').append('<div>he ...
- 【转载】React入门-Todolist制作学习
我直接看的这个React TodoList的例子(非常好!): http://www.reqianduan.com/2297.html 文中示例的代码访问路径:http://127.0.0.1:708 ...
- React-TodoList
React入门最好的学习实例-TodoList 前言 React 的核心思想是:封装组件,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件. 最近前端界闹的沸沸扬扬的技术当属react ...
- React之todo-list
基于React的一个简单Todo-list 先赌为快:在线DEMO,感觉还不错点一下star -_- ~ 源码地址: 一.已经完成的功能 1.新增选项(默认未完成) 2.完成状态可以切换 3.当前选 ...
- react——一个todolist的demo
代码如下: function ToDoListHeader(props) { return <h1 className={props.className}>ToDoList</h1& ...
- 使用React并做一个简单的to-do-list
1. 前言 说到React,我从一年之前就开始试着了解并且看了相关的入门教程,而且还买过一本<React:引领未来的用户界面开发框架 >拜读.React的轻量组件化的思想及其virtual ...
- React入门——制作一个TodoList App
源码 import React, { Component, Fragment } from "react"; class TodoList extends Component { ...
- react 的安装和案列Todolist
react 的安装和案列Todolist 1.react的安装和环境的配置 首先检查有没有安装node.js和npm node -v npm -v 查看相关版本 2.安装脚手架工具 2.构建:crea ...
- React 入门-写个 TodoList 实例
React 是一个用于构建用户界面的 JavaScript 库,主要特点有: 声明式渲染:设计好数据和视图的关系,数据变化 React 自动渲染,不必亲自操作DOM 组件化:页面切分成多个小部件,通过 ...
随机推荐
- Android基础开发入门(一)
前言:我学了一年多的C#(从学编程算起,也没有两年,我现在大二下),中间也一直在学WP开发,虽然技术不咋地,很渣渣,但微软在Build大会上宣布的策略让我觉得有必要学习一下安卓开发了.关于微软的策略, ...
- PHPEXCEL 不能输出中文内容,只显示空白
以他带的示例文件为例 01simple-download-xls.php // Add some data $objPHPExcel->setActiveSheetIndex(0) ...
- 水晶报表异常“CrystalDecisions.ReportSource.ReportSourceFactory”的类型初始值设定项引发异常,未能加载文件或程序集“log4net
System.TypeInitializationException: “CrystalDecisions.ReportSource.ReportSourceFactory”的类型初始值设定项引发异常 ...
- WPF生成二维码
WPF可以通过ZXing.Net库来实现二维码的功能. 可以通过NuGet安装: Install-Package ZXing.Net 二维码的实现代码: #region 二维码的方法 /// < ...
- WPF 窗体边框处理
一般做wpf窗口时都不会使用默认的标题栏等,会把他隐藏掉 此时设置以下属性 WindowStyle.AllowsTransparency.ResizeMode 中的两个或三个都能达到目的. 有一种场景 ...
- 安卓ImageButton圆角按钮设置
首先图片要做成圆角的,使用美图秀秀,这个不多说. 之后使用设置了圆角的按钮,效果有缺陷,按钮会有灰色的边角. 类似这样: 去掉的方法是将layout的 android:src="@draw ...
- 深度网络中的Tricks
数据增强(Data augmentation) 预处理(Pre-processing) 初始化(Initializations) 训练中的Tricks 激活函数(Activation function ...
- DateTimeToGreenUnix
@暗夜魔尊 { Unix date conversion support with time-zone detect } function DateTimeToGreenUnix(const AVal ...
- LFS Linux From Scratch 笔记2(经验非教程)BLFS
LFS 完了. 其实还没完,还要装一些其他的组件,系统才算是对人类有用的系统. 正好这里有个BLFS Beyound Linux From Scratch 的教程. 其实,按照现有的可运行的LFS系统 ...
- TopFreeTheme精选免费模板【20130704】
今天我们给大家分享10个最新的主题模板,6款WordPress主题,3款Joomla模板,1款Magento主题.它们分别来自ThemeForest,RocketTheme,YooTheme.有需要的 ...