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