感谢 Django-vue-admin 开源项目组的支持
开发是最重要的还是实战,如何能够快速理解掌握,当然是使用开源平台来进行研究学习。当然开源项目非常多,如何寻找变成了难事。这里我建议,如果是新手,先去Gitee进行开源搜找(毕竟这是国内开源第一地方)也有很多人可以交流,倘若一开始就去Github找开源项目,先不说语言问题,就单单能不能自己跑出来都是问题,一个Web项目如果在本地运行起来需要一定的基础功底。一个项目最重要的是先跑出来然后才有心思去研究。
此开源项目采用前后端分离,采用的接口设计,本来我之前是想找若依框架进行学习的,发现没有python-django版本的,全都是java版本,所以也索性选择了这个还不错的快速开发平台。
前端技术:D2Admin Vue ElementUI
后端技术:Django ,Django rest framework
权限认证:Django REST FRAMEWORK SIMPLEJWT
web开源项目可以进行二次开发,简单来说可以根据现有的东西来自取所需,留下需要的,并进行开发与部署(开源准则),所以以此框架进行课设亦或者毕业设计也是相当绰绰有余。本项目学习的点非常多,下面我也会抽时间对常见的技术模块进行讲解。
开源项目地址:
Gitee链接
Github链接
项目首页部分
环境配置
本次环境使用的IDE为Pycharm 2022.
对于开发环境是Python 3.7.9 Mysql 5.7 nodejs 18.2
前面两个没什么问题,主要是nodejs(基于服务端)。
Node.js 是一个开源和跨平台的Javascript运行环境。其中npm以简单的结构帮助Node.js蓬勃发展。
下载nodejs,并默认环境变量配置。
进入到nodejs目录下,创建node_cache和node_global
最好使用cmd管理员命令
# 检测是否安装node.js
node -v
#设置npm安装程序时的默认位置
npm config set prefix "C:\Tools\Nodejs\node_global"
#设置npm安装程序时的缓存位置
npm config set cache "C:\Tools\Nodejs\node_cache"
#并把环境变量添加到PATH中
NODE_PATH:C:\Tools\Nodejs
PATH:C:\Tools\Nodejs\node_global
同时使用pycharm打开项目,在项目-python解释器选择(添加)
建造虚拟环境,这样此时的终端就是和项目一样的python版本与pip包了。
克隆项目
git clone https://gitee.com/liqianglog/django-vue-admin.git
前端搭建
注意,为保证npm启动服务正常,这里统一使用cmd(管理员运行)。
# 进行web目录,首先输入 D: 进入D盘
cd web
下面是安装依赖,在这一方面我尝试了非常久的办法,遇到了不少麻烦,下面我将以上面的nodejs环境为例进行搭建:
# 首先清除缓存
npm cache clean --force
# 设置淘宝源
npm config set registry https://registry.npm.taobao.org
# 运行安装
npm install
# 安装依赖
npm install --registry=https://registry.npm.taobao.org
在此过程中可能由于版本问题会报错,Error: error:0308010C:digital envelope routines::unsupported。
那么首先先降低npm版本:
npm install npm@8.1.2 -g
重新配置淘宝下载镜像:
npm config set registry "https://registry.npm.taobao.org"
# 再安装一下依赖
npm install --registry=https://registry.npm.taobao.org
# 安装yarn
npm install --global yarn
当然由于node版本过高,必须要进行安全配置才可以启动前端项目:
cmd输入,每次运行前都需要输入
set NODE_OPTIONS=--openssl-legacy-provider
接着就可以运行了
npm run dev
# 浏览器访问 http://localhost:8080
# .env.development 文件中可配置启动端口等参数
# 构建生产环境
# npm run build
看到这样,恭喜前端跑起来了!
此时会自动跳转到默认浏览器中,不过由于没有设置后端操作,所以也无法登录。
后端操作
后端基本就是Python django的常规操作了。
首先是准备数据库
# 建一个 dv 数据库,这里就是字符集与排序顺序
CREATE DATABASE IF NOT EXISTS dv DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;
#在mysql中请大家忘记**utf8**,永远使用**utf8mb4**
1. 进入项目目录 cd backend
2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py
3. 在 env.py 中配置数据库信息
mysql数据库版本建议:8.0
mysql数据库字符集:utf8mb4
4. 安装依赖环境
pip install -r requirements.txt
5. 执行迁移命令:
python manage.py makemigrations
python manage.py migrate
6. 初始化数据
python manage.py init
7. 初始化省市县数据:
python manage.py init_area
8. 启动项目
python manage.py runserver 0.0.0.0:8000
或使用 daphne :
daphne -b 0.0.0.0 -p 8000 application.asgi:application
登录以后就来到了如下界面:
快速构建数据操作页面
这里以上述项目为框架进行搭建一个简易的图书管理页面,具体方法如下:
其中在pycharm中需要指定源文件目录**(项目根目录)**,这样才不会解析未引用:
#进入后端目录
cd backend
#新建一个后端应用名称为book
python manage.py startapp book
进行数据库表的构造:
from django.db import models
from dvadmin.utils.models import CoreModel, table_prefix
class Book(CoreModel):
book_no = models.CharField(max_length=24,unique=True, verbose_name='书籍编号', help_text="书籍编号")
book_name = models.CharField(max_length=24,null=False, verbose_name='书籍名称', help_text="书籍名称")
class Meta:
db_table = table_prefix + "book"
verbose_name = '书籍表'
verbose_name_plural = verbose_name
ordering = ('-create_datetime',)
# book/settings.py
INSTALLED_APPS:
'book'
进行表的迁移:
python manage.py makemigrations book
python manage.py migrate book
创建过滤器,序列化器,视图:
# book/views.py
# Create your views here.
from book.models import Book
from dvadmin.utils.serializers import CustomModelSerializer
from dvadmin.utils.viewset import CustomModelViewSet
class BookSerializer(CustomModelSerializer):
"""
书籍-序列化器
"""
class Meta:
model = Book
fields = "__all__"
read_only_fields = ["id"]
class BookCreateUpdateSerializer(CustomModelSerializer):
"""
书籍管理 创建/更新时的列化器
"""
class Meta:
model = Book
fields = '__all__'
# 以上是序列化函数,可以使用新建一个serializers.py来进行管理
class BookViewSet(CustomModelViewSet):
"""
书籍管理接口
list:查询
create:新增
update:修改
retrieve:单例
destroy:删除
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
extra_filter_backends = []
permission_classes = []
search_fields = ['label']
添加路由:
# book/urls.py
from rest_framework import routers
from book.views import BookViewSet
book_url = routers.SimpleRouter()
book_url.register(r'book', BookViewSet)
urlpatterns = []
urlpatterns += book_url.urls
backend/application/urls.py
path('api/', include('book.urls')),
以上后端的操作就全部完成了:
先运行后端,运行成功后将会进入到接口部分,表面后端操作已经实现
下面进行前端展示:
在src/view下新建一个目录,新建api.js curd.js index.vue
# api.js
import { request } from '@/api/service'
export const urlPrefix = '/api/book/'
/**
* 列表查询
*/
export function GetList (query) {
query.limit = 999
return request({
url: urlPrefix,
method: 'get',
params: query
})
}
/**
* 新增
*/
export function createObj (obj) {
return request({
url: urlPrefix,
method: 'post',
data: obj
})
}
/**
* 修改
*/
export function UpdateObj (obj) {
return request({
url: urlPrefix + obj.id + '/',
method: 'put',
data: obj
})
}
/**
* 删除
*/
export function DelObj (id) {
return request({
url: urlPrefix + id + '/',
method: 'delete',
data: { id }
})
}
# crud.js
import { request } from '@/api/service'
import { BUTTON_STATUS_NUMBER } from '@/config/button'
import { urlPrefix as bookPrefix } from './api'
export const crudOptions = (vm) => {
return {
pageOptions: {
compact: true
},
options: {
tableType: 'vxe-table',
rowKey: true, // 必须设置,true or false
rowId: 'id',
height: '100%', // 表格高度100%, 使用toolbar必须设置
highlightCurrentRow: false,
},
rowHandle: {
width: 140,
view: {
thin: true,
text: '',
disabled () {
return !vm.hasPermissions('Retrieve')
}
},
edit: {
thin: true,
text: '',
disabled () {
return !vm.hasPermissions('Update')
}
},
remove: {
thin: true,
text: '',
disabled () {
return !vm.hasPermissions('Delete')
}
}
},
indexRow: { // 或者直接传true,不显示title,不居中
title: '序号',
align: 'center',
width: 100
},
viewOptions: {
componentType: 'form'
},
formOptions: {
defaultSpan: 24, // 默认的表单 span
width: '35%'
},
columns: [{
title: '关键词',
key: 'search',
show: false,
disabled: true,
search: {
disabled: false
},
form: {
disabled: true,
component: {
props: {
clearable: true
},
placeholder: '请输入关键词'
}
},
view: { // 查看对话框组件的单独配置
disabled: true
}
},
{
title: 'ID',
key: 'id',
show: false,
disabled: true,
width: 90,
form: {
disabled: true
}
},
{
title: '书籍编码',
key: 'book_no',
sortable: true,
treeNode: true,
search: {
disabled: false,
component: {
props: {
clearable: true
}
}
},
type: 'input',
form: {
editDisabled: true,
rules: [ // 表单校验规则
{ required: true, message: '编码必填项' }
],
component: {
props: {
clearable: true
},
placeholder: '请输入编码'
},
itemProps: {
class: { yxtInput: true }
}
}
},
{
title: '书名',
key: 'book_name',
sortable: true,
search: {
disabled: false,
component: {
props: {
clearable: true
}
}
},
type: 'input',
form: {
rules: [ // 表单校验规则
{ required: true, message: '显示值必填项' }
],
component: {
props: {
clearable: true
},
placeholder: '请输入显示值'
},
itemProps: {
class: { yxtInput: true }
}
}
},
].concat(vm.commonEndColumns())
}
}
# index.vue
# 主要改name名
<template>
<d2-container :class="{ 'page-compact': crud.pageOptions.compact }">
<!-- <template slot="header">测试页面1</template>-->
<d2-crud-x ref="d2Crud" v-bind="_crudProps" v-on="_crudListeners">
<div slot="header">
<crud-search
ref="search"
:options="crud.searchOptions"
@submit="handleSearch"
/>
<el-button-group>
<el-button size="small" type="primary" @click="addRow"
><i class="el-icon-plus" /> 新增</el-button
>
</el-button-group>
<crud-toolbar
:search.sync="crud.searchOptions.show"
:compact.sync="crud.pageOptions.compact"
:columns="crud.columns"
@refresh="doRefresh()"
@columns-filter-changed="handleColumnsFilterChanged"
/>
</div>
</d2-crud-x>
</d2-container>
</template>
<script>
import * as api from './api'
import { crudOptions } from './crud'
import { d2CrudPlus } from 'd2-crud-plus'
export default {
name: 'book',
mixins: [d2CrudPlus.crud],
data () {
return {}
},
methods: {
getCrudOptions () {
return crudOptions(this)
},
pageRequest (query) {
return api.GetList(query)
},
addRequest (row) {
d2CrudPlus.util.dict.clear()
return api.createObj(row)
},
updateRequest (row) {
d2CrudPlus.util.dict.clear()
return api.UpdateObj(row)
},
delRequest (row) {
return api.DelObj(row.id)
},
// 授权
createPermission (scope) {
this.$router.push({
name: 'menuButton',
params: { id: scope.row.id },
query: { name: scope.row.name }
})
}
}
}
</script>
<style lang="scss">
.yxtInput {
.el-form-item__label {
color: #49a1ff;
}
}
</style>
此框架最方便的步骤来了,不需要自行搭建,这里使用框架中的菜单管理,进行页面展示:
先添加相应的信息:
由于我按确定没用,所以采取了这个办法:
完成以后将会变成这个样子,这就是我们刚刚建立的页面,数据库信息已经读入:
项目部分分析
由于这个项目非常庞大,里面内涵的东西非常多,在这里也不可能一下子讲完,而且部分技术非常新颖。
首先是前端框架技术:
Vue是一个用于创建用户界面的开源MVVM前端JavaScript框架,也是一个创建单页应用的Web应用框架。在这里构建的时候主要是对于Index.vue中的name修改。
整体目录
├── web/ // 前端项目
├── backend/ // 后端项目
├── docker_env/ // docker 安装配置
├── docker-compose.yml // docker-compose 配置
├── README.md // 项目的说明文档
├── LICENSE // 开源 LICENSE
└── README.md // 说明文档
前端目录
├── README.md // 项目的说明文档
├── dist // build 后存放内容
├── package.json // npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
├── babel.config.js // babel
├── public // 项目根目录
│ ├── image/ // 主题展示目录
│ ├── icon.ico // ico 图标
│ └── index.html // 首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
├── src // 开发的目录
│ ├── App.vue // 项目入口文件
│ ├── api // 系统公告 api 接口
│ ├── assets // 项目公用资源目录(图片,ico)
│ ├── components // 公共组件
│ ├── config // 基本配置
│ ├── layout // 全局 layout
│ ├── libs // 全局公用方法
│ ├── locales // 国际化
│ ├── menu // 菜单权限处理
│ ├── plugin // d2admin 前端插件
│ ├── router // 路由
│ ├── store // 全局 store管理
│ └── views // views 所有页面
│ ├── dashboard // 首页内容
│ ├── demo // demo 示例
│ ├── plugins // dvadmin 插件
│ ├── system // dvadmin 系统专有视图,建议不要修改或新增内容
│ │ ├── ...
│ │ ├── login // 登录页面
│ │ └── ...
│ ├── App.vue // vue App 入口
│ ├── install.js // d2admin 前端通用配置
│ ├── main.js // vue main 入口
│ └── setting.js // d2admin 配置
│ ├── main.js // 项目的核心文件
│ ├── permission.js // 页面是否登录判断权限判断,权限白名单
│ ├── settings.js // 全局配置
│ ├── .env.development // 开发环境配置
│ ├── .env.staging // 预发布环境配置
│ ├── .env.production // 生产环境配置
│ └── ...
└── vue.config.js // 本地跨域代理
后端目录
├── application // 工程名称
│ ├── asgi.py // Django asgi 默认配置
│ ├── celery.py // celery 默认配置
│ ├── settings.py // 项目的settings配置
│ ├── urls.py // 项目主URL对应关系
│ └── wsgi.py // wsgi 默认配置
├── conf // 配置信息
│ ├── env.example.py // 配置信息模板
│ └── env.py // 自行根据 env.example.py 复制重命名为env.py ,系统只会去读取env.py配置
├── dvadmin // docker 启动 celery 脚本
│ ├── system // dvadmin 系统app
│ └── utils // 全局公用方法
├── logs // 日志存放位置
├── media // 上传的文件存放位置
├── plugins // dvadmin 插件目录
├── └── ...
│── static // 静态文件
│ ├── drf-yasg // 静态文件 drf-yasg 静态文件
│ ├── ...
│ └── rest_framework // rest_framework 静态文件
├── docker_start.sh // docker 启动 django 脚本
├── manage.py
└── requirements.txt // 项目环境依赖
在本项目中使用的接口操作,比如在刚刚book等增删改查部分:
class BookViewSet(CustomModelViewSet):
"""
书籍管理接口
list:查询
create:新增
update:修改
retrieve:单例
destroy:删除
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
extra_filter_backends = []
permission_classes = []
search_fields = ['label']
就这么多语句完成了增删改查部分。
主要的技术:统一标准的返回格式;新增,查询,修改可使用不同序列化器(1)ORM性能优化, 尽可能使用values_queryset形式(2)create_serializer_class 新增时,使用的序列化器(3)update_serializer_class 修改时,使用的序列化器。这是十分便捷的技术,需要在日后强化。