补充知识-路由系统(URL)

URL传递额外的参数

在url.py里,除了默认会传一个request给处理函数,还可以传递额外的参数,把一个字典作为第三个参数传入,之后就可以在处理函数里取到对应的值:

from django.urls import path
from app01 import views urlpatterns = [
path('index/', views.index, {'foo': 'bar'}),
]

处理函数views.py:

from django.shortcuts import HttpResponse

def index(request, foo):
return HttpResponse(foo)

命名空间

做路由分发的时候,可以把两条路由指向同一个app,像这样:

在day21的urls.py中

from django.contrib import admin
from django.urls import path,include urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include("app01.urls",namespace="app01")),
path('app/', include("app01.urls",namespace="app")),
]
 app01\urls.py
from django.urls import path
from app01 import views app_name = "app01" urlpatterns = [
path('index1/', views.index1,name='index1'),
path('home1/', views.home1,name='home1'),
]

这里的 include() 里多了一个参数 namespace

现在我们访问网页发现访问 http://127.0.0.1:8000/app01/index1/ 和 http://127.0.0.1:8000/app/index1/ 页面效果,

这里就需要我们做路由分发,以区分。这里是从views反向查找的时候会用到

这里之前出来了一个大坑:

在app01里面的urls.py中少了一行代码,造成一直报错

File "D:\Python34\lib\site-packages\django\urls\conf.py", line 39, in include
'Specifying a namespace in include() without providing an app_name '
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include()
without providing an app_name is not supported. Set the app_name attribute in t
he included module, or pass a 2-tuple containing the list of patterns and app_na
me instead.

解决办法

在app01/urls.py中增加

app_name = "app01"

下面是在处理函数 views.py 以及html页面文件处理路由的问题

def home1(request):
v = reverse('app01:home1') # 这个v就是请求的路由,app01为命名空间 /app01/home1/
return HttpResponse(v)

HTML中处理为

<div>{% url 'app01:index1' %}</div>    //app01:index1   命名空间:url名字

查看请求的其他信息

用户发来请求的时候,不仅有数据,还有请求头

所有的信息都封装在了request这个对象里,现在就把他们找出来。先用下面的处理函数打印出request这个对象:

def index1(request):
print(type(request))
return HttpResponse("OK")

打印结果如下:

<class 'django.core.handlers.wsgi.WSGIRequest'>

我们导入上面的模块

from django.core.handlers.wsgi import WSGIRequest

按住ctrl,并点击WSGIRequest,可以看到一个参数,现在我们打印这个参数看看

print(request.environ)

结果为:

{'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\ZYP\\AppData\\Roaming', 'ASL.LOG': 'Destination=file', 'COMMONPROGRAMFILES': 'C:\\Program Files (x86)\\Common Files', 

'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'ZYP-PC', 'COMSPEC': 'C:\\windows\\system32\\cmd.exe', 

'DJANGO_SETTINGS_MODULE': 'day21.settings', 'ERLANG_HOME': 'd:\\Program Files\\erl9.3', 'FP_NO_HOST_CHECK': 'NO', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\ZYP', 

'IBM_JAVA_OPTIONS': '-Xrunjvmhook -Xbootclasspath/a:D:\\HP\\QUICKT~1\\bin\\JAVA_S~1\\classes;D:\\HP\\QUICKT~1\\bin\\JAVA_S~1\\classes\\jasmine.jar', 'JAVA_TOOL_OPTIONS': '-agentlib:jvmhook',
'LOCALAPPDATA': 'C:\\Users\\ZYP\\AppData\\Local', 'LOGONSERVER': '\\\\ZYP-PC', 'LSERVRC': 'C:\\ProgramData\\HP\\Functional testing\\License\\lservrc',
'MOZ_PLUGIN_PATH': 'D:\\software\\foxit reader\\Foxit Reader Plus\\plugins\\', 'MSJAVA_ENABLE_MONITORS': '', 'NUMBER_OF_PROCESSORS': '', 'OS': 'Windows_NT',
'PATH': 'C:\\windows\\system32;C:\\windows;C:\\windows\\System32\\Wbem;C:\\windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Intel\\OpenCL SDK\\2.0\\bin\\x86;C:\\Program Files (x86)\\Intel\\OpenCL SDK\\2.0\\bin\\x64;D:\\HP\\QuickTest Professional\\bin;C:\\Program Files (x86)\\Calibre2\\;C:\\driver;C:\\Program Files (x86)\\SecureCRT;D:\\Python\\Python36-32\\Scripts;"C:\\Program Files\\JetBrains\\PyCharm 2018.1.4\\bin\\pycharm64.exe";C:\\Program Files\\MySQL\\MySQL Server 8.0\\bin;C:\\Program Files (x86)\\Git\\cmd;C:\\Program Files (x86)\\IDM Computer Solutions\\UltraEdit;C:\\Program Files\\IDM Computer Solutions\\UltraCompare;D:\\Python\\Python36-32\\Scripts;D:\\Python\\Python36-32\\Scripts\\;D:\\Python\\Python36-32\\;C:\\Users\\ZYP\\AppData\\Local\\Programs\\Fiddler', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'PROCESSOR_ARCHITECTURE': 'x86', 'PROCESSOR_ARCHITEW6432': 'AMD64', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 42 Stepping 7, GenuineIntel', 'PROCESSOR_LEVEL': '', 'PROCESSOR_REVISION': '2a07', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files (x86)', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PSMODULEPATH': 'C:\\windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM_HOSTED': '', 'PYCHARM_MATPLOTLIB_PORT': '', 'PYTHONIOENCODING': 'UTF-8',
'PYTHONPATH': 'C:\\Program Files\\JetBrains\\PyCharm 2018.2.4\\helpers\\pycharm_matplotlib_backend;C:\\Users\\ZYP\\PycharmProjects\\day21', 'PYTHONUNBUFFERED': '', 'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\windows', 'TEMP': 'C:\\Users\\ZYP\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\ZYP\\AppData\\Local\\Temp', 'USERDOMAIN': 'ZYP-PC', 'USERNAME': 'ZYP', 'USERPROFILE': 'C:\\Users\\ZYP', 'WINDIR': 'C:\\windows', 'WINDOWS_TRACING_FLAGS': '', 'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log', '_CLASSLOAD_HOOK': 'jvmhook',
'_JAVA_OPTIONS': '-Xrunjvmhook -Xbootclasspath/a:D:\\HP\\QUICKT~1\\bin\\JAVA_S~1\\classes;D:\\HP\\QUICKT~1\\bin\\JAVA_S~1\\classes\\jasmine.jar', 'RUN_MAIN': 'true', 'SERVER_NAME': 'genuine.microsoft.com', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET',
'PATH_INFO': '/app01/index1/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0',
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_DNT': '', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '', 'HTTP_PRAGMA': 'no-cache', 'HTTP_CACHE_CONTROL': 'no-cache', 'wsgi.input': <_io.BufferedReader name=168>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}

字典的输出形式为

    for i ,j in request.environ.items():
print(i,j)

结果为:

ALLUSERSPROFILE C:\ProgramData
APPDATA C:\Users\ZYP\AppData\Roaming
ASL.LOG Destination=file
COMMONPROGRAMFILES C:\Program Files (x86)\Common Files
COMMONPROGRAMFILES(X86) C:\Program Files (x86)\Common Files
COMMONPROGRAMW6432 C:\Program Files\Common Files
COMPUTERNAME ZYP-PC
COMSPEC C:\windows\system32\cmd.exe
DJANGO_SETTINGS_MODULE day21.settings
ERLANG_HOME d:\Program Files\erl9.3
FP_NO_HOST_CHECK NO
HOMEDRIVE C:
HOMEPATH \Users\ZYP
IBM_JAVA_OPTIONS -Xrunjvmhook -Xbootclasspath/a:D:\HP\QUICKT~1\bin\JAVA_S~1\classes;D:\HP\QUICKT~1\bin\JAVA_S~1\classes\jasmine.jar
JAVA_TOOL_OPTIONS -agentlib:jvmhook
LOCALAPPDATA C:\Users\ZYP\AppData\Local
LOGONSERVER \\ZYP-PC
LSERVRC C:\ProgramData\HP\Functional testing\License\lservrc
MOZ_PLUGIN_PATH D:\software\foxit reader\Foxit Reader Plus\plugins\
MSJAVA_ENABLE_MONITORS 1
NUMBER_OF_PROCESSORS 4
OS Windows_NT
PATH C:\windows\system32;C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;D:\HP\QuickTest Professional\bin;C:\Program Files (x86)\Calibre2\;C:\driver;C:\Program Files (x86)\SecureCRT;D:\Python\Python36-32\Scripts;"C:\Program Files\JetBrains\PyCharm 2018.1.4\bin\pycharm64.exe";C:\Program Files\MySQL\MySQL Server 8.0\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files (x86)\IDM Computer Solutions\UltraEdit;C:\Program Files\IDM Computer Solutions\UltraCompare;D:\Python\Python36-32\Scripts;D:\Python\Python36-32\Scripts\;D:\Python\Python36-32\;C:\Users\ZYP\AppData\Local\Programs\Fiddler
PATHEXT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROCESSOR_ARCHITECTURE x86
PROCESSOR_ARCHITEW6432 AMD64
PROCESSOR_IDENTIFIER Intel64 Family 6 Model 42 Stepping 7, GenuineIntel
PROCESSOR_LEVEL 6
PROCESSOR_REVISION 2a07
PROGRAMDATA C:\ProgramData
PROGRAMFILES C:\Program Files (x86)
PROGRAMFILES(X86) C:\Program Files (x86)
PROGRAMW6432 C:\Program Files
PSMODULEPATH C:\windows\system32\WindowsPowerShell\v1.0\Modules\
PUBLIC C:\Users\Public
PYCHARM_HOSTED 1
PYCHARM_MATPLOTLIB_PORT 55790
PYTHONIOENCODING UTF-8
PYTHONPATH C:\Program Files\JetBrains\PyCharm 2018.2.4\helpers\pycharm_matplotlib_backend;C:\Users\ZYP\PycharmProjects\day21
PYTHONUNBUFFERED 1
SESSIONNAME Console
SYSTEMDRIVE C:
SYSTEMROOT C:\windows
TEMP C:\Users\ZYP\AppData\Local\Temp
TMP C:\Users\ZYP\AppData\Local\Temp
USERDOMAIN ZYP-PC
USERNAME ZYP
USERPROFILE C:\Users\ZYP
WINDIR C:\windows
WINDOWS_TRACING_FLAGS 3
WINDOWS_TRACING_LOGFILE C:\BVTBin\Tests\installpackage\csilogfile.log
_CLASSLOAD_HOOK jvmhook
_JAVA_OPTIONS -Xrunjvmhook -Xbootclasspath/a:D:\HP\QUICKT~1\bin\JAVA_S~1\classes;D:\HP\QUICKT~1\bin\JAVA_S~1\classes\jasmine.jar
RUN_MAIN true
SERVER_NAME genuine.microsoft.com
GATEWAY_INTERFACE CGI/1.1
SERVER_PORT 8000
REMOTE_HOST
CONTENT_LENGTH
SCRIPT_NAME
SERVER_PROTOCOL HTTP/1.1
SERVER_SOFTWARE WSGIServer/0.2
REQUEST_METHOD GET
PATH_INFO /app01/index1/
QUERY_STRING
REMOTE_ADDR 127.0.0.1
CONTENT_TYPE text/plain
HTTP_HOST 127.0.0.1:8000
HTTP_USER_AGENT Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_LANGUAGE zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
HTTP_ACCEPT_ENCODING gzip, deflate
HTTP_DNT 1
HTTP_CONNECTION keep-alive
HTTP_UPGRADE_INSECURE_REQUESTS 1
HTTP_PRAGMA no-cache
HTTP_CACHE_CONTROL no-cache
wsgi.input <_io.BufferedReader name=168>
wsgi.errors <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
wsgi.version (1, 0)
wsgi.run_once False
wsgi.url_scheme http
wsgi.multithread True
wsgi.multiprocess False
wsgi.file_wrapper <class 'wsgiref.util.FileWrapper'>

其中  HTTP_USER_AGENT     通过这个信息可以知道用户是用什么终端发来的请求,可以知道用户用的是iPhong还是用安卓系统。还可以判断用户是手机端就发回给一个手机端的页面。如果是PC端就返回一个PC端的页面。这是一个字典,用 request.environ['HTTP_USER_AGENT'] 就可以获取到这个信息了

上面的输出有这个内容,这里我们再打印出来看下

print(request.environ['HTTP_USER_AGENT'])

结果为:

HTTP_USER_AGENT Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0

模板的继承-extends

方法:

  将要改变的内容用black包起来,不动的部分继承模板即可

先写一个模板

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %} 这里表示标签可变{% endblock %}</title>
<link rel="stylesheet" href="/static/comments.css"></link>
{% block css %} 这里既可以继承模板的css,也可定义自己的css{% endblock %}
</head>
<body>
<div class="head">后台管理系统</div>
<div class="left">
<a class="menu" href="/ORM/user_info/">用户管理</a>
<a class="menu" href="/ORM/user_group/">用户组管理</a>
</div>
<div class="right">
{% block content %}
<span>这里编写变化的内容</span>
{% endblock %}
</div>
<script src="/static/jquery-3.2.1.min.js"></script>
{% block js %} 这里既可以继承模板的js,也可定义自己的js{% endblock %}
</body>
</html>

现在我们来写第一个继承模板的页面

先要声明是继承那个模板

然后定义自己的内容,标题,等信息

{% extends 'demo.html' %}
user_info.html页面
{% extends 'demo.html' %}
{% block title %}用户信息{% endblock %}
{% block content %}
<p>用户信息</p>
{% for i,j in dic.items %}
<p>{{ i }}:{{ j }}</p>
<p>{{ dic.username }}</p>
{% endfor %} {% endblock %}

注意css(style标签)和js(script标签),一般也都会需要追加css样式和js。css就接在模板的css后面写,js就还是写在最后的位置,如果有jQuery,必须要在导入jQuery静态文件的后面。
只能继承一个模板,不能继承多个。

模板的导入-include

先写个tag.html

<form>
<input type="text" />
<input type="submit" />
</form>

然后我们在user_info.html中使用include

{% extends 'demo.html' %}
{% block title %}用户信息{% endblock %}
{% block content %}
<p>用户信息</p>
{% for i,j in dic.items %}
<p>{{ i }}:{{ j }}</p>
<p>{{ dic.username }}</p>
{% endfor %}
{% include "tag.html" %}
{% include "tag.html" %}
{% endblock %}

上面的例子中看到了,模板的导入可以导入多次的。实际的应用中可能会结合模板语言的for循环,每条数据都通过这个组件渲染然后输出到页面。

内置函数

<p>{{ time | date:"Y-m-d H:i:s" }}</p>
<p>{{ time|truncatechars:'' }}</p>

在页面里使用双大括号 {{ value }} 取值的时候,还可以加上管道符,对结果进行处理后在输出。
比如输出一个日期,首先得先有一个日期对象输出到页面:

#views.py

def time(request):
import datetime
time = datetime.datetime.now()
print(time)
return render(request,'time.html',{"time":time})

然后写个页面,显示时间日期

//time.html

{% extends 'demo.html' %}
{% block title %}用户管理{% endblock %}
{% block content %}
<p>{{ time | date:"Y-m-d H:i:s" }}</p>
{% endblock %}

添加对应的url

执行后的结果为:

程序运行,输出的结果为:2019-01-17 19:08:09.720543
网页显示的为:2019-01-17 19:08:09

另外还有一个截取的

<p>{{ time|truncatechars:'' }}</p>

页面上显示的结果为:2019-01...

只输出10个字符,实际是只截取了前面7个字符,之后跟3个点(...)

自定义函数

要自定义函数,按照下面的步骤操作:

  1. 在APP下,创建templatetags目录,名字必须是这个。
  2. 创建任意 .py 文件,这里文件名随意,比如:demo.py。
  3. 导入template。from django import template,文件里创建一个template.Library()对象,名字是register。这里的对象名字必须是register。
  4. 然后写自己的函数,
  5. 装饰器可以用@register.simple_tag,@register.filter这两种

现在我们分别用这两个装饰器来写自己的函数

1、@register.simple_tag

from django import template

register = template.Library()

@register.simple_tag
def func(a,b,c):
return a + b + c @register.simple_tag
def func1():
return func1 @register.simple_tag
def func2(a,b):
return a + b

然后页面文件中加载你的文件{% load func %}。放在顶部就好了。只要在你使用前加载加可以,不一定要在上面。

如果有extends({% extends 'demo.html' %}),放在extends的下面。

接着使用自己定义的函数,并给自己的函数加上参数,{% 函数名 参数1 参数2 %}

{% extends 'demo.html' %}
{% load demo %}
{% block title %}用户管理{% endblock %}
{% block content %}
<p>{{ time | date:"Y-m-d H:i:s" }}</p>
<p>{{ time|truncatechars:'' }}</p>
<p>func {% func 1 2 3 %}</p>
<p>func1 {% func1 %}</p>
<p>func2 {% func2 "a" "b" %}</p>
{% endblock %}

页面上的内容为

从上面可以看出来,参数可以是多种形式的

2、@register.filter

@register.filter
def func3(a,b):
return a + b
@register.filter
def func4(a):
return a

之后在页面里使用的时候,传参的方式也是不同的,并且  filter最多只能传入2个参数,并且中间连空格都不能有:

页面添加如下

    <p>func3 {{  "a"|func3:"b" }}</p>
<p>func4 {{ "b"|func4 }}</p>

如果一定要用filter,并且还要传入多个参数,只能自己处理了,比如 {{ 'abc'|func4:'123,456,789' }} 这样还是传2个参数,后面的作为一个字符串,在我们自己的函数里做分割处理。

只传入一个参数也是可以的,第二个不写就好了。但是不能没参数。像 {{ my_fun }} 这样的用法是获取通过 return render() 给的字典里查找这个key来获取值。
单独使用,明显是simple_tag更方便,参数没有限制。
但是filter能够作为if的条件:

{% if 'abc'|func4:'' == 'abc: 123' %}
<h1>filter能够作为if的条件</h1>
{% endif %}

上面传参都是加了引号,表示传入的是字符串。不加引号,就是直接传入数字,类型是int。
也可以传入变量或者嵌套使用,比如处理函数最后这样返回 return render(request, 'time.html', {'str': "987"})

<h3>{{ 'abc'|func4:'def'|func4:'' }}</h3>
<h3>{{ 'xyz'|func4:str }}</h3>

示例-分页

LIST = []
for i in range(500):
LIST.append(i) def user_list(request):
p = request.GET.get("p",1)
p = int(p)
count = 20
#data = LIST[0:10]
#data = LIST[10:20]
start = (p-1)*count
end = p * count
data = LIST[start:end]
all_LIST = len(LIST)
page,y = divmod(all_LIST,count)
if y:
page += 1
page_list = []
# start_index = p - 5
# end_index = p + 5 + 1
page_num = 7
if page < page_num:
start_index = 1
end_index = page + 1
else:
if p <= (page_num + 1)/2:
start_index = 1
end_index = page_num + 1
else:
start_index = p - (page_num - 1)/2
end_index = p + (page_num + 1)/2
if (p + (page_num - 1)/2) > page:
end_index = page + 1
start_index = page - page_num + 1
if p == 1:
up = '<a class= "a " href="javascript:void(0);"> 上一页 </a>'
else:
up = '<a class= "a " href="/app01/user_list/?p=%s"> 上一页 </a>' %(p - 1)
page_list.append(up)
for i in range(int(start_index),int(end_index)):
if i == p:
temp = '<a class= "a active" href="/app01/user_list/?p=%s"> %s </a>' %(i, i)
else:
temp = '<a class= "a" href="/app01/user_list/?p=%s"> %s </a>' %(i,i)
page_list.append(temp)
if p == page:
down = '<a class= "a " href="javascript:void(0);"> 下一页 </a>'
else:
down = '<a class= "a " href="/app01/user_list/?p=%s"> 下一页 </a>' %(p + 1)
page_list.append(down) jump = """
<input style="width: 40px" type="text" /><a onclick='jumpTo(this,"/app01/user_list/?p=");'>GO</a>
<script>
function jumpTo(ths,base){
var p = ths.previousSibling.value;
location.href = base + p;
}
</script>
"""
page_list.append(jump)
page_str = mark_safe(''.join(page_list)) # xss攻击 page_str = mark_safe(page_str)
# from django.utils.safestring import mark_safe
# page_str = """
# <a href="/app01/user_list/?p=1"> 1 </a>
# <a href="/app01/user_list/?p=2"> 2 </a>
# <a href="/app01/user_list/?p=3"> 3 </a>
# """
# page_str = mark_safe(page_str) return render(request,"user_list.html",{"list":data,"page_str":page_str})
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.head .a {
display: inline-block;
padding: 5px;
background-color: #bce8f1;
margin: 5px;
}
.head .a.active {
background-color: #dddddd;
color: red;
}
</style>
</head>
<body>
<ul>
{% for i in list %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<div class="head">
{{ page_str }}
</div>
</body>
</html>

这里是有问题的,这里的a连接的html代码是处理函数传过来了,之后在页面里再用模板语言把内容加载进来。这里会有个XSS***的问题。
XSS***,大致就是你提供一个输入框给用户输入内容,然后可以把用户输入的内容在页面中显示出来,比如说论坛、评论。那么用户可以在这里输入源码,比如js脚本,然后你的后台直接不做处理就把代码返回给前端,那么前端就可能执行这段代码了。
所以默认模板语言认为加载的内容都是不安全的,所以都作为字符串加载。有2中方法可以声明这段内容是安全的,那么就能正常的在前端按照我们写的标签的样子展示出来。

  • 方法一:在处理函数里使用 mark_safe(page_str) 来转一下,使用前先导入模块 from django.utils.safestring import mark_safe
  • 方法二:在前端的模板语言里,在字符串后面使用管道符调用一个内置的filter方法,{{ page_str|safe }}

两种方法可以任选一种使用,在例子里都注释掉了,现在可以放开其中一种方法。两个方法一起用试下来也不会报错

自定义功能模块

上面的分页功能代码比较多(还可以继续优化),而且别的页面里也会需要用到分页的功能。把分页的功能单独提取出来,封装到一个单独的类里,做成一个功能模块。这种功能模块也集中存放在一个文件夹里,在项目目录下创建utils文件夹,再创建一个py文件pagination.py作为模块。要用的时候,处理函数里只需要实例化这个类,调用类中的方法就可以了:

from django.utils.safestring import mark_safe

class Page(object):

    def __init__(self, p, all_list, count=20, page_num=7):
self.p = p
self.all_list = all_list
self.count = count
self.page_num = page_num @property
def start(self):
return (self.p - 1) * self.count @property
def end(self):
return self.p * self.count @property #转为静态方法
def page(self):
page, y = divmod(self.all_list, self.count)
if y:
page += 1
return page def page_str(self, base_url):
page_list = []
if self.page < self.page_num:
start_index = 1
end_index = self.page + 1
else:
if self.p <= (self.page_num + 1) / 2:
start_index = 1
end_index = self.page_num + 1
else:
start_index = self.p - (self.page_num - 1) / 2
end_index = self.p + (self.page_num + 1) / 2
if (self.p + (self.page_num - 1) / 2) > self.page:
end_index = self.page + 1
start_index = self.page - self.page_num + 1
if self.p == 1:
up = '<a class= "a " href="javascript:void(0);"> 上一页 </a>'
else:
up = '<a class= "a " href="%s?p=%s"> 上一页 </a>' % (base_url, self.p - 1)
page_list.append(up)
for i in range(int(start_index), int(end_index)):
if i == self.p:
temp = '<a class= "a active" href="%s?p=%s"> %s </a>' % (base_url, i, i)
else:
temp = '<a class= "a" href="%s?p=%s"> %s </a>' % (base_url, i, i)
page_list.append(temp)
if self.p == self.page:
down = '<a class= "a " href="javascript:void(0);"> 下一页 </a>'
else:
down = '<a class= "a " href="%s?p=%s"> 下一页 </a>' % (base_url, self.p + 1)
page_list.append(down) jump = """
<input style="width: 40px" type="text" /><a onclick='jumpTo(this,"%s?p=");'>GO</a>
<script>
function jumpTo(ths,base){
var p = ths.previousSibling.value;
location.href = base + p;
}
</script>
""" % (base_url)
page_list.append(jump)
page_str = mark_safe(''.join(page_list))
return page_str
处理函数 views.py 中的内容
from utils import pagination
# Create your views here. #分页
LIST = []
for i in range(500):
LIST.append(i) def user_list(request):
p = request.GET.get("p", 1)
p = int(p)
page_obj = pagination.Page(p, len(LIST))
data = LIST[page_obj.start:page_obj.end] # 这里去掉了括号,在类里面使用了静态方法
page_str = page_obj.page_str("/app01/user_list/")
return render(request, "user_list.html", {"list": data, "page_str": page_str})

Cookie

什么是 Cookie?

Cookie 是一些数据, 存储于你电脑上的文本文件中。

当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息。

Cookie 的作用就是用于解决 "如何记录客户端的用户信息":

  • 当用户访问 web 页面时,他的名字可以记录在 cookie 中。
  • 在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录。

Cookie 以名/值对形式存储,如下所示:

username=John Doe

当浏览器从服务器上请求 web 页面时, 属于该页面的 cookie 会被添加到该请求中。服务端通过这种方式来获取用户的信息。

示例-登录

大致实现方式为:

先通过登录页面将登录成功的用户名发送给客户端保存到cookie中。然后在欢迎页面请求客户的的cookie拿到客户端登录成功的用户名。

#views.py

#cookie
user_dic = {
"aaa":{"pwd":""},
"bbb":{"pwd":""}
}
def login(request):
if request.method == "GET":
return render(request,"login.html")
if request.method == "POST":
u = request.POST.get("username")
p = request.POST.get("pwd")
uu = user_dic.get(u)
if uu:
if uu["pwd"] == p:
res = redirect("/app01/index/")
res.set_cookie("username",u)
return res
else:
return render(request,"login.html")
else:
return render(request, "login.html") def index(request):
v = request.COOKIES.get("username")
if v:
return render(request,'index.html',{"v":v})
else:
return redirect("/app01/index/")

添加url的对应关系

    path('login/', views.login,name='login'),
path('index/', views.index,name='index'),

login.html

    <form action="/app01/login/" method="post">
<input type="text" placeholder="用户名" name = "username" />
<input type="password" placeholder="密码" name = "pwd" />
<input type="submit" value="提交" />
</form>

index.html

    <a>{{ v }}</a>

尝试直接访问index,还是会跳转到login。只有登录成功后才会显示欢迎页面。这里的用户名是向客户端的浏览器请求获取到的。可以打开浏览器的F12开发人员工具在网络里查看到:

Cookie的语法

获取Cookie

request.COOKIES.get("username")   =  request.COOKIES["username"]

cookies本身是键值对,因此取值和字典取值一样

设置Cookie

设置cookies就是把对象返回客户端之前,先拿到这个对象,我们先创建个参数接受返回的内容:

res =  render(request,"login.html")  或者
res =  redirect("/app01/index/")   或者
res = HttpResponse("username")

拿到res后设置:res.set_cookies(key,value,……)所有参数如下
  • key :键
  • value='' :值
  • max_age=None :超时时间,单位是秒。不设置就是浏览器关闭就马上失效
  • expires=None :超时时间节点,设置一个具体的日期
  • path='/' :Cookie生效的路径,/ 表示根路径,根路径的cookie可以被任何url的页面访问
  • domain=None :Cookie生效的域名
  • secure=False :https传输,如果网站是https的,写cookie的时候把这个参数设置为True
  • httponly=Fals :只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)。document.cookie 获取不到设置了这个参数的cookie。

如果要注销,清除cookie,那么就 rep.set_cookie(key) ,给你的key设置个空值,并且超时时间设置为马上失效。

#max_age=5  cookies最大失效时间,5秒
res.set_cookie("username",u,max_age=5) #expires = ctime 设置从某一时间后cookies开始失效
import datetime
current_date = datetime.datetime.utcnow()
ctime = current_date +datetime.timedelta(seconds=5)
res.set_cookie("username",u,expires = ctime)

客户端操作Cookie

  • document.cookie :获取到cookie,返回的是字符串 "key1=value1; key2=value2"
  • document.cookie = "key=value;" :添加一个cookie的键值,其他参数也能加

分页-定制每页显示的数量

利用cookie,在上前面的分页的例子的基础上,增加一个功能,用户可以定制每页显示多少条数据。Web界面上值需要追加一个select框,然后绑定事件:

<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.head .a {
display: inline-block;
padding: 5px;
background-color: #bce8f1;
margin: 5px;
}
.head .a.active {
background-color: #dddddd;
color: red;
}
</style>
</head>
<body>
<ul>
{% for i in list %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<div>
<select class="ps"> //添加可供用户选择的select框
<option value="">10</option>
<option value="">30</option>
<option value="">50</option>
</select>
</div>
<div class="head">
{{ page_str }}
{# <input style="width: 40px" type="text" /><a class="go" href="/app01/user_list/?p=">GO</a>#}
</div>
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
$(".go").click( function () {
var p = $(this).siblings().val()
location.href = base + p
})
//没有下面的这个,当值为10的时候不会变化,这里在获取框架前,先获取当前的值,并设置值为当前获取的值
$(function () {
var v = $.cookie("se")
$(".se").val(v)
})
$(".ps").change(function () {
var v = $(this).val() //获取select的值
console.log(v)
$.cookie("se",v,{"path":"/app01/user_list/"}) //设置值为用户修改的值,然后后台获取后修改
location.reload()
}) </script>

给select绑定一个事件,当选项改变时,会获取当前select的值,然后将值传给cookie,

这里限定了生效的路径,只有这个叶脉内按照这个cookie的值显示数据的数量,如果别的页面有同样的需求,就不用加path这个参数,

上面代码如果只绑定了select事件,当你select选择的是10,但页面取到的可能是20,就是select没有同步,并且造成你永远无法设置为10,这里需要在加载框架前,需要先获取下cookie这个值,然后将这个值传给select

然后后台处理函数稍加修改:

先获取到cookie的值,然后将值传给类

将之前的user_list处添加如下

val = request.COOKIES.get("se")   #获取前端设置的值
print("val",val)
page_obj = pagination.Page(p, len(LIST),int(val)) #将val传给pagination的Page类

加密的Cookie

就是带签名的cookie。之前使用的cookie都是明文保存在客户端的,还可以对cookie加密。

#加密的cookie
def cook(request):
obj = HttpResponse("t")
obj.set_signed_cookie("user","aaaaaaa",salt="adfg")
a = request.get_signed_cookie("user",salt='adfg')
print(a) #aaaaaaa
return obj

网页上的cookie为:

装饰器

上面登录的例子中已经完成了登录验证的功能。实际应用中,很多页面都需要登录验证,这就需要把验证的功能独立出来并且做成装饰器。之后只要把其它处理函数装饰上即可。

FBV的装饰器

之前的登录验证,是把验证,登录后放一个函数里面了,用装饰器只需要将验证放在装饰器中即可

#装饰器
#FBV的装饰器
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username")
if not v:
return redirect("/app01/login/")
return func(request,*args,**kwargs)
return inner @auth
def user_auth(request):
v = request.COOKIES.get("username")
return render(request,'user_auth.html',{"v":v})

CBV的装饰器

CBV的装饰器有2中情况。一种是只装饰一个或部分方法,一种是装饰整个类中的方法。装饰器还是上面的装饰器。

单独装饰一个方法

#FBV的装饰器

def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username")
if not v:
return redirect("/app01/login/")
return func(request,*args,**kwargs)
return inner from django import views
from django.utils.decorators import method_decorator class Order(views.view): @method_decorator(auth)
def get(self,request):
v = request.COOKIES.get("username")
return render(request, 'user_auth.html', {"v": v}) @method_decorator(auth)
def post(self,request):
pass

装饰类中的所有方法

#装饰类,使所有的方法都装饰上
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username")
if not v:
return redirect("/app01/login/")
return func(request,*args,**kwargs)
return inner
class Order(views.view): @method_decorator(auth)
def dispatch(self, request, *args, **kwargs):
    """简单的继承并重构这个方法,然后什么都不改变,就为了加上装饰器"""
return super(Order, self).dispatch(request, *args, **kwargs) def get(self,request):
v = request.COOKIES.get("username")
return render(request, 'user_auth.html', {"v": v}) def post(self,request):
pass

装饰整个类
装饰的还是dispatch方法,把装饰器写在类上面

@method_decorator(auth,name="dispatch")
class Order(views.view): def get(self,request):
v = request.COOKIES.get("username")
return render(request, 'user_auth.html', {"v": v}) def post(self,request):
pass

Django基础--4的更多相关文章

  1. Python之路-(js正则表达式、前端页面的模板套用、Django基础)

    js正则表达式 前端页面的模板套用 Django基础 js正则表达式: 1.定义正则表达式 /.../  用于定义正则表达式 /.../g 表示全局匹配 /.../i 表示不区分大小写 /.../m ...

  2. Django 基础教程

    Django 基础教程 这是第一篇 Django 简介 »  Django 是由 Python 开发的一个免费的开源网站框架,可以用于快速搭建高性能,优雅的网站! 你一定可以学会,Django 很简单 ...

  3. python的django基础篇

    一.Django基础 Django 是用Python开发的一个免费开源的Web框架,可以用于快速搭建高性能,优雅的网站! Django的特点: 强大的数据库功能:拥有强大的数据库操作接口(QueryS ...

  4. Python学习(二十六)—— Django基础一

    转载自:http://www.cnblogs.com/liwenzhou/p/8258992.html 一.Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的 ...

  5. Django基础(一)

    Django基础(一) 知识预览 Django基本命令 二 路由配置系统(URLconf) 三 编写视图 四 Template 五 数据库与ORM admin的配置 一 什么是web框架? 框架,即f ...

  6. python3之Django基础篇

    一.Django基础 Django 是用Python开发的一个免费开源的Web框架,可以用于快速搭建高性能,优雅的网站! Django的特点: 强大的数据库功能:拥有强大的数据库操作接口(QueryS ...

  7. DJango 基础 (1)

    django基础 知识点: 基本认知 工具准备 新建项目 目录及文件说明 开发服务器 创建视图函数 新建应用(app) 1.基本认知 Django是用Python开发的一个免费开源的Web框架,可以用 ...

  8. Django基础和基本使用

    Django基础 Django是Python下的一款著名的Web框架 框架 任何语言进入到高级部分时,会有认证.session.http.连接数据库等等功能操作,没有框架时需要自己实现 框架 是整个或 ...

  9. {Django基础十之Form和ModelForm组件}一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 ModelForm

    Django基础十之Form和ModelForm组件 本节目录 一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 Model ...

  10. {Django基础九之中间件} 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证

    Django基础九之中间件 本节目录 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证 六 xxx 七 xxx 八 xxx 一 前戏 我们在前面的课程中已经学会了 ...

随机推荐

  1. Spring Boot如何初始化数据

    Hibernate机制:classpath下的import.sql,l在ddl-auto是create和create-drop时自动执行,如果ddl-auto设置为update就不合适,所以此选项不适 ...

  2. Linux忘记roo密码的解决办法

    Linux忘记root密码有三种解决办法: 下面详细介绍第一种: 重启系统后出现GRUB界面在引导装载程序菜单上,用上下方向键选择你忘记密码的那个系统键入“e” 来进入编辑模式.   接下来你可以看到 ...

  3. springMvc使用restful风格

    转载:https://blog.csdn.net/weide_java/article/details/53793769 1,REST架构师一个抽象的概念,目前主要是基于HTTP协议实现,其目的是为了 ...

  4. 3 不用IDE开发groovy

    1       不用IDE开发groovy 1.1  不用IDE开发的方法 可以在IDE中运行Groovy类或者脚本,但是Groovy也提供了其他运行途径.你能运行Groovy代码基于以下: ·    ...

  5. 转 Python多版本管理-pyenv

    #######for linux https://www.cnblogs.com/saneri/p/7642316.html 经常遇到这样的情况: 系统自带的Python是2.x,自己需要Python ...

  6. 安装NetCDF及HDF5

    平台信息 Description: CentOS Linux release 7.6.1810 (Core) 安装步骤 下载NetCDF.HDF5.zlib.curl[使用wget命令即可] 解包:t ...

  7. Surface Shader(表面着色器)

    Shader "Custom/Surface_Shadeer" { Properties {                                             ...

  8. 贪心:钱币找零问题(C++)

    贪心是一种算法范例,它一点一点地构建解决方案,总是选择下一个提供最明显和最直接好处的部分.因此,选择局部最优也会导致全局解的问题最适合贪心问题. 例如,考虑分数背包问题.局部最优策略是选择权重比最大的 ...

  9. ApplicationContextAware的作用

    ApplicationContextAware其实我们看到---Aware就知道是干嘛用的了,就是属性注入的, 但是这个ApplicationContextAware的不同地方在于,实现了这个接口的b ...

  10. 初识contiki(2.7版本)

    一个偶然的机会,我接触到了contiki这个家伙. Contiki 是一个开源的.高度可移植的.采用 C 语言开发的非常小型的嵌入式操作系统,针对小内存微控制器设计,适用于联网嵌入式系统和无线传感器网 ...