从零开始的Django框架入门到实战教程(内含实战实例) - 10 初试Ajax之订单界面(学习笔记)

目录

  • 0. Why Ajax?
  • 1. 既有工作
  • 2. 实战
    • 2.1. 效果展示
    • 2.2. 讲在前面
      • 2.2.0. 文件夹介绍
      • 2.2.1. 分页功能
      • 2.2.2. 前端模板
    • 2.3. 代码展示

  Django是目前比较火爆的框架,之前有在知乎刷到,很多毕业生进入大厂实习后因为不会git和Django框架3天就被踢掉了,因为他们很难把自己的工作融入到整个组的工作中。因此,我尝试自学Django并整理出如下笔记。

0. Why Ajax?

  之前我们的浏览器向网页发送请求都是以URL和表单的形式提交的(POST, GET),基本上都能实现我们想要的功能。那为什么要用Ajax请求?之前的方法不是挺好的嘛。

  之前的方法在每次提交请求的时候都会刷新页面,这是一个弊端。举个例子,在做验证码的时候,如果我们还是用原来的方法发送请求,就会导致每次换验证码的时候,都要重新输入账号和密码。

1. 既有工作

  已经完成的是login界面和几个管理界面,但是大多数请求都不是用Ajax写的。

  我们基于Ajax请求完成过简单的操作,算是入门级的实战(传送门),这次的实战是上一次的进阶版。

2. 实战

2.1. 效果展示

  新建订单

  删除订单

  修改订单

  查询订单

2.2. 讲在前面

  因为我们的工作是建立在以前工作的基础上的,因此不可避免的会用到以前的一些成果,现在这里罗列。

2.2.0. 文件夹介绍

从零开始的Django框架入门到实战教程(内含实战实例) - 10 初试Ajax之订单界面(学习笔记)_第1张图片

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					略

2.2.1. 分页功能

  因为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)

    2.2.2. 前端模板

      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>
    

    2.3. 代码展示

      前端代码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})
    

    你可能感兴趣的:(#,Django,前端,django,ajax,学习)