Python入门自学进阶-Web框架——11Django实践小项目

Posted kaoa000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——11Django实践小项目相关的知识,希望对你有一定的参考价值。

以学生、老师、班级管理实现一个小的管理项目。

基本的界面

 前端页面的总的框架,因为页头、左侧菜单栏基本是始终保持一致,只是右边内容随不同的菜单项改变,所以,使用一个lindex_base.html作为框架模板。模板中使用块标记来区分不同菜单对应的内容。如下index_base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body
            margin: 0;
        
        .menu .item
            display: block;
            padding: 5px 10px;
            border-bottom: 1px solid #dddddd;
          # 菜单项是使用a标签,内联标签,改为块级标签,分行显示,并显示一个下划线,美化界面 #
        
        .menu .item:hover
            background-color: black;
            color: white;
         # 鼠标移动到菜单项上时,改变背景及颜色,作为提醒 #
        
        .menu .item.active
            background-color: black;
            color: white;
        
        # 这个active样式用于在选择了相应菜单后,使菜单呈现选中的状态,提示当前操作是哪个菜单,动态添加 #
        % block css %% endblock %
    </style>
</head>
<body>
    <div style="height: 48px;background-color: black;color: white">
        <div style="float: right">当前用户: username  | <a href="logout.html">注销</a></div>
    </div>

    <div>
        <div class="menu" style="position: absolute;top:48px;left: 0;bottom: 0;width: 200px;background-color: #eeeeee">
            <a id="menu_class" class="item" href="classes.html">班级管理</a>
            <a id="menu_student" class="item" href="student.html">学生管理</a>
            <a id="menu_teacher" class="item" href="teacher.html">老师管理</a>
        </div>
        <div style="position: absolute;top:48px;left: 200px;bottom: 0px;right: 0;overflow: auto">
            % block content %% endblock %
        </div>
    </div>
<script src="/static/jquery-3.6.0.js"></script>
#  上面的src的写法要注意,使用pycharm时,会提示具体的文件夹,但是django下应该使用settings.py中的设置,是static,否则前端找不到  #
% block js % % endblock %
</body>
</html>

其他的具体内容页面继承这个页面并进行具体的块填充

1)index.html,欢迎界面

% extends "index_base.html" %
% block css %

% endblock %

% block content %
    <h1>欢迎访问后台管理系统</h1>
% endblock %

% block js %

% endblock %

2)classes.html,班级管理页面

% extends "index_base.html" %
% block css %

% endblock %

% block content %
    <h1>班级管理页面</h1>
% endblock %

% block js %

% endblock %

3)student.html学生管理页面

% extends "index_base.html" %
% block css %

% endblock %

% block content %
    <h1>学生管理页面</h1>
% endblock %

% block js %

% endblock %

5)teacher.html老师管理页面

% extends "index_base.html" %
% block css %

% endblock %

% block content %
    <h1>老师管理页面</h1>
% endblock %

% block js %

% endblock %

通过上面的代码框架,可以看出,班级、学生、老师页面只需要填充相应的块内容就可以了,代码简洁、复用高。

当点击了左侧菜单后,要将点击的菜单样式设为active,这是要动态添加的,需要使用js脚本在点击后动态添加,就是点击了哪一个菜单,就为哪一个菜单添加active样式:如下

% extends "index_base.html" %
% block css %

% endblock %

% block content %
    <h1>班级管理页面</h1>
% endblock %

% block js %
    <script>
        $(function () 
            $('#menu_class').addClass('active');
        )
    </script>
% endblock %

老师和学生的页面,只需将$('#menu_class').addClass('active');中的menu_class改为menu_teacher和menu_student。

以班级管理为例,进行功能开发,页面如下:

 增加班级时模拟使用模态对话框:形式如下:

def auth(func):
    def inner(req,*args,**kwargs):
        is_login = req.session.get('is_login')
        print(is_login)
        if is_login:
            return func(req,*args,**kwargs)
        else:
            return redirect('login.html')
    return inner

@auth
def index(req):
    current_user = req.session.get('username')
    return render(req, "index.html", 'username':current_user)

@auth
def classes(req):
    if req.method == "GET":
        current_user = req.session.get('username')
        cls_list = models.Classes.objects.all()
        ret = render(req, "classes.html", 'username':current_user,'cls_list':cls_list)
        return ret
    elif req.method == "POST":
        # 以下代码是form表单提交时的处理方式
        caption = req.POST.get('caption',None)
        if caption:
            models.Classes.objects.create(caption=caption)
        return redirect('classes.html')
       

% extends "index_base.html" %
% block css %

% endblock %

% block content %
    <h1>班级管理页面——班级列表</h1>
    <hr>
    <div>
        <input id="id_add" type="button" value="增加班级">
    </div>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>班级名称</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            % for item in cls_list %
            <tr>
                <td> item.id </td>
                <td> item.caption </td>
                <td>
                    <a>修改</a> | <a>删除</a>
                </td>
            </tr>
            % endfor %
        </tbody>
    </table>
    #  下面是增加班级时的操作页面层,一开始是处于隐藏状态,所以class中有hide样式  #
    #  增加操作测试两种提交方式,一是form表单提交,二是Ajax提交  #
    <div class="modal hide">
        <form method="post" action="classes.html">
            % csrf_token %
            <input name="caption" type="text" placeholder="标题"><br> capmsg 
            # placeholder属性在输入框中默认显示一个内容,当焦点进入输入框后自动消失 #
            <input id="id_modal_cancel" type="button" value="取消增加">
            <input type="submit" value="submit确定">
            <input id="modal_ajax_submit" type="button" value="Ajax确定">
        </form>
    </div>
    #  下面定义一个遮罩层,用于模拟模态对话框,位置在上一个div,即增加操作层的下面  #
    <div class="shade hide"></div>
    #  下面是点击删除时弹出的操作页面,未操作前也是隐藏状态,增加hide样式  #
    <div class="remove hide">
        <input id="id_remove_cancel" type="button" value="取消">
        <input type="button" value="确定">
    </div>
% endblock %

% block js %
    <script>
        # 页面加载完毕后执行 #
        $(function () 
            $('#menu_class').addClass('active');
            bindAddEvent();
        )
        # 下面的函数给增加班级按钮添加click事件,即点击增加班级按钮时执行此函数 #
        # 函数的功能就是将modal和shade这两个标签的hide样式删除,使之显示出来。 #
        function bindAddEvent() 
            $('#id_add').click(function () 
                $('.modal,.shade').removeClass('hide');
            )
        
    </script>
% endblock %

以上是使用form表单提交的方式。后台的代码很简单,判断是post提交时,判断班级名是否为空,为空则不增加,直接跳转到班级管理页面,不为空,数据库增加班级,然后在跳转班级管理页面。

跳转后页面重新刷新,新增加的记录在最后显示。

上面的代码在功能上有点缺陷,我本来想增加一个提示,在数据增加成功时,前端提示增加成功提示框,如果为班级名为空,也有一个提示,班级名不能为空。但是这个看似简单的功能实现起来却异常困难,一直无法解决。主要是点击“submit确定”按钮,后台使用了redirect进行重定向,这时如何向客户端传递内容?一开始想的是如render一样,加一个键值对传过去,但是redirect不支持,后来想用cookie,结果一直无法设置成功。留待以后解决吧。

使用Ajax提交方式:

% extends "index_base.html" %
% block css %

% endblock %

% block content %
    <h1>班级管理页面——班级列表</h1>
    <hr>
    <div>
        <input id="id_add" type="button" value="增加班级">
    </div>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>班级名称</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            % for item in cls_list %
            <tr>
                <td> item.id </td>
                <td> item.caption </td>
                <td>
                    <a>修改</a> | <a class="td-delete">删除</a>
                </td>
            </tr>
            % endfor %
        </tbody>
    </table>
    #  下面是增加班级时的操作页面层,一开始是处于隐藏状态,所以class中有hide样式  #
    #  增加操作测试两种提交方式,一是form表单提交,二是Ajax提交  #
    <div class="modal hide">
        <form method="post" action="classes.html">
            % csrf_token %
            <input name="caption" type="text" placeholder="标题"><br>
            # placeholder属性在输入框中默认显示一个内容,当焦点进入输入框后自动消失 #
            <input id="id_modal_cancel" type="button" value="取消增加">
            <input type="submit" value="submit确定">
            <input id="modal_ajax_submit" type="button" value="Ajax确定">
        </form>
    </div>
    #  下面定义一个遮罩层,用于模拟模态对话框,位置在上一个div,即增加操作层的下面  #
    <div class="shade hide"></div>
    #  下面是点击删除时弹出的操作页面,未操作前也是隐藏状态,增加hide样式  #
    <div class="remove hide">
        <input name="caption" type="text" placeholder="标题"><br>
        <input id="id_remove_cancel" type="button" value="取消">
        <input type="button" value="确定">
    </div>
% endblock %

% block js %
    <script>
        # 页面加载完毕后执行 #
        $(function () 
            $('#menu_class').addClass('active');
            bindAddEvent();
            bindAddEventAjax();
            bindCancelEvent();
            bindTdDeleteEvent();
        )
        # 下面的函数给增加班级按钮添加click事件,即点击增加班级按钮时执行此函数 #
        # 函数的功能就是将modal和shade这两个标签的hide样式删除,使之显示出来。 #
        function bindAddEvent() 
            $('#id_add').click(function () 
                $('.modal,.shade').removeClass('hide');
            )
        
        # 下面函数是对取消按钮的绑定事件,点击取消时,将modal和shade这个两个div增加hide类,隐藏 #
        function bindCancelEvent() 
            $('#id_modal_cancel,#id_remove_cancel').click(function () 
                $('.modal,.shade,.remove').addClass('hide');
            )
        
        # 下面函数是删除按钮的对应函数,这里暂时只显示删除的输入界面,没做数据的提交 #
        function bindTdDeleteEvent() 
            $('td .td-delete').click(function () 
                $('.remove,.shade').removeClass('hide');
            )
        
        # 使用Ajax方式进行数据的添加 #
        function bindAddEventAjax() 
            $('#modal_ajax_submit').click(function () 
                var val = $('.modal input[name="caption"]').val();
                $.ajax(
                    url:'classes.html',
                    type: 'POST',
                    data:caption:val,csrfmiddlewaretoken: ' csrf_token ',
                    # 数据这里要增加django的csrf安全验证,即发送时要将cookie中的csrftoken一起发送 #
                    dataType:"JSON",
                    success:function (rep_data) 
                        #data_t = JSON.parse(rep_data);  如果上面没有dataType设置,这里需要自己手动转换为json对象#
                        if(!rep_data.status)
                            alert(rep_data.error);
                        else
                            #location.reload(); # 第一种成功后的方法:当前页面刷新,这是班级增加成功时刷新页面 #
                            # 第二种方法,不刷新页面,直接创建元素添加到页面 #
                            var tr = document.createElement('tr');
                            var td1 = document.createElement('td');
                            td1.innerHTML = rep_data.data.id;
                            var td2 = document.createElement('td');
                            td2.innerHTML = rep_data.data.caption;
                            var td3 = document.createElement('td');
                            td3.innerText = "|";
                            var a1 = document.createElement('a');
                            a1.innerHTML = "编辑";
                            var a2 = document.createElement('a');
                            a2.className = "td-delete";
                            a2.innerHTML = "删除";
                            $(td3).prepend(a1);
                            $(td3).append(a2);
                            $(tr).append(td1);
                            $(tr).append(td2);
                            $(tr).append(td3);
                            $('table tbody').append(tr);
                            $('.modal,.shade').addClass('hide');

                        
                    
                )
            )
        
    </script>
% endblock %

后台处理修改一下:

@auth
def classes(req):
    print(';;;;;',req.GET.get('flags',None))
    if req.method == "GET":
        current_user = req.session.get('username')
        cls_list = models.Classes.objects.all()
        ret = render(req, "classes.html", 'username':current_user,'cls_list':cls_list)
        return ret
    elif req.method == "POST":
        # 以下代码是form表单提交时的处理方式
        caption = req.POST.get('caption',None)

        # form表单提交时的后台处理,使用了redirect跳转
        # if caption:
        #     models.Classes.objects.create(caption=caption)
        # return redirect('classes.html')

        # Ajax提交时的后台处理
        response_dict = 'status':True,'error':None,'data':None
        if caption:
            obj = models.Classes.objects.create(caption=caption)
            response_dict['data'] = 'id':obj.id,'caption':obj.caption
            print('添加成功')
        else:
            response_dict['status'] = False
            response_dict['error'] = '班名不能为空'
        return HttpResponse(json.dumps(response_dict))

以上是Ajax的提交方法,这里有一个问题,就是ajax提交后,局部刷新classes.html页面增加的数据,其编辑 | 删除所对应的事件没有加上,点击时没有反应。主要是元素的.click事件是一个静态的绑定,对之后动态添加的事件无能为力,改为使用具有事件委托功能的on方法进行绑定。

on方法使用:假设:

<ul>
    <li>菜单1</li>
    <li>菜单2</li>
</ul>

给li元素绑定事件:

1、$('li').click(function());
2、$('li').on('click',function());
3、$('ul').on('click','li',function());

第1、2种等价,属于静态绑定,第3种的写法注意,是父元素使用on,在方法参数2中写被绑定的元素,是事件委托形式,动态添加。注意,第3中方法的ul不能是动态添加的

function bindTdDeleteEvent() 
            // $('td .td-delete').click(function () 
            //   $('.remove,.shade').removeClass('hide');
            // )  // s上面是静态绑定
            //下面的是事件委托,动态绑定,注意,使用tbody进行委托,不能使用tr或td,因为这两个也是动态添加的。
            $('tbody').on('click','.td-delete',function () 
                $('.remove,.shade').removeClass('hide');
            )
        

前面的添加是使用了模态对话框进行添加,这适合数据量少,逻辑简单的情况,如果数据量很大,最好使用单独的页面:

<!--将下面的语句修改 -->
<div>
        <input id="id_add" type="button" value="增加班级">
</div>

<!--改为a标签,单独开一个页面 -->
<div>
        <a href="add_class.html">增加班级</a>
</div>

增加一个页面:add_class.html

% block content %
    <h1>添加班级页面</h1>
    <hr>
    <form action="add_class.html" method="post">
        % csrf_token %
        <input type="text" name="caption" placeholder="班级名称"> msg <br>
        <input type="submit" value="submit确定">
    </form>
% endblock %

后台视图:

@auth
def add_class(req):
    message = ""
    if req.method == "GET":
        return render(req,'add_class.html','msg':message)
    else:
        caption = req.POST.get('caption', None)
        if caption:
            models.Classes.objects.create(caption=caption)
            return redirect('classes.html')
        else:
            message = "名称不能为空"
            return render(req,'add_class.html','msg':message)

班级信息的显示,前面是全部列在一个页面,如果数据很多,就不能一下全部显示出来,需要分页显示。自定义分页:

修改classes.html:

    <h1>班级管理页面——班级列表</h1>
    <hr>
    <div>
        <input id="id_add" type="button" value="增加班级">
        <a href="add_class.html">增加班级-单独页面</a>
    </div>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>班级名称</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            % for item in cls_list %
            <tr>
                <td> item.id </td>
                <td> item.caption </td>
                <td>
                    <a>修改</a> | <a class="td-delete">删除</a>
                </td>
            </tr>
            % endfor %
        </tbody>
    </table>
    <div class="pageinfo">
    <!-- 可以将这段代码在后台进行组织,然后直接使用模板语言引用
        <a href="classes.html?p=1">1</a>
        <a href="classes.html?p=2">2</a>
        <a href="classes.html?p=3">3</a>
        <a href="classes.html?p=4">4</a>
     -->

     page_tag | safe
    <!-- 加上管道符和safe,是告诉浏览器进行渲染,否则作为字符串显示,是安全机制 -->
    <!-- 也可以在后台加上安全机制,引入from django.utils.safestring import mark_safe,使用mark_safe(page_tag),-->

修改后台视图函数:

@auth
def classes(req):
    from django.utils.safestring import mark_safe
    print(';;;;;',req.GET.get('flags',None))
    if req.method == "GET":
        current_user = req.session.get('username')
        current_page = req.GET.get('p',1)
        current_page = int(current_page)
        total_count = models.Classes.objects.all().count()
        # 计算按每页10条记录计算,共多少页
        v,a = divmod(total_count,10)
        if a != 0:
            v += 1
        page_list = []
        # 比较简单的实现如下,将所有页码都显示出来,但是如果页比较多,这个实现不好
        # for i in range(1,v+1):
        #     if i == current_page:
        #         page_list.append('<a class="active" href="classes.html?p=%s">%s</a>' % (i,i))
        #     else:
        #         page_list.append('<a href="classes.html?p=%s">%s</a>' % (i,i))
        # page_list.append('<a href="classes.html?p=%s">下一页</a>' % (current_page+1,))
        # page_tag = "".join(page_list)
        # 判断输入的页码是否在合理范围内,因为是get请求,可以在地址栏中随意输入
        # 如果请求的页码小于1,就设为1,如果请求的页码大于总页码,就设为最后一个页码
        if current_page < 1 :
            current_page = 1
            print(current_page)
        elif current_page > v:
            current_page = v
        # 页码只显示最多N条,这里假设11条,加上前后的上一页和下一页
        # 例如显示第10页,则页码显示10的前后5条,即从5到15页显示在页面上
        if v <= 11:
            # 页数小于11页,所有页码都显示出来就可以了
            page_range_start = 1
            page_range_end = v + 1
        else:
            if current_page < 6:
                # 总的页数大于11,并且当前选择的页码小于6,显示的是从1开始的11个页码
                page_range_start = 1
                page_range_end = 12
            else:
                # 总的页数大于11,并且当前选择的页码大于等于6,显示的页码是当前页的前后5个加上当前页共11个页码
                # 此时,显示的开始页码就是当前页-5,显示的最后页码就是当前页+5,range范围需要在加1.
                page_range_start = current_page - 5
                page_range_end = current_page +5 +1
                if page_range_end > v:
                    page_range_end = v + 1
                    page_range_start = v - 10

        if current_page == 1:
            #当前页码为1时,上一页不能在进行计算
            # page_list.append('<a href="#">上一页</a>')
            page_list.append('<a href="javascript:void(0)">上一页</a>')
        else:
            page_list.append('<a href="classes.html?p=%s">上一页</a>' % (current_page - 1,))
        for i in range(page_range_start,page_range_end):
            if i == current_page:
                # 对于当前选择的页码,需要突出显示
                page_list.append('<a class="active" href="classes.html?p=%s">%s</a>' % (i,i))
            else:
                page_list.append('<a href="classes.html?p=%s">%s</a>' % (i,i))
        if current_page == v:
            # 当前页码为最后一页时,下一页不能在进行计算
            page_list.append('<a href="javascript:void(0)">下一页</a>')
        else:
            page_list.append('<a href="classes.html?p=%s">下一页</a>' % (current_page+1,))
        page_tag = "".join(page_list)
        # page_tag = '''
        # <a href="classes.html?p=1">1</a>
        # <a href="classes.html?p=2">2</a>
        # <a href="classes.html?p=3">3</a>
        # <a href="classes.html?p=4">4</a>
        # '''
        start = (current_page - 1) * 10
        end = current_page * 10
        cls_list = models.Classes.objects.all()[start:end]
        # 返回的数据,可以使用切片返回部分数据,但是查询数据库是查询了所有的数据
        ret = render(req, "classes.html", 'username':current_user,'cls_list':cls_list,'page_tag':mark_safe(page_tag))
        # mark_safe(page_tag)使相应的文本在前端被浏览器渲染,否则作为纯文本对待
        return ret
    elif req.method == "POST":
        # 以下代码是form表单提交时的处理方式
        caption = req.POST.get('caption',None)

        # form表单提交时的后台处理,使用了redirect跳转
        # if caption:
        #     models.Classes.objects.create(caption=caption)
        # return redirect('classes.html')

        # Ajax提交时的后台处理
        response_dict = 'status':True,'error':None,'data':None
        if caption:
            obj = models.Classes.objects.create(caption=caption)
            response_dict['data'] = 'id':obj.id,'caption':obj.caption
            print('添加成功')
        else:
            response_dict['status'] = False
            response_dict['error'] = '班名不能为空'
        return HttpResponse(json.dumps(response_dict))

对这个分页功能进行分离,抽取成一个独立的模块,做成一个通用的类:PageHelper

# 将分页做成一个通用的工具类
class PageHelper:
    # 产生一个分页字符串,形式如下:
    # 上一页 1 2 3 4 5 6 7 8 9 10 11 下一页
    def __init__(self,total_rec_count,current_page,base_url,page_rec_count=10):
        # total_rec_count:查询到的记录的总条数
        # current_page:当前页码,即要显示哪一页,点击相应页码时的页码值
        # base_url:查询使用的url
        # page_rec_count:每页显示的记录条数,默认10条
        self.total_rec_count = total_rec_count
        self.current_page = current_page
        self.page_rec_count = page_rec_count
        self.base_url = base_url

    @property
    def page_rec_start(self):
        # 返回当前显示页的记录的开始值
        return (self.current_page - 1)*self.page_rec_count
    @property
    def page_rec_end(self):
        # 返回当前显示页的记录的结束值
        return self.current_page * self.page_rec_count

    def total_page_count(self):
        # 计算总共有多少页
        v, a = divmod(self.total_rec_count, self.page_rec_count)
        if a != 0:
            v += 1
        return v

    def pager_tag_str(self):
        # 返回一个页码字符串
        v = self.total_page_count()
        page_list = []

        # 判断输入的页码是否在合理范围内,因为是get请求,可以在地址栏中随意输入
        # 如果请求的页码小于1,就设为1,如果请求的页码大于总页码,就设为最后一个页码
        if self.current_page < 1:
            self.current_page = 1
        elif self.current_page > v:
            self.current_page = v

        # 页码只显示最多N条,这里假设11条,加上前后的上一页和下一页
        # 例如显示第10页,则页码显示10的前后5条,即从5到15页显示在页面上
        if v <= 11:
            # 页数小于11页,所有页码都显示出来就可以了
            page_range_start = 1
            page_range_end = v + 1
        else:
            if self.current_page < 6:
                # 总的页数大于11,并且当前选择的页码小于6,显示的是从1开始的11个页码
                page_range_start = 1
                page_range_end = 12
            else:
                # 总的页数大于11,并且当前选择的页码大于等于6,显示的页码是当前页的前后5个加上当前页共11个页码
                # 此时,显示的开始页码就是当前页-5,显示的最后页码就是当前页+5,range范围需要在加1.
                page_range_start = self.current_page - 5
                page_range_end = self.current_page + 5 + 1
                if page_range_end > v:
                    page_range_end = v + 1
                    page_range_start = v - 10

        if self.current_page == 1:
            # 当前页码为1时,上一页不能在进行计算
            page_list.append('<a href="javascript:void(0)">上一页</a>')
        else:
            page_list.append('<a href="%s?p=%s">上一页</a>' % (self.base_url,self.current_page - 1,))
        for i in range(page_range_start, page_range_end):
            if i == self.current_page:
                # 对于当前选择的页码,需要突出显示
                page_list.append('<a class="active" href="%s?p=%s">%s</a>' % (self.base_url,i, i))
            else:
                page_list.append('<a href="%s?p=%s">%s</a>' % (self.base_url,i, i))
        if self.current_page == v:
            # 当前页码为最后一页时,下一页不能在进行计算
            page_list.append('<a href="javascript:void(0)">下一页</a>')
        else:
            page_list.append('<a href="%s?p=%s">下一页</a>' % (self.base_url,self.current_page + 1,))

        page_tag = "".join(page_list)
        return page_tag

在项目目录下创建包myutils,将此类放在以page.py保存,然后在使用时引入,然后调用即可:

@auth
def classes(req):
    from django.utils.safestring import mark_safe
    from myutils.page import PageHelper   # 引入分页组件类,调用即可

    if req.method == "GET":
        current_user = req.session.get('username')
        current_page = req.GET.get('p',1)
        current_page = int(current_page)
        total_count = models.Classes.objects.all().count()
        
        obj = PageHelper(total_count,current_page,'classes.html',20)
        page_tag =  obj.pager_tag_str()

        cls_list = models.Classes.objects.all()[obj.page_rec_start:obj.page_rec_end]
        # 返回的数据,可以使用切片返回部分数据,但是查询数据库是查询了所有的数据
        ret = render(req, "classes.html", 'username':current_user,'cls_list':cls_list,'page_tag':mark_safe(page_tag))
        # mark_safe(page_tag)使相应的文本在前端被浏览器渲染,否则作为纯文本对待
        return ret
    elif req.method == "POST":

        caption = req.POST.get('caption',None)

        # Ajax提交时的后台处理
        response_dict = 'status':True,'error':None,'data':None
        if caption:
            obj = models.Classes.objects.create(caption=caption)
            response_dict['data'] = 'id':obj.id,'caption':obj.caption
            print('添加成功')
        else:
            response_dict['status'] = False
            response_dict['error'] = '班名不能为空'
        return HttpResponse(json.dumps(response_dict))

可以看到,经过抽取整理,形成了通用的组件,其他使用的函数中代码更加简洁。

删除班级记录的实现:删除div修改一下:

#  下面是点击删除时弹出的操作页面,未操作前也是隐藏状态,增加hide样式  #
    <div class="remove hide">
        <input name="id" type="text" class="hide">
        <input name="caption" type="text" readonly>
        <input id="id_remove_cancel" type="button" value="取消">
        <input type="button" value="确定">
    </div>

点击删除按钮,对应的id和caption传到删除div中的对应标签中,id是隐藏的,caption只读,显示要删除的班级名称。

对应的删除按钮的绑定事件和删除确定按钮的事件:

function bindTdDeleteEvent() 
            //下面的是事件委托,动态绑定,注意,使用tbody进行委托,不能使用tr或td,因为这两个也是动态添加的。
            $('tbody').on('click','.td-delete',function () 
                $('.remove,.shade').removeClass('hide');
                var nid = $(this).parent().prevAll()[1].innerText;
                var cp = $(this).parent().prevAll()[0].innerText;
                $('.remove input[name="caption"]')[0].value = cp;
                $('.remove input[name="id"]')[0].value = nid;
            )
            //下面绑定删除确定按钮的事件
            $('.remove #id_remove_del').click(function () 
                var nid = $('.remove input[name="id"]')[0].value;
                $.ajax(
                    url: 'del_class.html',
                    type: 'POST',
                    data: 'id':nid,csrfmiddlewaretoken: ' csrf_token ',
                    dataType: 'JSON',
                    success: function (rep_data) 
                        if(!rep_data.status)
                                alert(rep_data.error);
                                $('.remove,.shade').addClass('hide');
                            else
                            location.reload();
                            
                    
                )
            )
        

后台del_class视图:

@auth
def del_class(req):
    response_dict = 'status': True, 'error': None, 'data': None
    nid = req.POST.get('id',None)
    if not nid:
        response_dict['status'] = False
        response_dict['error'] = '传递的ID值为空'
    else:
        res = models.Classes.objects.filter(id=nid).delete()
        if not res:
            response_dict['status'] = False
            response_dict['error'] = '删除失败!'
    return HttpResponse(json.dumps(response_dict))

关于修改操作:

借助于增加班级的div,稍微修改一下:

 <div class="modal hide">
        <form method="post" action="classes.html">
            % csrf_token %
            <input name="id" type="text" class="hide">
            <input name="caption" type="text" placeholder="标题"><br>
            # placeholder属性在输入框中默认显示一个内容,当焦点进入输入框后自动消失 #
            <input id="id_modal_cancel" type="button" value="取消增加">
            <input type="submit" value="submit确定">
            <input id="modal_ajax_submit" type="button" value="Ajax确定">
            <input id="update_ajax_submit" type="button" value="Ajax修改">
        </form>
    </div>

增加一个Ajax修改按钮,然后给这个按钮绑定事件:

function bindUpdateEvent() 
            $('tbody').on('click','.td-edit',function () 
                $('.modal,.shade').removeClass('hide');
                var tds = $(this).parent().prevAll();
                $('.modal input[name="id"]')[0].value = tds[1].innerText;
                $('.modal input[name="caption"]')[0].value = tds[0].innerText;
                $('#update_ajax_submit').click(function () 
                    var cp = $('.modal input[name="caption"]').val();
                    var nid = $('.modal input[name="id"]').val();
                    $.ajax(
                        url: 'update_class.html',
                        type: 'POST',
                        data: caption: cp,id:nid,csrfmiddlewaretoken:' csrf_token ',
                        dataType: 'JSON',
                        success: function (rep_data) 
                            if(!rep_data.status)
                                alert(rep_data.error);
                                $('.remove,.shade').addClass('hide');
                            else
                            location.reload();
                            
                        
                    )
                )
            )

        

后台视图函数:

@auth
def update_class(req):
    if req.method == "POST":
        response_dict = 'status': True, 'error': None, 'data': None
        nid = req.POST.get('id',None)
        cp = req.POST.get('caption',None)
        if not nid:
            response_dict['status'] = False
            response_dict['error'] = "ID为空"
        elif ((not cp) or len(cp)==0):
            response_dict['status'] = False
            response_dict['error'] = "班级名称不能为空"
        else:
            res = models.Classes.objects.filter(id=nid).update(caption=cp)
            print('update:',res)
        if not res:
            response_dict['status'] = False
            response_dict['error'] = "更新失败"
        else:
            return  HttpResponse(json.dumps(response_dict))

程序代码精简:

上面的代码,增加、修改、删除的模态对话框基本相同,考虑将模态对话框统一利用。三个功能使用同一个div层。

 <div class="commpage  hide">
        <form method="post" action="classes.html">
            <span class="leixing"></span>
            <hr>
            % csrf_token %
            <input name="id" type="text" class="hide">
            <input name="caption" type="text" placeholder="标题"><br>
            <input id="id_cancel" type="button" value="取消">
            <input id="ajax_submit" type="button" value="确定">
        </form>
    </div>

关键点是对确定按钮的事件绑定,点击不同的按钮,即点击增加,删除、修改时,对确定按钮绑定不同的事件。

搞了很长时间也没成功,看其中一个删除:

function bindTdDeleteEvent() 
            //下面的是事件委托,动态绑定,注意,使用tbody进行委托,不能使用tr或td,因为这两个也是动态添加的。
            $('tbody').on('click','.td-delete',function () 
                $('.commpage,.shade').removeClass('hide');
                var nid = $(this).parent().prevAll()[1].innerText;
                var cp = $(this).parent().prevAll()[0].innerText;
                $('.commpage span')[0].innerText = '删除班级';
                $('.commpage input[name="caption"]')[0].value = cp;
                // $('.remove input[name="caption"]')[0].readOnly=false/true;  //设置输入框是否只读
                $('.commpage input[name="id"]')[0].value = nid;
                alert('delete1');
            )
            submit_url = 'del_class.html'
            $('.commpage #ajax_submit').off().on('click',function () 
                alert('delete2');
                var nid = $('.commpage input[name="id"]')[0].value;
                $.ajax(
                    url: submit_url,
                    type: 'POST',
                    data: 'id':nid,csrfmiddlewaretoken: ' csrf_token ',
                    dataType: 'JSON',
                    success: function (rep_data) 
                        if(!rep_data.status)
                                alert('删除模块:',rep_data.error);
                                $('.commpage,.shade').addClass('hide');
                                $('#ajax_submit').off();
                        else
                            location.reload();
                            $('.commpage,.shade').addClass('hide');
                            alert("删除模块:chenggong");
                            $('#ajax_submit').off();
                        


                    
                )

            )
        

难搞的地方是如何在绑定事件的时候,删除已经绑定的其他事件,按照在网上查找的方法,主要有unbind()、off()等方法,但是在测试时,前面的事件总是没有删除掉,相当于绑定了一个事件链,会执行多个事件。代码如下:
$('#ajax_submit').off(‘click’).on('click',function ()

按照预想,$('#ajax_submit')这个按钮应该是先删除其上的click事件,然后再绑定新的事件。结果一直出错。先放弃吧。有时间再看看。

以上是关于Python入门自学进阶-Web框架——11Django实践小项目的主要内容,如果未能解决你的问题,请参考以下文章

Python入门自学进阶-Web框架——3Django的URL配置

Python入门自学进阶-Web框架——18FormModelForm

Python入门自学进阶-Web框架——18FormModelForm

Python入门自学进阶-Web框架——20Django其他相关知识2

Python入门自学进阶-Web框架——2Django初识

Python入门自学进阶-Web框架——21DjangoAdmin项目应用