Django Ajax 提交,新建,编辑和删除。
新建、编辑使用相同模态框
新建和编辑使用同一个弹出模态框,思路:
为模态框的 save 按键 绑定事件,判断 id 字段是否有值,如果没有值,执行 添加 事件;有值,执行 编辑 事件。
// 思路核心, 有id时,执行编辑, 没有id时,执行新增
if (EDIT_ID) {
// 有数据 编辑
doEdit();
} else {
// 没有数据,执行增加
doAdd();
}
删除事件思路:
1.为删除按钮增加属性: uid, 此uid等于每次循环queryset的id. 2.设置全局变量: var DELETE_ID; 3.第一个点击事件,弹开模态框:$("选择器").modal('show'); 4.通过 $obj.attr(name) 获取 删除按钮的 uid 属性,并赋值给全局变量 DELETE_ID。 DELETE_ID = $(this).attr('uid') 5.对弹出模态框的 确定 按键绑定事件 id='btnConfirmDelete' 6.Ajax, get方式传递数据 7.拼接url: url: '/order/' + DELETE_ID + '/delete/', 8.根据 urls.py 中 order/<int:nid>/delete/, 将 nid 做为参数传递给views对应方法 9.验证id=nid的数据是否存在: 不存在,返回给前端 错误提示 存在,views对应方法执行删除,返回给前端 正常提示 10.前端关闭模态框,重新刷新页面 $("#deleteModal").modal('hide'); location.reload(); */
所有代码如下:
models.py
class Admin(models.Model):
username = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
def __str__(self):
return self.username
class Order(models.Model):
""" 订单 """
oid = models.CharField(verbose_name='订单号', max_length=64)
title = models.CharField(verbose_name='名称', max_length=32)
price = models.IntegerField(verbose_name='价格')
status_choice = (
(1,'特支付'),
(2,'已支付'),
)
status = models.SmallIntegerField(verbose_name='支付状态', choices=status_choice, default=1)
# admin_id ForeignKey 会自动增加 _id
admin = models.ForeignKey(verbose_name='管理员', to='Admin', on_delete=models.CASCADE)
urls.py
# 订单管理
path('order/lis/', order.order_list),
path('order/add/', order.order_add),
path('order/<int:nid>/delete/', order.order_delete),
path('order/detail/', order.order_detail),
path('order/<int:nid>/edit/', order.order_edit),
views.py
import random
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from sainfo import models
# 免CSRF认证
from django.views.decorators.csrf import csrf_exempt
import json
from django.http import JsonResponse
from datetime import datetime
from sainfo.Utils.pagination import Pagination
# 定义ModelForm, 在模态框里显示需要的输入框
class OrderForm(forms.ModelForm):
class Meta:
model = models.Order
# '__all__' 所有字段
# fields = '__all__'
# ['需要的字段1', '需要的字段2', '需要的字段3', ......]
# fields = ['oid', 'title', 'price']
# exclude = ['排除的字段1', '排除的字段1', ......]
exclude = ['oid', 'admin']
# 该方法是通过修改内部代码,对样式进行修改,样式的值可根据需求随意修改
# 重新定义 init 方法
def __init__(self, *args, **kwargs):
# 重新定义 它 父类的 init 方法
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段设置插件 field.widget.attrs
for name, field in self.fields.items():
print(name, field)
# 判断,给某个字段不加插件
# if name == 'password':
# continue
# 字段的插件
field.widget.attrs = {
"class": "form-control",
# "placeholder": field.label
}
def order_list(request):
form = OrderForm()
print(f"当前登录id是:{request.session['info']['id']}")
print(f"当前登录name是:{request.session['info']['name']}")
# 根据session,只显示当前登录用户的所有记录
# queryset = models.Order.objects.filter(admin_id=request.session['info']['id']).order_by('-oid')
# 显示所有记录
queryset = models.Order.objects.all().order_by('-oid')
page_object = Pagination(request, queryset)
context = {
'form': form,
# 分完页的数据
'queryset': page_object.page_queryset,
# 生成页码
'page_string': page_object.html(),
}
return render(request, 'order_list.html', context=context)
@csrf_exempt # 免csrf认证
def order_add(request):
""" 创建订单(Ajax请求) """
form = OrderForm(data=request.POST)
if form.is_valid():
# 订单号:额外增加一些不是用户输入的值(自己计算出来)
# oid 字段在生成 form 时被排除了,需要自动生成
# 对象.instance.指定字段 来设置指定字段的值
# 当前时间+4数字
form.instance.oid = (datetime.now().strftime('%Y%m%d%H%M%S') +
str(random.randint(1000, 9999)))
# 固定设置管理员ID, 从session里获取当前用户
# 根据键读取值: request.session.get('键', 默认值)
# 或者: request.session['键']
# session 内容是字典,得到id
form.instance.admin_id = request.session['info']['id']
# 保存到数据库
form.save()
# 验证正常保存后,字典格式,使用 json.dumps() 返回给前端
# 或者使用JsonResponse()
# data_dict = {'status': 'ok'}
# return HttpResponse(json.dumps(data_dict))
# JsonResponse() 将字典序列化,生成Json格式字符串返回给前端
return JsonResponse({'status': True})
else:
# 如果保存失败,返回错误状态和错误信息
return JsonResponse({'status': False, 'error': form.errors})
def order_delete(request, nid):
""" 删除订单 """
# 如果前端使用 data 传递数据, nid = request.GET.get('uid')
# 验证是否存在
exists = models.Order.objects.filter(id=nid).exists()
if not exists:
return JsonResponse({'status': False, 'error': '删除失败,数据不存在'})
else:
models.Order.objects.filter(id=nid).delete()
return JsonResponse({'status': True, 'info': '删除成功'})
def order_detail(request):
""" 根据id获取订单详细信息 """
""" 方法一 """
"""
# 前端get请求,获得uid信息
uid = request.GET.get('uid')
# 根据id ,拿到当前行的对象,得到符合条件的 对象, 对象无法直接通过JSON返回给前端
row_object = models.Order.objects.filter(id=uid).first()
# 如果数据不存在
if not row_object:
return JsonResponse({'status': False, 'error': '修改失败,数据不存在'})
else: # 数据存在
# 因为要用JSON传递数到前端,要使用 对象.字段 手动生成字典格式
result = {
'title': row_object.title,
'price': row_object.price,
'status': row_object.status,
}
print(f"result:{result}")
# JsonResponse 返回到前端
return JsonResponse({'status': True, 'info': result})
"""
""" 方法二 values()"""
# 前端使用get请求,获得uid信息
uid = request.GET.get('uid')
# values() 返回一个Queryset对象,可遍历 列表里面是字典 为空的话 默认查出所有数据
row_queryset = models.Order.objects.filter(id=uid).values('title', 'price', 'status')
# <QuerySet [{'title': '空气净化器', 'price': 900, 'status': 2}]>
# .first() 取第一条数据,直接变成了字典
row_dict = models.Order.objects.filter(id=uid).values('title', 'price', 'status').first()
# {'title': '空气净化器', 'price': 900, 'status': 2}
if not row_dict: # 数据不存在
return JsonResponse({'status': False, 'error': '修改失败,数据不存在'})
else: # 数据存在
print(row_dict)
# JsonResponse 返回到前端
return JsonResponse({'status': True, 'info': row_dict})
@csrf_exempt
def order_edit(request, nid):
""" 更新数据 """
# 根据nid需要修改的那行,对象
row_object = models.Order.objects.filter(id=nid).first()
print(type(row_object))
# 验证是否存在
if not row_object:
return JsonResponse({'status': False, 'tips': '此条数据已经不存在'})
# 把要修改 的对象通过 instance 传入form组件中 必须为本类的对象
# 如果 instance 有对象则是修改数据 没有就是 新增数据
# data 来源数数据, instance 需要更新的数据
form = OrderForm(data=request.POST, instance=row_object)
if form.is_valid():
# oid字段 保持不变,原有数据
form.instance.oid = row_object.oid
# admin_id字段 根据session得到当前用户
form.instance.admin_id = request.session['info']['id']
form.save()
return JsonResponse({'status': True})
else:
print(form.errors)
return JsonResponse({'status': False, 'error': form.errors})
order_list.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div style="margin-bottom: 10px">
<!--使用 data-toggle="modal" data-target="#myModal" 属性 调用模态框-->
<input class="btn btn-primary" type="button" value="新建订单-使用属性" data-toggle="modal"
data-target="#myModal_add">
<!--增加id,使用JS 调用模态框 -->
<input id="btnAdd" class="btn btn-success" type="button" value="新建订单-使用JS">
</div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
<span class="glyphicon glyphicon-list" aria-hidden="true"></span>
Order列表
</div>
<!-- Table -->
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>订单号</th>
<th>名称</th>
<th>价格</th>
<th>状态</th>
<th>管理员</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for obj in queryset %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.oid }}</td>
<td>{{ obj.title }}</td>
<td>{{ obj.price }}</td>
<td>{{ obj.get_status_display }}</td>
<td>{{ obj.admin.username }}</td>
<td>
{# 不用a标签,使用input type="button", 增加样式 btn-edit, 样式选择器会使用 #}
{# 为标签自定义属性 #}
{# 增加属性 uid="{{ obj.id }}" 选择时会调用此属性操作 #}
{# 增加属性 oid="{{ obj.id }}" 选择时会调用此属性操作 #}
<input uid="{{ obj.id }}" oid="{{ obj.oid }}" class="btn btn-primary btn-xs btn-edit"
type="button" value="Edit">
{# 不用a标签,使用input type="button", 增加样式 btn-delete, 样式选择器会使用 #}
{# 为标签自定义属性 #}
{# 增加属性 uid="{{ obj.id }}" 选择时会调用此属性进行删除对象操作 #}
{# 增加属性 oid="{{ obj.id }}" 选择时会调用此属性进行删除对象操作 #}
<input uid="{{ obj.id }}" oid="{{ obj.oid }}" class="btn btn-danger btn-xs btn-delete"
type="button" value="Delete">
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<ul class="pagination">
{{ page_string }}
</ul>
</div>
<!-- 新建/编辑订单 模态框 -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel1">新建</h4>
</div>
<div class="modal-body">
<!-- 表单内容区域 -->
<form id="formAdd" novalidate>
{# class="clearfix" 清除内部的浮动#}
<div class="clearfix">
{# form 是 views传过来的对象,也是ModelForm自动生成的html内容#}
{% for field in form %}
{# 栅格 #}
<div class="col-xs-6">
{# position: relative 显示相对定位 #}
<div class="form-group" style="position: relative; margin-bottom: 20px">
{# filed.label 就是 字段的 verbose_name#}
{# field 是循环后得到的每一个字段的 input框#}
<label>{{ field.label }}:</label>
{{ field }}
{# position: absolute 显示绝对定位 漂浮在上面 #}
{# class="error-msg" 创建类,ajax 类选择器会使用 #}
<span class="error-msg1" style="color:red; position: absolute"></span>
</div>
</div>
{% endfor %}
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取 消</button>
<button type="button" class="btn btn-primary" id="btnSave">保 存</button>
</div>
</div>
</div>
</div>
<!-- 删除订单 模态框 -->
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<!-- id="deleteInfo" 增加id选择器, 通过选择器个修改显示内容 -->
<h4 id="deleteInfo">是否确定删除!</h4>
<p style="margin: 10px 0;">删除后,所有关联的相关数据都会被删除</p>
<p style="text-align: right">
<!-- id='btnConfirmDelete' id选择器会绑定事件 -->
<button id='btnConfirmDelete' type="button" class="btn btn-danger">确 定</button>
<!-- data-dismiss="modal" 关闭对话框 -->
<button type="button" class="btn btn-default" data-dismiss="modal">取 消</button>
</p>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
// 声明全局变量
// 声明了一个变量但没有对其进行赋值,该变量的值默认为undefined
var DELETE_ID;
var DELETE_OID;
// 如果EDIT_ID 等于 undefined, 执行添加,
// 如果EDIT_ID 有值,不等于 undefined , 执行编辑
var EDIT_ID;
// JQuery绑定事件写法
$(function () {
// 添加事件,弹出模态框, 与编辑同一个模态框
bindBtnAddEvent();
// 编辑事件 弹出模态框并用获取内容 与添加同一个模态框
bindBtnEditEvent();
// 保存事件 根据 EDIT_ID 的值,来判断添加或编辑
bindBtnSaveEvent();
// 删除事件,弹出模态确认框
bindBtnDeleteEvent();
// 删除事件
bindBtnConfirmDeleteEvent();
})
// 添加事件,弹出模态框
function bindBtnAddEvent() {
$("#btnAdd").click(function () {
// 当编辑使用过模态框,会有对应的id属性,再选择添加时,会把上次编辑模态框的id带过来
// 所以,新建模态框要把设置为空
EDIT_ID = undefined;
// 清空表单,这里要使用DOM对象的reset()方法
// 将JQuery对象: jQuery对象加前缀$,用以区分DOM对象 转化为JavaScript DOM 对象
// jQuery对象[索引值] 转成 DOM 对像
// $('#formAdd')是JQuery的对象 --> $('#formAdd')[0] 是 DOM 的对象
$('#formAdd')[0].reset();
// 因为和编辑共用一个模态框, 修改标题
$('#myModalLabel1').text('新建');
// 如果上次增加,没有填数据,仍然会有错误提示,需要每次打开都清一次
// ".error-msg1" 类选择器,每次执行时先把 span 标签清空
$(".error-msg1").empty();
// 详见 Bootstrap 手动打开模态框。
$('#myModal').modal('show')
})
}
// 编辑事件 弹出模态框 和 add 使用同一模态框
function bindBtnEditEvent() {
$('.btn-edit').click(function () {
// 清空form
$('#formAdd')[0].reset();
// 根据当前选择按钮属性拿到 id
// 定义一个局部变量
var current = $(this).attr('uid');
EDIT_ID = current;
console.log('当前选中的id是:' + current);
$.ajax({
url: '/order/detail/',
type: 'get',
data: {
uid: current,
},
dataType: 'JSON',
success: function (res) {
// 数据正常
if (res.status) {
// 因为和新增共用一个模态框, 修改标题
$('#myModalLabel1').text('编辑');
// 使用add同样的模态框, 显示模态对话枉
$('#myModal').modal('show');
$.each(res.info, function (key, value) {
console.log(key + '...' + value);
$('#id_' + key).val(value);
})
} else {
alert(res.error);
}
}
})
})
}
// btnSave按键,绑定事件:添加 or 删除
function bindBtnSaveEvent() {
$('#btnSave').click(function () {
// ".error-msg" 类选择器,每次执行时先把 span 标签清空
$(".error-msg1").empty();
// 思路核心, 有id时,执行编辑, 没有id时,执行新增
if (EDIT_ID) {
// 有数据 编辑
doEdit();
} else {
// 没有数据,执行增加
doAdd();
}
});
}
function doAdd() {
// 向后台发送请求 添加的Ajax请求
$.ajax({
url: '/order/add/',
type: 'post',
// serialize() 对选中的form表单内容进行序列化,返回给后端
// 不用分别对每个input框使用 val() 取值
data: $('#formAdd').serialize(),
// 接收后端的数据格式
dataType: 'JSON',
// res 接收后端的回传数据
success: function (res) {
if (res.status) {
alert('保存成功');
// 如果没有刷新页面,每次打开模态框,form仍会显示上次内容,
// 清空表单,这里要使用DOM对象的reset()方法
// 将JQuery对象: jQuery对象加前缀$,用以区分DOM对象 转化为JavaScript DOM 对象
// jQuery对象[索引值] 转成 DOM 对像
// $('#formAdd')是JQuery的对象 --> $('#formAdd')[0] 是 DOM 的对象
$('#formAdd')[0].reset(); // 只重置表单,不刷新页面
// 详见 Bootstrap 关闭模态框。
$('#myModal').modal('hide')
// 用JS实现整个页面刷新,
location.reload();
} else {
console.log(res.error);
// 循环错误信息
$.each(res.error, function (name, value) {
console.log(name + '......' + value);
// 失败,错误信息返回在前端页面
// 字符串拼接
// ModelForm 在生成 input 框时会自动增加 id_,格式是: id_属性名
// 比如这里的input框会自动生成: id="id_title", id="id_detail", id="id_user"
// '#id_'+ name, 拼接字符串,并选中标签。 name是遍历的字典key.
// next() 选中当前标签的下一个标签,这里就是input标签的下一个是span标签
// text(value[0]), value是数组,取第0个元素,jQuery对象text()写入内容
$('#id_' + name).next().text(value[0]); // #id选择器
})
}
}
})
}
function doEdit() {
// 向后台发送请求 添加的Ajax请求
// ajax将数据传递给后端
$.ajax({
url: "/order/" + EDIT_ID + "/edit/",
type: 'post',
dataType: 'JSON',
// serialize() 对选中的form表单内容进行序列化,返回给后端
// 不用分别对每个input框使用 val() 取值
data: $('#formAdd').serialize(),
success: function (res) {
if (res.status) { // 成功
alert('更新完成');
$('#myModal').modal('hide')
// 用JS实现整个页面刷新,
location.reload();
} else { // 失败
if (res.tips) { // 如果有tips,弹出不存在的错误
alert(res.tips);
} else {
console.log(res.error);
$.each(res.error, function (key, value) {
console.log(key + '......' + value);
// #id选择器
$('#id_' + key).next().text(value[0]);
})
}
}
}
})
}
/* 删除事件-思路
1.在删除按钮增加属性: uid, 此uid等于每次循环queryset的id.
2.设置全局变量: var DELETE_ID;
3.第一个点击事件,弹开模态框:$("选择器").modal('show');
4.通过 $obj.attr(name) 获取 删除按钮的 uid 属性,并赋值给全局变量 DELETE_ID。
DELETE_ID = $(this).attr('uid')
5.对弹出模态框的 确定 按键绑定事件 id='btnConfirmDelete'
6.Ajax, get方式传递数据
7.拼接url: url: '/order/' + DELETE_ID + '/delete/',
8.根据 urls.py 中 order/<int:nid>/delete/, 将 nid 做为参数传递给views对应方法
9.验证id=nid的数据是否存在:
不存在,返回给前端 错误提示
存在,views对应方法执行删除,返回给前端 正常提示
10.前端关闭模态框,重新刷新页面
$("#deleteModal").modal('hide');
location.reload(); */
// 删除事件 打开删除模态框, 将选中的id 赋值给全局变量 DELETE_ID
function bindBtnDeleteEvent() {
// 类选择器,选中对象
$('.btn-delete').click(function () {
// alert('点击了删除')
// 显示删除对话框 id选择器
// 详见 Bootstrap 打开模态框。
$("#deleteModal").modal('show');
// 需要获取的属性名称,返回对应的属性值
// $obj.attr(name)
// 获取当前选中行的 uid属性={{ obj.id }}, 并赋值给全局变量 DELETE_ID
DELETE_ID = $(this).attr('uid');
// 获取当前选中行的 oid属性={{ obj.oid }}, 并赋值给全局变量 DELETE_OID
DELETE_OID = $(this).attr('oid');
// ('#deleteInfo') id选择器,选中对象
// JQuery text()或html()方法, 字符串拼接,模态框内显示要输入的内容
$('#deleteInfo').text("Warning! 是否删除订单号:" + DELETE_OID);
console.log(DELETE_ID);
})
}
// 删除事件 Ajax get传递给后端,
function bindBtnConfirmDeleteEvent() {
// id选择器,选中对象
$('#btnConfirmDelete').click(function () {
// 点击确认删除按钮, 将全局变量 DELETE_ID 发送给后端
$.ajax({
// 字符串拼接 /order/123/delete/
// DELETE_ID 在 urls.py 被转译为 nid
url: '/order/' + DELETE_ID + '/delete/',
// url: '/order/delete/', // /order/delete/?uid=123
type: 'get',
// data: {uid: DELETE_ID},
dataType: 'JSON',
success: function (res) {
// 删除成功, 状态是 True
if (res.status) {
// alert(res.info);
// 详见 Bootstrap 关闭模态框。
$("#deleteModal").modal('hide');
// 页面刷新,列表更新
location.reload();
} else {
// 删除失败
alert(res.error)
}
}
})
});
}
</script>
{% endblock %}