19 Jun 18
一、作业讲解(代码统计)
# views.py
from django.shortcuts import HttpResponse,render,redirect
import shutil
import os
import uuid
# 导入settings的方式
from django.conf import settings
ACCEPT_FILE_TYPE = ['zip','tar','gztar','bztar','xztar','rar'] # 可以给一个全局变量,放在配置文件中,用之前导入
def upload(request):
if request.method == 'POST':
file_obj = request.FILES.get('file')
filename, suffix = file_obj.name.rsplit('.',maxsplit=1)
if suffix not in ACCEPT_FILE_TYPE: # 增加强健性
return HttpResponse('wrong file type')
with open(file_obj.name,'wb') as f:
for chunk in file_obj.chunks(): # 从上传文件对象一点一点读取数据
f.write(chunk)
real_file_path = os.path.join(settings.BASE_DIR,file_obj.name) # 拼接得到上传文件的全路径
upload_path = os.path.join(settings.BASE_DIR,'files',str(uuid.uuid4())) # 对上传的文件做处理
shutil.unpack_archive(real_file_path,extract_dir=upload_path) # 解压代码文件至指定文件夹
total_num = 0
for (dir_path,dir_names,filenames) in os.walk(upload_path):
# os.walk() 用来遍历所有的文件,可以拿到以下三个参数
# dir_path: 根目录dir_names: 文件夹filenames: 文件
for filename in filenames:
file_path = os.path.join(dir_path,filename) # 将文件名和根目录拼接成完整的路径
file_path_list = file_path.rsplit('.',maxsplit=1)
if len(file_path_list) != 2:
continue
if file_path_list[1] != 'py':
continue
line_num = 0
with open(file_path,'r') as f:
for line in f:
if line.strip().startswith('#'):
continue
line_num += 1
total_num += line_num
return
render(request,'done.html',{'file_name':file_obj.name,'file_size':file_obj.size,'total_num':total_num})
return render(request,'upload.html')
二、内容补充
目前,this和箭头函数一起使用可能出现问题。所以,推荐尽量少用箭头函数;但要能看懂。
三、今日内容
- 模版语言剩下的
http://www.cnblogs.com/liwenzhou/p/7931828.html
- csrf_token: 用于跨站请求伪造保护。是Django提供的一个解决方案:在提交表单时带着之前Django给该页面生成的token,如果没有token,或token匹配失败,将拒绝继续访问;这样,钓鱼网站就不可以通过单单设置action,而跳转至正经网站某个页面,进而修改正经网站的数据库。
在页面的form表单里面写上{% csrf_token %},在settings中即可不注释csrf相关
#钓鱼网站html
<h1>这是一个钓鱼网站</h1>
<formaction="http://127.0.0.1:8000/zhuanzhang/"method="post">
# action直接跳转到别的网站
<p>转给谁:<input type="text"></p>
<p><input style="display: none" type="text" name="id" value="3"></p>
<p>转多少:<input type="text" name="num"></p>
<p>密码:<input type="password" name="pwd"></p>
<p><input type="submit" value="提交"></p>
</form>
#正经网站html
<h1>这是一个正经网站</h1>
<formaction="/zhuanzhang/"method="post">
{% csrf_token %} # 阻止跨站请求伪造
<p>转给谁:<input type="text" name="id"></p>
<p>转多少:<input type="text" name="num"></p>
<p>密码:<input type="password" name="pwd"></p>
<p><input type="submit" value="提交"></p>
</form>
ps:如果不设置数据库,使用默认的数据库db.sqlite3
- 静态文件相关(取代硬编码(/static/…))
如果有改static这个名字的需求,用硬编码要通篇改;如果使用以下方式,则不存在这个问题;一般来说,上述需求比较不常见
{% load static %}
<img src="{% static "images/hi.jpg" %}" alt="Hi!" />
引用JS文件时使用:
{% load static %}
<script src="{% static "mytest.js" %}"></script>
某个文件多处被用到可以存为一个变量
{% load static %}
{% static "images/hi.jpg" as myphoto %}
<img src="{{ myphoto }}"></img>
- get_static_prefix
# 极少使用
{% load static %}
<img src="{% get_static_prefix %}images/hi.jpg" alt="Hi!" />
{% load static %}
{% get_static_prefix as STATIC_PREFIX %}
<img src="{{ STATIC_PREFIX }}images/hi.jpg"alt="Hi!" />
<img src="{{ STATIC_PREFIX }}images/hi2.jpg" alt="Hello!" />
- 自定义simpletag和自定义filter类似,只不过接收更灵活的参数。
自定义的filter,simpletag和inclusion_tag都要放在templatetags 这个新建的package中的py文件下
filter: {{ }} 在变量的基础上做额外的调整
simpletag {% %} 可传多个参数,返回它们之间所做运算的结果
定义注册simple tag
@register.simple_tag(name="plus")
def plus(a, b, c):
return "{} + {} + {}".format(a, b, c)
使用自定义simple tag
{% load app01_demo %}
{% plus "1" "2" "abc" %}
- inclusion_tag:多用于返回html代码片段;后续项目会使用
# templatetags/my_inclusion.py
from django import template
register = template.Library()
@register.inclusion_tag('result.html')
def show_results(n):
n = 1 if n < 1 else int(n)
data = ["第{}项".format(i) for i in range(1, n+1)]
return {"data": data}
templates/snippets/result.html
<ul>
{% for choice in data %}
<li>{{ choice }}</li>
{% endfor %}
</ul>
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>inclusion_tag test</title>
</head>
<body>
{% load inclusion_tag_test %}
{% show_results 10 %}
</body>
</html>
- urls.py(路由系统)
http://www.cnblogs.com/liwenzhou/p/8271147.html
a. URLconf配置
# 基本格式:
from django.conf.urls import url
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
# 在Django2.0 中用path替换了url(from django.urls import path)
b. 正则表达式详解
1)urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
2)若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
3)不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是^/articles。
# Django settings.py配置文件中默认没有APPEND_SLASH 这个参数,但 Django 默认这个参数为APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。可设置APPEND_SLASH = False,改变默认值。
4)每个正则表达式前面的'r' 是可选的但是建议加上。
c. 正则匹配的模式
1) 分组匹配 --> 调用视图函数的时候额外传递位置参数;正则表达式分组匹配(通过圆括号)来捕获URL中的值并以位置参数形式自动传递给视图的args。
# *args, agrs = (a,b)
url(r'^articles/([0-9]{4})/$', views.year_archive),
2) 分组命名匹配 --> 调用视图函数的时候额外传递关键字参数
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
注意:
a) 要么全都用分组匹配,要么全都用分组命名匹配,不要混着用!
b) Django utl正则表达式匹配的位置:从第一个斜线到问号之前这一段的路径
c) URL正则表达式捕获的值都是字符串类型,如果要参与后续运算,应先进行类型转换。
d) 可以给视图函数的参数设置默认值
# 正则匹配时,不论是否分组带参数,即都可以调用views中的page方法
# urls.py中
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^blog/$', views.page),
url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]
# views.py中,可以为num指定默认值
def page(request, num="1"):
pass
d. include 分级路由(级级匹配:/app01/upload; 在第一级匹配app01,在第二级匹配upload)
# 推荐在各个app下建立自己的urls.py
查找的顺序:请求--> project/urls.py --> app/urls.py --> app/views.py
from django.conf.urls import include, url
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/', include('blog.urls')), # 可以包含其他的URLconfs文件
]
e. 传递额外的参数给视图函数
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]
f. URL匹配规则的别名(命名URL和URL反向解析(别名->正则表达式(URL)))
1) 起别名是为了,增加代码的健壮性,不将URL硬编码到代码中!
2) 用法
a) 在views.py中如何根据别名找到url
from django.urls import reverse
url = reverse('别名')
b) 在模板语言中如何根据别名找到url
{% url '别名' %}
3) 带参数的url如何反向生成?
a) 位置参数
i) 在views.py中:
reverse("别名", args=(参数1, 参数2, ...))
ii) 在模板语言中:
{% url "别名" 参数1, 参数2... %}
b). 关键字参数
i) 在views.py中:
reverse("别名", kwargs={"k1":参数1, ...})
ii) 在模板语言中:
{% url "别名" 参数1, 参数2... %}
# 在模版语言中,无法传递关键字参数,只能根据位置,依次传参
4) namespace(命名空间), 是include的一个参数
# 使用include语法,将其他的urls.py 包含进来
from django.conf.urls import url, include
urlpatterns = [
url(r'^app01/', include('app01.urls', namespace='app01')),
url(r'^app02/', include('app02.urls', namespace='app02')),
]
语法:'命名空间名称:URL名称'
# 这样两个app中url名称重复了,反转URL的时候就可以通过命名空间的名称得到当前的URL;即使app中URL的命名相同,也可以反转得到正确的URL了。
模板中使用:
{% url 'app01:detail' pk=12 pp=99 %}
views中的函数中使用
v = reverse('app01:detail', kwargs={'pk':11})
四、今日作业
用一个函数一个URL匹配模式实现 书书籍表、作者表、出版社表的展示和删除操作
关键点:
- URL匹配模式的设计(分组匹配或分组命名匹配;给URL匹配模式起别名)
2. 反射:由一个字符串,找到对应的类变量
3. URL的反向解析(别名--> 完整的URL)
4. 正则表达式
import re
# 写一个正则表达式, 只能从add delete edit list 这四个单次里面选一个
s1 = "delete" # 匹配成功
s2 = "cut" # 匹配不成功
r = re.compile(r'add|delete|edit|list')
print(r.match(s1))
print(r.match(s2))
# urls.py
url(r'(?P<operation>list|delete)_(?P<table_name>[a-zA-Z]+)/', views.op, name = 'list_delete'),
# views.py
def op(request,operation,table_name):
table = table_name.capitalize()
if hasattr(models,table):
class_obj = getattr(models,table)
if operation.upper() == 'LIST':
ret = class_obj.objects.all()
return render(request,'{}_{}.html'.format(table_name,operation),{'ret':ret})
elif operation.upper() == 'DELETE':
id = request.GET.get('id')
class_obj.objects.get(id=id).delete()
url = reverse('list_delete', kwargs={'operation':'list','table_name':table_name})
return redirect(url)