python manage.py startapp crud_demo
from django.db import models
# Create your models here.
from dvadmin.utils.models import CoreModel
class CrudDemoModel(CoreModel):
goods = models.CharField(max_length=255, verbose_name="商品")
inventory = models.IntegerField(verbose_name="库存量")
goods_price = models.FloatField(verbose_name="商品定价")
purchase_goods_date = models.DateField(verbose_name="进货时间")
class Meta:
db_table = "goods"
verbose_name = '商品表'
verbose_name_plural = verbose_name
ordering = ('-create_datetime',)
'''
@author: hongzai
@contact: 2505811377@qq.com
@file: serializers.py
@time: 2022/4/8 11:12
@desc:
'''
from crud_demo.models import CrudDemoModel
from dvadmin.utils.serializers import CustomModelSerializer
class CrudDemoModelSerializer(CustomModelSerializer):
"""
序列化器
"""
class Meta:
model = CrudDemoModel
fields = "__all__"
class CrudDemoModelCreateUpdateSerializer(CustomModelSerializer):
"""
创建/更新时的列化器
"""
class Meta:
model = CrudDemoModel
fields = '__all__'
# Create your views here.
from crud_demo.models import CrudDemoModel
from crud_demo.serializers import CrudDemoModelSerializer, CrudDemoModelCreateUpdateSerializer
from dvadmin.utils.viewset import CustomModelViewSet
class CrudDemoModelViewSet(CustomModelViewSet):
"""
list:查询
create:新增
update:修改
retrieve:单例
destroy:删除
"""
queryset = CrudDemoModel.objects.all()
serializer_class = CrudDemoModelSerializer
create_serializer_class = CrudDemoModelCreateUpdateSerializer
update_serializer_class = CrudDemoModelCreateUpdateSerializer
filter_fields = ['goods', 'goods_price']
search_fields = ['goods']
'''
@author: hongzai
@contact: 2505811377@qq.com
@file: urls.py
@time: 2022/4/8 12:23
@desc:
'''
from rest_framework.routers import SimpleRouter
from .views import CrudDemoModelViewSet
router = SimpleRouter()
router.register("api/crud_demo", CrudDemoModelViewSet)
urlpatterns = [
]
urlpatterns += router.urls
path('',include('crud_demo.urls'))
python manage.py makemigrations
python manage.py migrate
打开swgger可以看到我们配置的接口已经生效了
dvadmin的前端是基于d2admin的管理系统,集成了d2-crud-plus等d2admin生态圈的功能
d2-crud-plus 是基于 d2-admin 的 d2-crud 的扩展,旨在简化 d2-crud 配置,快速开发crud功能
打开web/src/views/新建一个目录为crud_demo,并新建crud.js,index.vue,api.js三个文件
目录结构
views
|--crud_demo
|--api.js //定义api接口
|--crud.js //配置crud界面
|--index.vue //vue文件
api.js
/*
* @Description:
* @Author: hongzai
* @version:
* @Date: 2022-04-08 12:44:55
* @LastEditors: hongzai
* @LastEditTime: 2022-04-08 12:44:55
*/
import { request } from '@/api/service'
export const urlPrefix = '/api/crud_demo/'
export function GetList(query) {
return request({
url: urlPrefix,
method: 'get',
params: query
})
}
export function AddObj(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: "ID",
key: "id",
show: false,
disabled: true,
width: 90,
form: {
disabled: true
}
},
{
title: "商品",
key: "goods",
sortable: true,
treeNode: true,
type: "input",
form: {
editDisabled: true,
rules: [
// 表单校验规则
{ required: true, message: "商品名称必填" }
],
component: {
props: {
clearable: true
},
placeholder: "请输入商品"
},
itemProps: {
class: { yxtInput: true }
}
}
},
{
title: "库存量",
key: "inventory",
sortable: true,
type: "number",
form: {
editDisabled: true,
rules: [
// 表单校验规则
{ required: true, message: "库存量必填" }
],
component: {
props: {
clearable: true
},
placeholder: "请输入库存量"
},
itemProps: {
class: { yxtInput: true }
}
}
},
{
title: "商品定价",
key: "goods_price",
sortable: true,
search: {
component: {
props: {
clearable: true
}
}
},
type: "number",
form: {
editDisabled: true,
rules: [
// 表单校验规则
{ required: true, message: "商品定价必填" }
],
component: {
props: {
clearable: true
},
placeholder: "请输入商品定价"
},
itemProps: {
class: { yxtInput: true }
}
}
},
{
title: "进货时间",
key: "purchase_goods_date",
sortable: true,
type: "date",
form: {
rules: [
{ required: true, message: "进货时间必填" }
],
component: {
props: {
clearable: true,
format: 'yyyy-MM-dd',
valueFormat: 'yyyy-MM-dd'
},
placeholder: "请输入进货时间"
},
itemProps: {
class: { yxtInput: true }
}
}
}
].concat(vm.commonEndColumns())
};
};
index.vue
<!--
* @Description:
* @Author: hongzai
* @version:
* @Date: 2022-04-08 12:44:41
* @LastEditors: hongzai
* @LastEditTime: 2022-04-08 12:54:31
-->
<template>
<d2-container :class="{ 'page-compact': crud.pageOptions.compact }">
<template slot="header">测试页面</template>
<d2-crud-x ref="d2Crud" v-bind="_crudProps" v-on="_crudListeners">
<!-- 自动绑定参数与事件 -->
<!-- 包含详细参数见:https://gitee.com/greper/d2-crud-plus/blob/master/packages/d2-crud-plus/src/lib/mixins/crud.js#L164-->
<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 v-bind="_crudToolbarProps" v-on="_crudToolbarListeners" />
</div>
</d2-crud-x>
</d2-container>
</template>
<script>
import { crudOptions } from './crud' // 上文的crudOptions配置
import { d2CrudPlus } from 'd2-crud-plus'
import { AddObj, GetList, UpdateObj, DelObj } from './api' // 查询添加修改删除的http请求接口
export default {
name: 'curd_demo',
mixins: [d2CrudPlus.crud], // 最核心部分,继承d2CrudPlus.crud
methods: {
getCrudOptions () {
return crudOptions(this)
},
pageRequest (query) {
return GetList(query)
}, // 数据请求
addRequest (row) {
return AddObj(row)
}, // 添加请求
updateRequest (row) {
return UpdateObj(row)
}, // 修改请求
delRequest (row) {
return DelObj(row.id)
} // 删除请求
}
}
</script>
刷新前端页面就可以看到我们的菜单
添加测试数据
d2-crud-plus文档
默认crud的配置可以参考文档根据你的需求
ok,现在可以看到我们的数据了
数据库里也看到了我们的数据
在dvadmin/system/views/file_list.py里能够找到系统自带的文件管理的modelserializers和modelviewset
在urls里找到接口的路由
打开前端web目录,根目录的install.js这里有全局crud的配置
这里就是d2uploader文件上传控件的配置,因为我们使用的是本地服务器存储,而不是第三方储存,我们只修改form的配置
form: {
action: util.baseURL() + 'api/system/file/',
name: 'file',
headers: {
Authorization: 'JWT ' + util.cookies.get('token')
},
type: 'form',
successHandle(ret, option) {
if (ret.data == null || ret.data === '') {
throw new Error('上传失败')
}
return { url: ret.data.url }
}
}
crud里类型填写file-uploader,在对应的事件处理数据
{
title: '选择数据',
key: 'algorithmData',
valueResolve(row, key) {
if (row.algorithmData.length > 0) {
row.algorithmData = row.algorithmData[0]
}
},
sortable: true,
search: {
disabled: true
},
type: 'file-uploader',
form: {
props: { //表单组件的参数,具体参数请查看对应的组件文档
drag: true
},
valueChange(key, value, form, { getColumn, mode, component, immediate, getComponent }) {
// form表单数据change事件,表单值有改动将触发此事件
console.log(form[key]);
},
}
},
上传文件测试,ok,看一下文件管理
可以看到我们的文件上传成功
使用方法和官网的demo相同
1.在index里把d2-crud-x表格注释,或者你使用插槽把展示图表的div放进去也可以,我这里使用第一种方式。
2.在api.js里添加我们获取数据的接口
index.vue
<!--
* @Author: your name
* @Date: 2022-04-14 14:13:20
* @LastEditTime: 2022-04-17 16:15:40
* @LastEditors: hongzai
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* @FilePath: \web\src\views\data_visual\index.vue
-->
<template>
<d2-container :class="{ 'page-compact': crud.pageOptions.compact }">
<div class="background">
<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> -->
<dir>
<el-form ref="form" :model="form" label-width="200px">
<el-form-item label="选择已导入的算法和数据">
<el-select
v-model="value"
placeholder="请选择"
style="width: 500px"
@change="change"
>
<el-option
v-for="item in dataList"
:key="item.id"
:label="item.label"
:value="item"
>
<span style="float: left">{{ item.label }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{
item.value
}}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">展示</el-button>
</el-form-item>
</el-form>
</dir>
<div id="chart"></div>
</div>
</d2-container>
</template>
<script>
import * as api from "./api";
import { crudOptions } from "./crud";
import { d2CrudPlus } from "d2-crud-plus";
import * as echarts from "echarts";
import { process_chart_data1 } from "./process_data.js";
export default {
name: "data_visual",
mixins: [d2CrudPlus.crud],
mounted() {
this.pageRequest().then((res) => {
let resList = res.data.data;
resList.forEach((val, index) => {
this.dataList.push({
value: val.algorithmData,
label: val.algorithmName,
});
});
console.log(this.dataList);
});
},
data() {
return {
form: {},
dataList: [],
value: "",
};
},
methods: {
change(val) {
console.log(val);
},
onSubmit() {
// 在这设置图表option
api
.getFileData({
filePath: this.value.value,
})
.then((res) => {
console.log(res.data.data);
this.data = res.data.data;
switch (this.value.label) {
case "逻辑回归":
echarts.init(document.getElementById("chart")).setOption({
xAxis: {},
yAxis: {},
series: [
{
symbolSize: 20,
data: this.data,
type: "scatter",
},
],
});
break;
case "线性回归":
echarts.init(document.getElementById("chart")).setOption({
xAxis: {},
yAxis: {},
series: [
{
symbolSize: 20,
data: this.data,
type: "scatter",
},
],
});
break;
default:
break;
}
});
},
getCrudOptions() {
return crudOptions(this);
},
pageRequest(query) {
return api.GetList(query);
},
addRequest(row) {
console.log("api", api);
return api.AddObj(row);
},
updateRequest(row) {
console.log("----", row);
return api.UpdateObj(row);
},
delRequest(row) {
return api.DelObj(row.id);
},
},
};
</script>
<style>
#chart {
width: 800px;
height: 500px;
}
.background {
height: 100vh;
/* background-image: url("index_bg.png"); */
}
</style>
api.js
export function getFileData(obj) {
return request({
url: '/api/get_file_data',
method: 'post',
data: obj
})
}
效果如图
{
title: "日期范围",
key: "daterange",
sortable: true,
type: "daterange",
show: false,
search: { disabled: false, width: 380 },
form: {
disabled: true,
component: {
props: {
"time-arrow-control": false,
"picker-options": { shortcuts: shortcuts },
format: "yyyy-MM-dd",
valueFormat: "yyyy-MM-dd",
},
},
},
valueBuilder(row, key) {
if (!StringUtils.hasEmpty(row.daterangeStart, row.daterangeEnd)) {
row.daterange = [
new Date(row.daterangeStart),
new Date(row.daterangeEnd),
];
}
},
valueResolve(row, key) {
if (row.daterange != null && row.daterange.length > 1) {
row.start_time = row.daterange[0];
row.stop_time = row.daterange[1];
} else {
row.start_time = null;
row.stop_time = null;
}
},
},
class NucleicAcidInfoModelFilter(django_filters.rest_framework.FilterSet):
# 指定区间过滤
start_time = django_filters.DateFilter(field_name="create_datetime", lookup_expr='gte')
stop_time = django_filters.DateFilter(field_name="create_datetime", lookup_expr='lte')
class Meta:
model = NucleicAcidInfoModel
fields = ['identification_id']
在视图里添加filterset_class即可
请问在该curd教程中,后端返回给前端的数据格式是什么样子的,查询,删除,更新等等操作的返回数据格式是什么样
@liar 这个你不需要关心,前后端分离数据都是restful的格式,dvadmin框架里已经给你做了这些操作,不明白的话去学一下restful api
@hongzai 由于项目问题,需要使用到oracle数据库,所以并没有使用你们的customModel,我自定义了数据操作,所以在view当中需要我自定义返回的数据,在尝试了你们教程中的d2-crud-plus链接中的pageRequest返回数据类型,与restful的返回数据类型,都是前端无法正常解析的,所以我目前需要的是后端crud返回前端的数据格式,麻烦您了
@liar 加我微信我给你解决方案,17684081694
上传文件显示请求地址出错。
还有实现文件上传的前后端代码哪里有或者能否发一下,我不确定我是否写对了
@labmem
我才发现我没改成本地存储,所以一开始的问题没有了。
但是文件管理页面我发现添加文件的按钮被注释掉了,取消后发现上传文件会报错,所以目前是有问题的吗,还是我自己的问题?
@labmem
还需要改一下install.js里的defaultType为我们配置的form
@hongzai 你好,我已经改了,但是我没办法删回复或者编辑,重新贴的新的问题。
我在看文件管理页面,发现上传文件按钮被注释掉了,取消注释后在表单里上传文件可行,但是无法点确定。取消之后能看到上传的文件。
我想知道怎么才能在我的模块里,新建一项时保存文件和其他字段
@hongzai 你好,添加新的菜单,刷新不显示,是什么原因?
@labmem 这块做的不是表单上传,所有有这个问题,但是不影响使用,我原来做过一个上传直接关闭弹窗