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

Posted kaoa000

tags:

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

实现老师管理中心班级选择的左右框列表方式实现:

Django中的数据库查询,有filter(),还有一个exclude()

models.Tb.objects.filter(name='root')      查找name为root的记录
models.Tb.objects.exclude(name='root')  查找name不是root的记录
models.Tb.objects.filter(id__in=[1,2,3])      查找id为1或2或3,即id在列表中的记录
models.Tb.objects.exclude(id__in=[1,2,3]) 查找id不在列表中的记录

jQuery对象与DOM对象的互换:

obj = document.getElementById('sel')     DOM对象
$(obj)     jQuery对象
$('#sel')  jQuery对象          $('#sel')[0] DOM对象

 对于未选择班级,一种方法:前端

% block content %
    <h1>修改老师信息页面</h1>
    <hr>
    <form action="edit_teacher- obj.id .html" method="post">
        <p>
            老师姓名:<input name="name" type="text" value=" obj.name ">
        </p>
        <p>已选班级:
            <select name="cls" multiple>
                % for row in obj_cls_list %
                    <option value=" row.id "> row.caption </option>
                % endfor %
            </select>
            未选班级:
            <select name="cls_w" multiple>
                % for row in cls_list %
                    % if row.id not in id_cls_list %
                        <option value=" row.id "> row.caption </option>
                    % endif %
                % endfor %
            </select>
        </p>
        % csrf_token %
        <input type="submit" value="确定修改">
    </form>
% endblock %

后端:

    if req.method == "GET":
        obj = models.Teacher.objects.get(id=nid)
        obj_cls_list = obj.cls.all()
        id_cls_list = list(zip(*obj_cls_list.values_list('id')))[0]
        cls_list = models.Classes.objects.all()
        return render(req,'edit_teacher.html','obj':obj,'cls_list':cls_list,'obj_cls_list':obj_cls_list,'id_cls_list':id_cls_list)

拿到已选择的班级,及已选择班级id的列表,再拿到所有班级的列表,在前端判断如果id在已选择班级id的列表中,就不添加option元素。

第二种方法,在后端直接查询到未选择的班级列表:

@auth
def edit_teacher(req,nid):
    if req.method == "GET":
        obj = models.Teacher.objects.get(id=nid)
        obj_cls_list = obj.cls.all()
        print(obj_cls_list)
        if len(obj_cls_list) == 0:
            id_cls_list = list()
        else:
            id_cls_list = list(zip(*obj_cls_list.values_list('id')))[0]
        cls_list = models.Classes.objects.exclude(id__in=id_cls_list) # 只获取未选择的班级
        return render(req,'edit_teacher.html','obj':obj,'cls_list':cls_list,'obj_cls_list':obj_cls_list,'id_cls_list':id_cls_list)
    else:
        name = req.POST.get('name')
        cls_li = req.POST.getlist('cls')
        obj = models.Teacher.objects.get(id=nid)
        obj.name = name
        obj.save()
        obj.cls.set(cls_li)
        return redirect('teacher.html')

前端进行完善:

% extends "index_base.html" %
% block css %
    <style>
        .tag1
            display: inline-block;
            background-color: rosybrown;
            border: 1px solid red;
            padding: 2px;
            cursor: pointer;
        
    </style>

% endblock %

% block content %
    <h1>修改老师信息页面</h1>
    <hr>
    <form action="edit_teacher- obj.id .html" method="post">
        <p>
            老师姓名:<input name="name" type="text" value=" obj.name ">
        </p>
        <p>已选班级:
            <select id="sel" name="cls" multiple>
                % for row in obj_cls_list %
                    <option value=" row.id "> row.caption </option>
                % endfor %
            </select>
            未选班级:
            <select id="nosel" name="cls_w" multiple>
                % for row in cls_list %
#                    % if row.id not in id_cls_list %#
                        <option value=" row.id "> row.caption </option>
#                    % endif % #
                % endfor %
            </select>
        </p>
        % csrf_token %
        <a id="removecls"> >>>>>>> </a><span>  ||  </span></span><a id="addcls"> <<<<<<<< </a><br>
        <input type="submit" value="确定修改" onclick="selectall();">
    </form>
% endblock %

% block js %
    <script>
        $(function () 
            $('#menu_teacher').addClass('active');
            bindRemoveCls();
            bindAddCls();
        )
        function bindRemoveCls() 
            $('#removecls').click(function () 
                var options = $('#sel')[0].selectedOptions;
                while (options.length > 0) 
                    $(options[0]).prop('selected',false).appendTo('#nosel');
                    // 添加到对端时,以未选中状态显示
                
            )
        
        function bindAddCls() 
            $('#addcls').click(function () 
                var options = $('#nosel')[0].selectedOptions;
                while (options.length > 0) 
                    $(options[0]).prop('selected',false).appendTo('#sel');
                
            )
        
        function selectall()    // 提交前,要将最终的已选班级中的选项全部设置为选中状态
            $("#sel").children().each(function () 
                $(this).prop('selected',true)
            )

        

    </script>
% endblock %

文件上传的实现:

form表单的文件上传:前端upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="upload.html" enctype="multipart/form-data">
        <input type="text" name="user">
        <input type="file" name="fafafa">
        % csrf_token %
        <input type="submit" value="提交">
    </form>
<hr>
<div>
    % for item in img_list %
        <img style="height: 150px;width: 150px" src="/ item.img_url ">
    % endfor %
</div>
</body>
</html>

后台视图函数:

def uplaod(req):
    if req.method == "GET":
        img_list = models.Imgs.objects.all()
        return render(req,'upload.html','img_list':img_list)
    elif req.method == "POST":
        user = req.POST.get('user')
        file_1 = req.FILES.get('fafafa')
        file_path = os.path.join('statics','upload',file_1.name)
        file_url = os.path.join('static','upload',file_1.name)
        f = open(file_path,'wb')
        for chunk in file_1.chunks():
            f.write(chunk)
        f.close()
        models.Imgs.objects.create(user=user,img_url=file_url)
        return redirect('upload.html')

使用了数据库,将上传的图片的url保存在数据库中,然后显示在页面上。关键点是上传页面中form表单要设置enctype="multipart/form-data",接收端要使用chunks()分段接收。

几个需要深入思考的问题:
后台路径问题,开始设置了一个upload文件夹,与static同级,但是,前端使用路径http://ip/upload/xxx.jpg无法访问,提示url有问题,而static下的upload就可以访问,如何创建类似static的文件夹?

上传文件的验证文体,怎么确保上传的文件完整,即在后台接收完文件后,如何判定接收文件与上传的文件一模一样?通过文件大小吗?

Ajax上传文件:原生XMLHttpRequest和jQuery

对于字符串的发送:

XMLHttpRequest:

xml = new XMLHttpRequest();
xml.open('post','/upload.html',true)
xml.send("k1=v1;k2=v2;")

jQuery:

$.ajax(
        url:'/upload.html'
        type:'POST'
        data:'k1':'v1','k2':'v2'
)

FormData对象:类似字典

dic = new FormData();
dic.append('k1','v1');
dic.append('k2','v2');

xml.send(dic)

$.ajax(
        data:dic,
)

FormData对象不但能承载字符串,还能承载文件。Ajax就是使用这个对象实现文件上传。

前端:原生Ajax和jQuery提交:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<input id="user1" type="text" name="user">
<input id="img1" type="file" name="fafafa">
<input type="button" value="提交Ajax" onclick="uploadajax();">
<input type="button" value="提交jQuery" onclick="uploadjquery();">
<hr>
<div id="imgs1">
    % for item in img_list %
        <img style="height: 150px;width: 150px" src="/ item.img_url ">
    % endfor %
</div>
<script src="/static/jquery-3.6.0.js"></script>
<script>
    function uploadajax() 
        var dic = new FormData();
        var u = document.getElementById('user1').value;
        dic.append('user',u);
        dic.append('fafafa',document.getElementById('img1').files[0])
        dic.append('csrfmiddlewaretoken', ' csrf_token ');
        var xml = new XMLHttpRequest();
        xml.open('post','upload1.html');
        xml.onreadystatechange =function () 
            if(xml.readyState == 4)
                var obj = JSON.parse(xml.responseText);
                if(obj.status)
                    var img_tag = document.createElement('img');
                    img_tag.src = "/" + obj.img_url;
                    img_tag.style = "height:150px;width:150px;"
                    document.getElementById('imgs1').appendChild(img_tag);
                
            
        
        xml.send(dic);
    
    function uploadjquery() 
        var dic = new FormData();
        var u = document.getElementById('user1').value;
        dic.append('user',u);
        dic.append('fafafa',document.getElementById('img1').files[0])
        dic.append('csrfmiddlewaretoken', ' csrf_token ');

        $.ajax(
            url: 'upload1.html',
            type: 'POST',
            data: dic,
            processData:false,    // 告诉jQuery不要处理数据,否则会将dic做字符串处理,这里实际已不是单纯字符串
            contentType: false,   // 告诉jQuery不要设置contentType
            dataType: 'JSON',
            success: function (arg) 
                if(arg.status)
                    var img_tag = document.createElement('img');
                    img_tag.src = "/" + arg.img_url;
                    img_tag.style = "height:150px;width:150px;"
                    $('#imgs1').append(img_tag);
                
            
        )
    
</script>
</body>
</html>

后台视图函数:

def uplaod1(req):
    if req.method == "GET":
        img_list = models.Imgs.objects.all()
        return render(req,'upload1.html','img_list':img_list)
    elif req.method == "POST":
        user = req.POST.get('user')
        file_1 = req.FILES.get('fafafa')
        file_path = os.path.join('statics','upload',file_1.name)
        file_url = os.path.join('static','upload',file_1.name)
        f = open(file_path,'wb')
        for chunk in file_1.chunks():
            f.write(chunk)
        f.close()
        models.Imgs.objects.create(user=user,img_url=file_url)
        ret = 'status':True,'img_url':file_url
        return HttpResponse(json.dumps(ret))

基于form表单和iframe实现ajax请求:

JSONP的本质是动态在head中生成<script>标签,利用script的src跨域请求数据。script的src不受同源策略限制,可以跨域请求。

一,什么是iframe
1.iframe是html元素,用于在网页中内嵌另一个网页。
2.iframe默认有一个宽高,存在边界
3.iframe是一个行内快级元素,可以通过display修改
二,iframe元素属性介绍
1.src : 指定内联网页的地址
2.frameborder: iframe默认有个边界,可以设置frameborder为0清除边界。
3.width,height: 控制iframe的宽高。
4.name: 框架的名称
5.scrolling: 是否可滚动,yes ,no , auto

iframe基本测试:

<body>
    <input id="user1" type="text" name="user">
    <input type="button" value="点击" onclick="iframeChange();">
    <iframe id="ifr" src=""></iframe>
    <hr>

<script src="/static/jquery-3.6.0.js"></script>
<script>
    function iframeChange() 
        var url_t = $('#user1').val();
        $('#ifr').attr('src',url_t);
    
</script>
</body>

 在输入框中输入网址,点击按钮,在iframe框中显示网站内容,但是本网页没有刷新,类似ajax请求。(对于https请求,会提示不是sameorgin,即不同源,不能访问)。

使用iframe实现文件的上传:

<form action="upload1.html" method="post" target="iframe1" enctype="multipart/form-data">
        <iframe name = "iframe1" src=""></iframe>
        <input type="text" name="user">
        <input type="file" name="fafafa">
        % csrf_token %
        <input type="submit">
    </form>
    <hr>

form提交动作后的目标设为iframe,这样就可以在不刷新页面的情况下上传文件:

 

提交后,结果出现在iframe框中。

一开始出现了错误提示:

问题是Django 使用了避免中间人攻击的中间件,解决的方法,一是禁用这个中间件:

这个方法不好,会破坏整体的安全性;
第二种方法,是在settings.py中设置对应的选项:
X_FRAME_OPTIONS = 'SAMEORIGIN'

# deny : 不允许在frame中展示
# sameorigin : 允许在通域名的frame中展示
# ALLOWALL url: 在指定地址的frame中可以展示 所以后边应该还有个url

 第三种方法是在视图函数中使用装饰器:

# @xframe_options_deny				#设置为deny
# @xframe_options_sameorigin			#设置为sameorigin
# @xframe_options_exempt				#去掉iframe限制,允许iframe访问某一个voews
from django.views.decorators.clickjacking import xframe_options_exempt
@xframe_options_exempt
def uplaod1(req):

这里应为是同源访问,所以设置为sameorgin和exempt都可以。

然后实现在当前页中动态添加新上传的图片:

<body>
    <input id="user1" type="text" name="user">
    <input type="button" value="点击" onclick="iframeChange();">
    <iframe id="ifr" src=""></iframe>
    <hr>
    <hr>
    <form action="upload1.html" method="post" target="iframe1" enctype="multipart/form-data">
        <iframe id="ifr1" name = "iframe1" src="" onload="loadiframe();"></iframe>
        <input type="text" name="user">
        <input type="file" name="fafafa">
        % csrf_token %
        <input type="submit">
    </form>
    <hr>
<script src="/static/jquery-3.6.0.js"></script>
<script>
    function loadiframe() 
        //获取iframe中内部的内容
        //关键点是$('#ifr1').contents(),这里要使用contents来获取iframe,因为iframe中包含的是一个嵌套的一整个html
        //所以需要使用上下文,即contents来获取,再使用find来找到对应的标签及内容
        var str_json = $('#ifr1').contents().find('body').text()
        var obj = JSON.parse(str_json)
        var img_tag = document.createElement('img');
        img_tag.src = "/" + obj.img_url;
        img_tag.style = "height:150px;width:150px;"
        $('#imgs1').append(img_tag);
    
</script>

给iframe增加一个onload事件,即加载完成后执行的事项,将添加的图片在本页面末尾动态添加上。

在控制台查看时,在iframe中的body中还多了一个div标签,这是因为浏览器中有迅雷下载器组件。

禁用迅雷组件后,如下,是我们返回的内容。

 

 将iframe标签隐藏,
<iframe style="display: none" id="ifr1" name = "iframe1" src="" οnlοad="loadiframe();" dis></iframe>

至此,使用iframe模拟ajax提交完成。 

JSONP的实现原理:

动态在head中增加一个script标签,标签体中的内容是跨域返回的内容,这个内容应该是约定好的特定格式的内容,一般都是函数调用,如xxx(args),然后,我们自定义xxx(args)函数,这样就获取了args参数,即跨域传递过来的参数。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="JSONP跨域获取数据" onclick="jsonpRequest();">

    <script>
        function jsonpRequest() 
            var tag = document.createElement('script');
            tag.src = "http://test.com";
            document.head.appendChild(tag);
        
    </script>
</body>
</html>

点击按钮后:

 

head中增加了script标签,并且网络中发出了test.com请求。

现在假设http://test.com返回的是mydisplay([11,22,33,44])这种形式,那么新添加的script标签就是如下形式:

<script> mydisplay([11,22,33,44]) </script>

相当于调用mydisplay()这个函数,然后我们再自己定义mydisplay(arg)这个函数,就可以获得arg这个参数值,也就获得了跨域的数据。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>



</head>
<body>
    <input type="button" value="JSONP跨域获取数据" onclick="jsonpRequest();">

    <script>
        tag = null;
        function jsonpRequest() 
            tag = document.createElement('script');
            tag.src = "http://test.com";  //约定好返回的是mydisplay([11,22,33,44])
            document.head.appendChild(tag);
        

        function mydisplay(arg) 
            var my_jsonp_data = arg;
            alert(my_jsonp_data);
            //获取跨域数据,即这里的arg,然后进行下一步的处理
            document.head.removeChild(tag)
            //用完后删除,避免多次请求多次累加
        
    </script>
</body>
</html>

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

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

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

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

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

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

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