之前我们的浏览器向网页发送请求都是以URL和表单的形式提交的(POST, GET),基本上都能实现我们想要的功能。那为什么要用Ajax请求?之前的方法不是挺好的嘛。
之前的方法在每次提交请求的时候都会刷新页面,这是一个弊端。举个例子,在做验证码的时候,如果我们还是用原来的方法发送请求,就会导致每次换验证码的时候,都要重新输入账号和密码。
已经完成的是login界面和几个管理界面,但是大多数请求都不是用Ajax写的。
我们基于Ajax请求完成过简单的操作,算是入门级的实战(传送门),这次的实战是上一次的进阶版。
新建订单
删除订单
修改订单
查询订单
因为我们的工作是建立在以前工作的基础上的,因此不可避免的会用到以前的一些成果,现在这里罗列。
static 包含各种静态文件,css样式,jquery等
templates 包含各种html文件,所有的静态页面都是这里出来的
utils 包含各种中途会用到的工具
- bootstrap_input.py 继承ModelForm,在其中嵌入Bootstrap样式
- encrypt.py 利用md5加密密码
- form.py 所有的ModelForm存这里
- page.py 分页类,负责为界面添加分页功能
views
- admin.py 管理员界面的views放这里
- depart.py 部门界面的views放这里
- num.py 号码界面的views放这里
- user.py 用户界面的views放这里
__init__.py 略
admin.py 略
apps.py 略
models.py 存放我们对数据库的数据表的定义
test.py 略
Employee_Management
- __init__.py 略
- asgi.py 略
- settings.py 略
- urls.py views内的py文件中各个函数与templates内html文件的对应关系
- wsgi.py 略
因为Django自带的分页组件不太好用,我们自己写了个分页类PAGE
# -*- coding:utf-8 -*-
# 自定义分页主键
from django.utils.safestring import mark_safe
class PAGE(object):
def __init__(self, request, queryset, page_size=10, plus=5, page_param="page"):
"""
:param request: 进入这个页面时的request,请求对象
:param queryset: 符合条件的对象,就是经过搜索筛选之后仍符合要求的数据
:param page_size: 每页发的那个多少行数据
:param plus: 分页时显示本页前后plus页的页码
:param page_param: 再url中传递的获取分页的参数
"""
# 获取当前页码
page = request.GET.get(page_param, "1")
# 防止页码为字母等等
if page.isdecimal():
page = int(page)
else:
page = 1
# 获取总样本数
total_count = queryset.count()
total_page_count, div = divmod(total_count, page_size)
if div:
total_page_count += 1
self.total_page_count = total_page_count
self.page = page
# 防止搜索的时候超限
if self.page < 1:
self.page = 1
if self.page > self.total_page_count:
self.page = self.total_page_count
self.plus = plus
self.page_param = page_param
self.page_size = page_size
self.start = (self.page - 1) * self.page_size * (self.page != 0)
self.end = self.page * self.page_size
# 搜索的时候分页会把原来搜索的条件替换掉,用这四行代码解决
import copy
querydict = copy.deepcopy(request.GET) # 把包含原来所有参数的网址给弄过来
querydict.mutable = True # 这句话要看源码去找_mutable
self.query_dict = querydict
self.page_queryset = queryset[self.start: self.end]
"""生成html代码"""
def create_html(self):
# 显示当前页的前plus页和后plus页
if self.total_page_count <= 2 * self.plus + 1:
start_page = 1
end_page = self.total_page_count
else:
if self.page <= self.plus:
start_page = 1
end_page = 2 * self.plus + 1
else:
if (self.page + self.plus) > self.total_page_count:
start_page = self.total_page_count - 2 * self.plus
end_page = self.total_page_count
else:
start_page = self.page - self.plus
end_page = self.page + self.plus
page_str_list = list()
self.query_dict.setlist(self.page_param, [1])
# 首页
page_str_list.append('- 首页
'.format(self.query_dict.urlencode()))
# 上一页
if self.page > 1:
self.query_dict.setlist(self.page_param, [self.page - 1])
prev = '- 上一页
'.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [1])
prev = '- 上一页
'.format(self.query_dict.urlencode())
page_str_list.append(prev)
# 展示页
for i in range(start_page, end_page + 1):
self.query_dict.setlist(self.page_param, [i])
if i == self.page:
ele = '- {}
'.format(self.query_dict.urlencode(), i)
else:
ele = '- {}
'.format(self.query_dict.urlencode(), i)
page_str_list.append(ele)
# 下一页
if self.page < self.total_page_count:
self.query_dict.setlist(self.page_param, [self.page + 1])
nex = '- 下一页
'.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [self.total_page_count])
nex = '- 下一页
'.format(self.query_dict.urlencode())
page_str_list.append(nex)
# 尾页
self.query_dict.setlist(self.page_param, [self.total_page_count])
page_str_list.append('- 尾页
'.format(self.query_dict.urlencode()))
page_string = mark_safe("".join(page_str_list))
return page_string
"""搜索页码"""
def search(self):
search_string = """
"""
return mark_safe(search_string)
Django有自己的模板语言,可以在前端继承模板,这里附上模板layout.html
{% load static %}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Olsentitle>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap-datetimepicker.min.css' %}">
<style>
.navbar{
border-radius: 0;
}
style>
head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
<span class="icon-bar">span>
<span class="icon-bar">span>
<span class="icon-bar">span>
button>
<a class="navbar-brand" href="#">Olsen用户管理系统a>
div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/admin/list">管理员账户a>li>
<li><a href="/depart/list">部门管理a>li>
<li><a href="/user/list">用户管理a>li>
<li><a href="/num/list">靓号管理a>li>
<li><a href="/task/list">任务管理a>li>
<li><a href="/order/list">订单管理a>li>
<li><a href="/chart/list">数据统计a>li>
ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">{{ request.session.info.username }} <span class="caret">span>a>
<ul class="dropdown-menu">
<li><a href="#">个人资料a>li>
<li><a href="#">我的信息a>li>
<li role="separator" class="divider">li>
<li><a href="/logout">注销a>li>
ul>
li>
ul>
div>
div>
nav>
<div>
{% block content %}
{% endblock %}
div>
<script src="{% static 'js/jquery.js' %}">script>
<script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
{% block js %}
{% endblock %}
body>
html>
前端代码order_list.html
,主要包含订单新建按钮、模板语言展示订单、对话框内添加/编辑订单、Ajax请求的js函数。有点小长,但是注释比较详细。
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div>
<input id="btnAdd" type="button" value="新建订单" class="btn btn-primary">
<div style="float: right; width: 300px;">
<form method="get">
<div class="input-group">
<input type="text" class="form-control" placeholder="搜索订单号" name="search" value="{{search_data}}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search">span>
button>
span>
div>
form>
div>
div>
<p style="margin: 0;"> p>
<div class="panel panel-default">
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true">span>
订单列表
div>
<table class="table table-bordered">
<thead>
<tr>
<th>IDth>
<th>订单号th>
<th>名称th>
<th>价格th>
<th>状态th>
<th>管理员th>
<th>操作th>
tr>
thead>
<tbody>
{% for obj in queryset %}
<tr uid="{{ obj.id }}">
<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>
<input uid="{{ obj.id }}" type="button" class="btn btn-warning btn-xs btn-edit" href="#" value="编 辑">
<input uid="{{ obj.id }}" type="button" class="btn btn-danger btn-xs btn-delete" href="#" value="删 除">
td>
tr>
{% endfor %}
tbody>
table>
div>
<ul class="pagination" style="width:100%;">
{{ page_string }}
{{ search_page }}
ul>
div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<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="myModalLabel">新 建h4>
div>
<div class="modal-body">
<form id="addForm" novalidate>
<div class="clearfix">
{% for field in form %}
<div class="col-xs-6">
<div class="form-group" style="position: relative; margin-bottom: 20px">
<label>{{ field.label }}label>
{{ field }}
<span class="error-msg" 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 id="btnSave" type="button" class="btn btn-primary">保 存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">
<h3>是否确定删除?h3>
<p style="margin: 10px 0;">删除后数据不可恢复p>
<p>删除该数据将导致其他数据表相关数据也被删除p>
<p style="text-align: right;">
<button type="button" id="btnConfirmDelete" class="btn btn-danger">确 定button>
<button type="button" class="btn btn-default" data-dismiss="modal">取 消button>
p>
div>
div>
div>
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
var DELETE_ID; // 用于标注是否为删除命令以及操作对象
var EDIT_ID; // 用于标注是否为编辑命令以及操作对象
bindBtnAddEvent(); // 这个函数完成点击按钮跳出对话框
bindBtnSaveEvent(); // 这个函数完成保存数据
bindBtnDeleteEvent(); // 这个函数用于删除数据
bindBtnConfirmDeleteEvent();// 这个函数用于删除时跳出是否确认删除的框
bindBtnEditEvent(); // 这个函数用于编辑数据
})
function bindBtnAddEvent() {
$("#btnAdd").click(function () {
EDIT_ID = undefined; // 清空编辑标志,不然编辑后新建还是编辑效果
$("#addForm")[0].reset(); // 注意要清空
$('#myModalLabel').text('新 建');
$('#myModal').modal('show'); // 显示对话框
});
}
function bindBtnSaveEvent() {
$("#btnSave").click(function () {
$(".error-msg").empty(); // 清空报错
if(EDIT_ID) {
Edit_ajax();
} else {
Add_ajax();
}
function Add_ajax() {
$.ajax({
url: "/order/add",
type: "post",
data: $("#addForm").serialize(), // 序列化addForm的信息,传入
dataType: "JSON",
success: function (res) {
if(res.status){
alert('创建成功')
// 清空表单, jquerry无置空功能,我们这里算是取一个dom对象(加了[0]之后)操作
$("#addForm")[0].reset();
// 关闭对话框
$('#myModal').modal('hide');
// 刷新
location.reload();
}else{
$.each(res.error, function(name, error_list){
$('#id_' + name).next().text(error_list[0]);
})
}
}
})
}
function Edit_ajax() {
$.ajax({
url: "/order/edit/?uid=" + EDIT_ID,
type: "post",
data: $("#addForm").serialize(),
dataType: "JSON",
success: function (res) {
if(res.status){
alert('编辑成功')
// 清空表单, jquerry无置空功能,我们这里算是取一个dom对象(加了[0]之后)操作
$("#addForm")[0].reset();
// 关闭对话框
$('#myModal').modal('hide');
// 刷新
location.reload();
}else{
if(res.tips){
alert(res.tips);
} else {
$.each(res.error, function(name, error_list){
$('#id_' + name).next().text(error_list[0]);
})
}
}
}
})
}
});
}
function bindBtnDeleteEvent() {
$(".btn-delete").click(function () {
$('#deleteModal').modal('show');
// 获取当前行的id并赋值给全局变量(=>当前点击到这个标签的时候)
DELETE_ID = $(this).attr("uid");
});
}
function bindBtnConfirmDeleteEvent(){
$("#btnConfirmDelete").click(function () {
$.ajax({
// url: "/order/" + DELETE_ID + "/delete",
url: "/order/delete",
type: "GET",
data:{
uid: DELETE_ID
},
dataType: "JSON",
success: function (res) {
if(res.status){
// alert("删除成功");
// $('#myModal').modal('hide');
// $("tr[uid='" + DELETE_ID + "']").remove();
// DELETE_ID = 0;
location.reload();
}else{
alert(res.error);
}
}
})
})
}
function bindBtnEditEvent() {
$(".btn-edit").click(function () {
var currentId = $(this).attr('uid');
EDIT_ID = currentId;
$.ajax({
url: "/order/detail",
type: "GET",
data:{
uid: currentId
},
dataType: "JSON",
success: function (res) {
if(res.status){
// 利用循环给edit里面的输入框内加入默认值
$.each(res.data, function(name, value) {
$("#id_" + name).val(value);
})
$('#myModalLabel').text('编 辑');
$('#myModal').modal('show');
}else{
alert(res.error);
}
}
})
});
}
script>
{% endblock %}
在上面的html文件 中提到了几个url,这里附上urls.py
中的url路径:
"""Employee_Management URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from application01.views import depart, num, user, admin, account, task, order, chart
urlpatterns = [
# path('admin/', admin.site.urls),
path('depart/list', depart.depart_list),
path('depart/add', depart.depart_add),
path('depart/del', depart.depart_del),
path('depart//edit' , depart.depart_edit),
path('user/list', user.user_list),
path('user/add', user.user_add),
path('user/madd', user.user_madd),
path('user//edit' , user.user_edit),
path('user//del' , user.user_del),
path('num/list', num.num_list),
path('num/add', num.num_add),
path('num/a', num.num_a),
path('num//edit' , num.num_edit),
path('num//del' , num.num_del),
path('admin/list', admin.admin_list),
path('admin/add', admin.admin_add),
path('admin//edit' , admin.admin_edit),
path('admin//del' , admin.admin_del),
path('admin//reset' , admin.admin_reset),
path('login', account.login),
path('logout', account.logout),
path('image/code', account.image_code),
path('task/list', task.task_list),
path('task/add', task.task_add),
path('order/list', order.order_list), // 订单列表显示
path('order/add', order.order_add), // 添加订单
path('order/delete', order.order_delete), // 删除订单
path('order/detail', order.order_detail), // 加载编辑操作中的订单信息
path('order/edit/', order.order_edit), // 编辑订单
path('chart/list', chart.chart_list),
path('chart/bar', chart.chart_bar),
path('chart/pie', chart.chart_pie),
path('chart/line', chart.chart_line)
]
这些函数所对应的路径置于views文件夹中的order.py
文件。
# -*- coding:utf-8 -*-
import random
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from application01.utils.form import OrderModelForm
from django.http import JsonResponse
from datetime import datetime
from application01.utils.page import PAGE
from application01 import models
def order_list(request):
search_dict = dict()
# 搜索功能
value = request.GET.get("search", "")
if value:
search_dict["oid__contains"] = value
form = OrderModelForm()
# 保证展示的是搜索后的结果
queryset_list = models.Order.objects.filter(**search_dict).order_by('-id')
# 调用分页类
page_obj = PAGE(request, queryset_list, page_size=7)
context = {
'form': form,
"search_data": value,
'queryset': page_obj.page_queryset,
'page_string': page_obj.create_html(),
"search_page": page_obj.search()
}
return render(request, 'order_list.html', context)
# Ajax的post请求需要加这个
@csrf_exempt
def order_add(request):
form = OrderModelForm(data=request.POST)
# 判断输入是否为空
if form.is_valid():
# 用户没权限输入oid,我们动态计算出一个
form.instance.oid = datetime.now().strftime('%Y%m%d%H%M%S') + str(random.randint(1000, 9999))
# 管理员直接在session中拿,因为管理员已经登录了
form.instance.admin_id = request.session['info']['id']
form.save()
return JsonResponse({"status": True})
return JsonResponse({"status": False, 'error': form.errors})
def order_delete(request):
uid = request.GET.get('uid')
# 先判断这个uid还在不在
exists = models.Order.objects.filter(id=uid).exists()
if not exists:
return JsonResponse({'status': False, 'error': '删除失败,数据不存在!!!'})
models.Order.objects.filter(id=uid).delete()
return JsonResponse({'status': True})
def order_detail(request):
uid = request.GET.get('uid')
# 得到对象
row_obj = models.Order.objects.filter(id=uid).values("title", "price", "status").first()
if not row_obj:
return JsonResponse({'status': False, 'error': '删除失败,数据不存在!!!'})
# 返回对象的详细信息
result = {
"status": True,
"data": row_obj,
}
return JsonResponse(result)
@csrf_exempt
def order_edit(request):
uid = request.GET.get('uid')
row_obj = models.Order.objects.filter(id=uid).first()
if not row_obj:
return JsonResponse({'status': False, 'tips': '编辑失败!数据不存在!!!'})
form = OrderModelForm(data=request.POST, instance=row_obj)
if form.is_valid():
form.save()
return JsonResponse({'status': True})
return JsonResponse({'status': True, 'error': form.errors})