通过上节课的学习,我们已经对Django有了简单的了解,现在来深入了解下~
1. 路由系统
1.1 单一路由对应
url(r'^index$', views.index),
1.2 基于正则的路由
url(r'^index/(\d*)', views.index),
url(r'^manage/(?P<name>\w*)/(?P<id>\d*)', views.manage),
- 找到urls.py文件,修改路由规则
from django.conf.urls import url,include
from django.contrib import admin
from cmdb import views urlpatterns = [
url(r'^index', views.index),
url(r'^detail-(\d+).html/', views.detail),
]
- 在views.py文件创建对应方法
USER_DICT = {
'1':{'name':'root1','email':'root@live.com'},
'2':{'name':'root2','email':'root@live.com'},
'3':{'name':'root3','email':'root@live.com'},
'4':{'name':'root4','email':'root@live.com'},
} def index(request):
return render(request,"index.html",{"user_dict":USER_DICT}) def detail(request,nid): # nid指定的是(\d+)里的内容
detail_info = USER_DICT[nid]
return render(request, "detail.html", {"detail_info": detail_info})
1.3 url分组
在url.py增加对应路径
from django.conf.urls import url,include
from django.contrib import admin
from cmdb import views urlpatterns = [
url(r'^index', views.index),
url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html/', views.detail),<br> # nid=\d+ uid=\d+
]
在views.py文件创建对应方法
def detail(request,**kwargs):
print(kwargs)
#{'nid': '4', 'uid': '3'}
nid = kwargs.get("nid")
detail_info = USER_DICT[nid]
return render(request, "detail.html", {"detail_info": detail_info})
1.4 为路由映射名称
from django.conf.urls import url,include
from django.contrib import admin
from cmdb import views urlpatterns = [
url(r'^asdfasdfasdf/', views.index, name='i1'), #第一种方式i1
url(r'^yug/(\d+)/(\d+)/', views.index, name='i2'), #第二种方式i2
url(r'^buy/(?P<pid>\d+)/(?P<nid>\d+)/', views.index, name='i3'), #第三种方式i3
]
在templates目录下的index.html
<body>
{#第一种方法i1 路径asdfasdfasdf/#}
{#<form action="{% url "i1" %}" method="post">#}
{#第二种方法i2 路径yug/1/2/#}
{#<form action="{% url "i2" 1 2 %}" method="post">#}
{#第三种方法i3 路径buy/1/9//#}
<form action="{% url "i3" pid=1 nid=9 %}" method="post">
<p><input name="user" type="text" placeholder="用户名"/></p>
<p><input name="password" type="password" placeholder="密码"/></p>
<p><input type="submit" value="提交"/></p>
</form>
</body>
1.5 根据app对路由分类
主程序urls.py文件
from django.conf.urls import url,include
from django.contrib import admin urlpatterns = [
url(r'^monitor/', include('monitor.urls')), #调整到monitor目录中的urls.py文件
]
cmdb下的url.py文件
from django.conf.urls import url
from django.contrib import admin
from monitor import views
#
urlpatterns = [
url(r'^login', views.login),
]
1.6 获取当前URL
view.py中配置
def index(request):
print(request.path_info) #获取客户端当前的访问链接
# / index
return render(request,"index.html",{"user_dict":USER_DICT})
在templates目录下的index.html文件
<form action="{{ request.path_info }}" method="post">
<p><input name="user" type="text" placeholder="用户名"/></p>
<p><input name="password" type="password" placeholder="密码"/></p>
<p><input type="submit" value="提交"/></p>
</form>
1.7 默认值
在url.py增加对应路径
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^index/',views.index,{"name":"root"}),
]
在views.py文件创建对应方法
from django.shortcuts import render,HttpResponse
def index(request,name):
return HttpResponse("%s is ok"%name)
运行并访问http://127.0.0.1:8000/index,页面显示:root is ok
1.8 命名空间
不同的url指向同一个视图就需要借助命名空间
主程序url.py文件
from django.conf.urls import url,include urlpatterns = [
url(r'^a/', include('app01.urls', namespace='author-polls')),
url(r'^b/', include('app01.urls', namespace='publisher-polls')),
]
app01下的urls.py文件
from django.conf.urls import url
from app01 import views app_name = 'app01'
urlpatterns = [
url(r'^index/$', views.detail, name='detail')
]
在views.py文件创建对应方法
from django.shortcuts import render,HttpResponse
def index(request):
return HttpResponse("ok")
访问http://127.0.0.1:8000/a/index,页面显示"ok"
以上定义带命名空间的url之后,使用name生成URL时候,应该如下:
- v = reverse('author-polls:detail')
- {% url 'author-polls:detail'%}
2. 视图
2.1 获取用户请求数据
request.GET
request.POST
request.FILES
其中,GET一般用于获取/查询 资源信息,而POST一般用于更新 资源信息 ; FILES用来获取上传文件;
2.2 checkbox等多选的内容
在templates目录下创建login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="POST" >
<p>
男:<input type="checkbox" name="favor" value="11"/>
女:<input type="checkbox" name="favor" value="22"/>
人妖:<input type="checkbox" name="favor" value="33"/>
</p>
<input type="submit" value="提交"/>
</form>
</body>
</html>
修改views.py文件对表单处理
def login(request):
#checkbox 多选框
if request.method == "POST":
favor_list = request.POST.getlist("favor") #getlist获取多个值
print(favor_list) #多选框获取到的是列表格式
#['11', '22', '33']
return render(request,"login.html")
elif request.method == "GET":
return render(request,"login.html")
else:
print("other")
2.3 上传文件
文件对象 = reqeust.FILES.get()
文件对象.name
文件对象.size
文件对象.chunks()
在templates目录下创建login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="POST" enctype="multipart/form-data">
<p>
<input type="file" name="files"/>
</p>
<input type="submit" value="提交"/>
</form>
</body>
</html>
修改views.py文件对表单处理
def login(request):
#file 上传文件
if request.method == "POST":
obj = request.FILES.get('files') #用files获取文件对象
if obj:
print(obj, type(obj), obj.name)
# test.jpg <class 'django.core.files.uploadedfile.InMemoryUploadedFile'> test.jpg
import os
file_path = os.path.join('upload', obj.name)
f = open(file_path, "wb")
for item in obj.chunks(): #chunks表示所有的数据块,是个迭代器
f.write(item)
f.close()
return render(request,"login.html")
elif request.method == "GET":
return render(request,"login.html")
else:
print("other")
2.4 请求的其他信息
from django.core.handlers.wsgi import WSGIRequest
request.environ #获取所有用户请求信息
request.environ['HTTP_USER_AGENT'] #获取用户请求的设备信息
2.5 FBV & CBV
2.5.1 FBV
1.在templates目录下创建home.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/home/" method="POST">
<p>
<input type="text" name="user" placeholder="用户名"/>
</p>
<p>
<input type="password" name="pwd" placeholder="密码"/>
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
</body>
</html>
2. 在urls.py文件增加home路径
from django.conf.urls import url,include
from django.contrib import admin
from cmdb import views urlpatterns = [
# 固定语法
url(r'^home/', views.Home.as_view()),
]
3. 在views.py文件创建函数Home
def home(request):
return render(request,"home.html")
2.5.2 CBV
1. 在templates目录下创建home.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/home/" method="POST">
<p>
<input type="text" name="user" placeholder="用户名"/>
</p>
<p>
<input type="password" name="pwd" placeholder="密码"/>
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
</body>
</html>
2. 在urls.py文件增加home路径
from django.conf.urls import url,include
from django.contrib import admin
from cmdb import views urlpatterns = [
# 固定语法
url(r'^home/', views.Home.as_view()),
]
3. 在views.py文件创建类Home
from django.views import View class Home(View):
# 先执行dispatch里面的内容
def dispatch(self,request, *args, **kwargs):
print("before")
# 调用父类中的dispatch
result = super(Home,self).dispatch(request, *args, **kwargs)
print("after")
return result # 根据反射获取用户提交方式,执行get或post方法
def get(self,request):
print(request.method)
return render(request,"home.html") def post(self,request):
print(request.method)
return render(request,"home.html")
3. Templates
通过之前的学习我们已经可以实现模板功能,但是大家会发现一个问题:按照之前学习的方法,同一个系统下的不同功能页面,我们需要在每个页面复制菜单、左侧右侧导航等;这样虽然也可以实现我们的需求,但是如果菜单、左右侧导航有所修改,我们就需要再挨个去变更。那么有没有其他的方法解决这个问题呢?----这里我们引入母版。
3.1 继承
首先,我们需要新建一个母版html文件(如:master.html),文件内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<style>
.pg_header{
height: 50px;
background-color: aquamarine;
color:green;
}
</style>
</head>
<body>
<div class="pg_header">母版测试</div>
{% block content %}{% endblock %}
</body>
</html>
再新建一个子板html文件(如:userlist.html),文件内容如下:
{% extends 'master.html' %} <!--有多个母版时,此处可区分继承的是哪个母版-->
{% block title %}用户管理{% endblock %}
{% block content %}
<h1>用户管理</h1>
<ul>
{% for i in u %}
<li>{{ i }}</li>
{% endfor %}
</ul>
{% endblock %}
运行后查看页面,userlist.html继承了母版master.html的CSS和JS。(想要修改导航、菜单等内容,直接修改母版内容即可。)
学习完上面的东西之后,大家很快就会发现另外一个问题:上面的东西虽然能实现我们的需求,但是每个页面想要使用自己的CSS和JS就不行了,如何解决这个问题呢?
1.母版文件内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<style>
.pg_header{
height: 50px;
background-color: aquamarine;
color:green;
}
</style>
{% block css %}{%endblock%}
</head>
<body>
<div class="pg_header">母版测试</div>
{% block content %}{% endblock %}
<script src="XXX"></script>
{% block js %}{%endblock%}
</body>
</html>
2. 子板文件内容
{% extends 'master.html' %} <!--有多个母版时,此处可区分继承的是哪个母版-->
{% block title %}用户管理{% endblock %}
{% block content %}
<h1>用户管理</h1>
<ul>
{% for i in u %}
<li>{{ i }}</li>
{% endfor %}
</ul>
{% endblock %}
{% block css%}
<style>
.body{
color:red
}
</style>
{% endblock%}
{% block js%}
<script>.....</script>
{% endblock%}
PS:一个html文件只能继承一个母版
3.2 导入
新建tag.html文件, 文件内容如下:
<form>
<input type="text"/>
<input type="submit"/>
</form>
子板文件中插入代码
<% include "tag.html" %>
<% include "tag.html" %>
<% include "tag.html" %>
<% include "tag.html" %>
3.3 自定义simple_tag
在app中创建templatetags模块
- 创建任意 .py 文件,如:xx.py
#!/usr/bin/env python
#coding:utf-8
from django import template
from django.utils.safestring import mark_safe register = template.Library() @register.simple_tag
def my_simple_time(v1,v2,v3):
return v1 + v2 + v3 @register.simple_tag
def my_input(id,arg):
result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
return mark_safe(result) -
在使用自定义simple_tag的html文件顶部导入之前创建的 xx.py 文件名
{% load xx %}
- 使用simple_tag
{% 函数名 arg1 arg2 %}
{% my_simple_time 1 2 3%}
{% my_input 'id_username' 'hide'%} - 在settings中配置当前app,不然django无法找到自定义的simple_tag
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
)
3.4 自定义filter
在app中创建templatetags模块
-
创建任意 .py 文件,如:xx.py
#!/usr/bin/env python
#coding:utf-8
from django import template
from django.utils.safestring import mark_safe register = template.Library() @register.filter
def my_simple_time(v1,v2):
return v1 + v2 @register.filter
def my_input(id,arg):
result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
return mark_safe(result) -
在使用自定义filter的html文件顶部导入之前创建的 xx.py 文件名
{% load xx %}
- 使用filter
{{ 参数1|函数名:"参数二,参数三" }} {{ 参数1|函数名:数字 }}
-
在settings中配置当前app,不然django无法找到自定义的simple_tag
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
)
filter和simple_tag的优缺点:
- simple_tag不能作为if条件;但可使用任意参数;
- filter可以作为if条件;但最多两个参数,不能加空格;
4. 分页
4.1 django内置分页
Paginator
4.2 自定义分页
分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该在数据库表中的起始位置。
4.2.1 准备基础数据
1. 在url.py增加对应路径
from django.conf.urls import url,include
from django.contrib import admin
from cmdb import views urlpatterns = [
url(r'^index', views.index),
url(r'^userlist/', views.userlist),
]
2. 在views.py文件创建对应方法
from django.shortcuts import render,HttpResponse
LIST=[]
for i in range(109):
LIST.append(i)
def user_list(request):
return render(request,'userlist.html',{'li':LIST})
3. 在templates目录下的userlist.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for item in li %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>
运行程序并访问http://127.0.0.1:8000/userlist/,我们会发现该页面直接显示所有数据;
4.2.2 实现简单分页
我们可以考虑在url后带上参数表名用户访问的是第几页,从而返回数据;
views.py文件更改:
def userlist(request):
current_page = request.GET.get('p',1)
current_page = int(current_page)
start = (current_page-1)*10
end = current_page*10
data = LIST[start:end]
return render(request,'userlist.html',{'li':data})
重新运行程序后访问http://127.0.0.1:8000/userlist/?p=2,我们就可以看到第二页的数据了
4.2.3 简单优化
上面虽然可以实现简单分页,但是考虑到用户体验,肯定不能让用户如此访问,我们需要简单优化下:
html文件更改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for item in li %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<div>
<a href="user_list/?p=1">1</a>
<a href="user_list/?p=2">2</a>
<a href="user_list/?p=3">3</a>
</div>
</body>
</html>
重新运行程序后访问http://127.0.0.1:8000/userlist/,点击页面的1、2即可实现简单翻页功能;
4.2.4 最终分页实现
现在新的分页需求下来了:
1)每页10条;
2)停留在第一页的时候,上一页不可点击;停留在末页的时候,下一页不可点击;
3)当前页面被默认选中且颜色高亮显示;
4)只显示当前页、前3个页码和后3个页码;
头疼Ing,继续苦逼的写代码吧!
app的同级目录下新建utils目录,目录下新建pagination.py文件,文件内容如下:
from django.utils.safestring import mark_safe
class Page:
def __init__(self,current_page,data_count,per_page_count=10,pager_num=7):
self.current_page = current_page
self.data_count = data_count
self.per_page_count = per_page_count
self.pager_num = pager_num
@property
def start(self):
return (self.current_page-1) * self.per_page_count
@property
def end(self):
return self.current_page * self.per_page_count
@property
def total_count(self):
v,y = divmod(self.data_count, self.per_page_count)
if y:
v += 1
return v
def page_str(self, base_url):
page_list = []
if self.total_count < self.pager_num:
start_index = 1
end_index = self.total_count + 1
else:
if self.current_page <= (self.pager_num+1)/2:
start_index = 1
end_index = self.pager_num + 1
else:
start_index = self.current_page - (self.pager_num-1)/2
end_index = self.current_page + (self.pager_num+1)/2
if (self.current_page + (self.pager_num-1)/2) > self.total_count:
end_index = self.total_count + 1
start_index = self.total_count - self.pager_num + 1
if self.current_page == 1:
prev = '<a class="page" href="javascript:void(0);">上一页</a>'
else:
prev = '<a class="page" href="%s?p=%s">上一页</a>' %(base_url,self.current_page-1,)
page_list.append(prev)
for i in range(int(start_index),int(end_index)):
if i ==self.current_page:
temp = '<a class="page active" href="%s?p=%s">%s</a>' %(base_url,i,i)
else:
temp = '<a class="page" href="%s?p=%s">%s</a>' %(base_url,i,i)
page_list.append(temp)
if self.current_page == self.total_count:
nex = '<a class="page" href="javascript:void(0);">下一页</a>'
else:
nex = '<a class="page" href="%s?p=%s">下一页</a>' %(base_url,self.current_page+1,)
page_list.append(nex)
jump = """
<input type='text' /><a onclick='jumpTo(this, "%s?p=");'>GO</a>
<script>
function jumpTo(ths,base){
var val = ths.previousSibling.value;
location.href = base + val;
}
</script>
""" %(base_url,)
page_list.append(jump)
page_str = mark_safe("".join(page_list))
return page_str
pagination.py
views文件内容如下:
from django.shortcuts import render,HttpResponse
from utils import pagination
LIST=[]
for i in range(109):
LIST.append(i)
def user_list(request):
current_page = request.GET.get('p',1)
current_page = int(current_page)
page_obj = pagination.Page(current_page,len(LIST))
data = LIST[page_obj.start:page_obj.end]
page_str = page_obj.page_str("/userlist/")
return render(request,'userlist.html',{'li':data,'page_str':page_str})
templates文件下的userlist.html文件内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pagination .page{
display: inline-block;
padding: 5px;
background-color:darkmagenta;
margin: 5px;
}
.pagination .page.active{
background-color: red;
color:white;
}
</style>
</head>
<body>
<ul>
{% for item in li %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<div class="pagination">
{{ page_str }}
</div>
</body>
</html>
userlist.html
运行程序,访问http://127.0.0.1:8000/user_list;
总结,分页时需要做三件事:
- 创建处理分页数据的类
- 根据分页数据获取数据
- 输出分页HTML,即:[上一页][1][2][3][4][5][下一页]
5. cookie
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
5.1 cookie机制
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。
而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
5.2 什么是cookie?
Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
注意:Cookie功能需要浏览器的支持。
如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。
5.3 获取cookie
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间
5.4 设置cookie
rep = HttpResponse(...) 或 rep = render(request, ...) rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
参数:
key, 键
value='', 值
max_age=None, 超时时间
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=False, https传输
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。
<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });
5.5 基于cookie实现用户登录
主程序urls文件:
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^login/', views.login),
url(r'^index/', views.index),
]
app目录下的views文件:
from django.shortcuts import render, HttpResponse,redirect
user_info = {
'cc': {'pwd': ""},
'root': {'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')
dic = user_info.get(u)
if not dic:
return render(request,'login.html')
if dic['pwd'] == p:
res = redirect('/index/')
res.set_cookie('uname',u)
res.set_cookie('user_type',"asdfjalskdjf",httponly=True)
return res
else:
return render(request,'login.html')
def index(reqeust):
# 获取当前已经登录的用户
v = reqeust.COOKIES.get('uname')
if not v:
return redirect('/login/')
return render(reqeust,'index.html',{'current_user': v})
views
app目录下的templates下新建login.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/login/" method="POST">
<input type="text" name="username" placeholder="用户名" />
<input type="password" name="pwd" placeholder="密码" />
<input type="submit" />
</form>
</body>
</html>
login.html
新建index文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>欢迎登录:{{ current_user }}</h1>
</body>
</html>
index.html
运行程序并访问http://127.0.0.1:8000/login,输入用户名cc,密码123123,显示欢迎页面
5.6 基于cookie实现定制显示数据条数
我们还是在上面实现最终分页的代码基础上修改;
html文件修改如下:(在这之前需要下载jquery-1.12.4.min.js和jquery.cookie.js,并放在static的js目录下)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pagination .page{
display: inline-block;
padding: 5px;
background-color:darkmagenta;
margin: 5px;
}
.pagination .page.active{
background-color: red;
color:white;
}
</style>
</head>
<body>
<ul>
{% for item in li %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<div class="pagination">
<select id="page_size" onchange="changePageSize(this)">
<option value="">5</option>
<option value="">10</option>
<option value="">20</option>
</select>
{{ page_str }}
</div>
<script src="../static/js/jquery-1.12.4.min.js"></script>
<script src="../static/js/jquery.cookie.js"></script>
<script>
$(function(){
var v = $.cookie('per_page_count')
$("#page_size").val(v)
});
function changePageSize(ths) {
var v = $(ths).val();
$.cookie('per_page_count',v)
location.reload();
};
</script>
</body>
</html>
userlist.html
views文件内容修改如下:
from django.shortcuts import render,HttpResponse
from utils import pagination
LIST=[]
for i in range(109):
LIST.append(i)
def user_list(request):
current_page = request.GET.get('p',1)
current_page = int(current_page)
page_count = request.COOKIES.get("per_page_count")
page_count = int(page_count)
page_obj = pagination.Page(current_page,len(LIST),page_count)
data = LIST[page_obj.start:page_obj.end]
page_str = page_obj.page_str("/userlist/")
return render(request,'userlist.html',{'li':data,'page_str':page_str})
运行程序,访问http://127.0.0.1:8000/user_list;页面最下方选择每页要显示的条数即可实现数据显示定制~!
5.7 CBV和FBV用户认证装饰器
FBV:
views文件
def auth(func):
def inner(reqeust,*args,**kwargs):
v = reqeust.COOKIES.get('uname')
if not v:
return redirect('/login/')
return func(reqeust, *args,**kwargs)
return inner @auth
def index(reqeust):
# 获取当前已经登录的用户
v = reqeust.COOKIES.get('uname')
return render(reqeust,'index.html',{'current_user': v})
CBV:
主程序urls文件:
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^login/', views.login),
url(r'^index/', views.index),
url(r'^order/', views.Order.as_view()),
]
app下的views文件:
from django.shortcuts import render, HttpResponse,redirect
from django import views
from django.utils.decorators import method_decorator @method_decorator(auth,name='dispatch')
class Order(views.View):
@method_decorator(auth)
def dispatch(self, request, *args, **kwargs):
return super(Order,self).dispatch(request, *args, **kwargs)
@method_decorator(auth)
def get(self,reqeust):
v = reqeust.COOKIES.get('uname')
return render(reqeust,'index.html',{'current_user': v})
@method_decorator(auth)
def post(self,reqeust):
v = reqeust.COOKIES.get('uname')
return render(reqeust,'index.html',{'current_user': v})
6. session
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
- 数据库(默认)
- 缓存
- 文件
- 缓存+数据库
- 加密cookie
6.1 数据库Session
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) b. 使用 def index(request):
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1'] # 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems() # 用户session的随机字符串
request.session.session_key # 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否
request.session.exists("session_key") # 删除当前用户的所有Session数据
request.session.delete("session_key") request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
6.2 缓存Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 b. 使用 同上
6.3 文件Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 b. 使用 同上
6.4 缓存+数据库Session
数据库用于做持久化,缓存用于提高效率 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 b. 使用 同上
6.5 加密cookie Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 b. 使用 同上
6.6 实例
本实例为简单的登录+主机管理程序,代码如下:
1)urls.py代码如下:
from django.conf.urls import url
from django.conf.urls import include
from host import views
urlpatterns=[
url(r'^login/',views.login),
url(r'^index/',views.Index),
url(r'^logout/',views.logout),
url(r'^business/',views.business),
url(r'^hostinfo/', views.Host_Info),
url(r'^del_host/',views.Del_Host),
url(r'^add_host/',views.Add_Host),
url(r'^edit_host-(?P<eid>\d+)/', views.Edit_Host),
url(r'^detail_host-(?P<did>\d+)/', views.Detail_Host),
]
urls.py
2)views.py代码如下:
from django.shortcuts import render
from django.shortcuts import redirect,HttpResponse
import json,time
from utils import pagination
from host import models
from django import forms
from django.forms import widgets
from django.forms import fields
def auth(func):
def inner(request,*args,**kwargs):
try:
v = request.session['username']
if not v:
return redirect('../login/')
except Exception:
return redirect('../login/')
return func(request, *args, **kwargs)
return inner
class login_fm(forms.Form):
username = fields.CharField(
error_messages={'required': '用户名不能为空'},
widget=widgets.TextInput(attrs={'class': 'input input-big input-right','placeholder':'请输入登录账号'})
)
pwd = fields.CharField(
error_messages={'required': '密码不能为空'},
widget=widgets.TextInput(attrs={'class': 'input input-big input-right','placeholder':'请输入密码'})
)
def login(request):
if request.method == "GET":
obj = login_fm()
return render(request,'login.html',{'obj':obj})
elif request.method == "POST":
ret = {'status': True, 'error': None, 'data': None}
obj = login_fm(request.POST)
try:
u = request.POST.get('username')
p = request.POST.get('pwd')
if len(u)>0:
user_info = models.UserInfo.objects.filter(username=u)
if user_info:
for userinfo in user_info:
user_pwd = userinfo.password
if len(p) == 0:
ret['status'] = False
ret['error'] = obj.errors['pwd'][0]
else:
if p == user_pwd:
ret['status'] = True
request.session['username'] = u
request.session['is_login'] = True
else:
ret['status'] = False
ret['error'] = "用户名或者密码不正确,请重新输入!"
else:
ret['status'] = False
ret['error'] = "用户名不存在,请重新输入!"
elif len(u)== 0:
ret['status'] = False
ret['error'] = obj.errors['username'][0]
except Exception as e:
ret['status'] = False
ret['error'] = "请求错误!"
return HttpResponse(json.dumps(ret))
def logout(request):
request.session.clear()
return redirect('../login/')
@auth
def Index(request):
v = request.session['username']
return render(request,'index.html',{'current_user': v})
@auth
def business(request):
business_info = models.Business.objects.all()
return render(request, 'business.html',{'business_info':business_info})
views.py
3)static下文件省略
4)templates下文件:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="renderer" content="webkit">
<title>登录</title>
<link rel="stylesheet" href="../static/css/pintuer.css">
<link rel="stylesheet" href="../static/css/admin.css">
<link rel="stylesheet" href="../static/css/background.css">
</head>
<body>
<div class="bg"></div>
<div class="container">
<div class="line bouncein">
<div class="xs6 xm4 xs3-move xm4-move">
<div style="height:150px;"></div>
<div class="media media-y margin-big-bottom"></div>
<form id="login_form">
<div class="panel loginbox">
<div class="text-center margin-big padding-big-top"><h1>后台管理中心</h1></div>
<div class="panel-body" style="padding:30px; padding-bottom:10px; padding-top:10px;">
{% csrf_token %}
<div class="form-group">
<div class="field field-icon-right">
{{ obj.username }}
{# <input type="text" class="input input-big" name="username" placeholder="登录账号" data-validate="required:请填写账号" />#}
<span class="icon icon-user margin-small"></span>
</div>
</div>
<div class="form-group">
<div class="field field-icon-right">
{{ obj.pwd }}
<span class="icon icon-key margin-small"></span>
</div>
</div>
<div class="form-group">
<span class="error_mg" id="error_msg">{{ error_msg}}</span>
</div>
</div>
<div style="padding:30px;"><input type="button" id="login_button" class="button button-block bg-main text-big input-big" value="登录"></div>
</div>
</form>
</div>
</div>
</div>
<script src="../static/js/jquery-1.12.4.min.js"></script>
<script src="../static/js/pintuer.js"></script>
<script src="../static/js/jquery.cookie.js"></script>
<script>
$(function () {
$("#id_username").removeAttr('required');
$("#id_pwd").removeAttr('required');
$.ajaxSetup({
beforeSend:function (xhr,settings){
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'));
}
});
$('#login_button').click(function () {
$.ajax({
url:"../login/",
type:"POST",
data:$('#login_form').serialize(),
success:function (data) {
var obj=JSON.parse(data);
if (obj.status){
location.href='../index/';
}else{
$('#error_msg').text(obj.error);
}
}
})
})
})
</script>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta name="renderer" content="webkit">
<title>后台管理中心</title>
<link rel="stylesheet" href="../static/css/pintuer.css">
<link rel="stylesheet" href="../static/css/admin.css">
</head>
<body style="background-color:#f2f9fd;">
<div class="header bg-main">
<div class="logo margin-big-left fadein-top">
<h1><img src="../static/images/y.jpg" class="radius-circle rotate-hover" height="" alt=""/>后台管理中心</h1>
</div>
<div class="head-l">
<a class="button button-little bg-green" href="../index/" >
<span class="icon-home"></span>
前台首页
</a>
<a class="button button-little bg-red" href="../logout/">
<span class="icon-power-off"></span>
退出登录
</a>
</div>
<div class="head-2">欢迎{{ current_user }}~!</div>
</div>
<ul class="bread">
<li><a href="#" target="right" class="icon-home"> 首页</a></li>
<li><a href="##" id="a_leader_txt">网站信息</a></li>
<li><b>当前语言:</b><span style="color:red;">中文</span>
切换语言:<a href="##">中文</a> <a href="##">英文</a></li>
</ul>
<div class="admin">
<iframe scrolling="auto" rameborder="" src="/business/" name="right" width="100%" height="100%"></iframe>
</div>
<div class="leftnav">
<div class="leftnav-title"><strong><span class="icon-list"></span>菜单列表</strong></div>
<h2><span class="icon-user"></span>模态对话框</h2>
<ul style="display:block">
<li><a href="/hostinfo" target="right"><span class="icon-caret-right"></span>主机管理</a></li>
</ul>
</div>
<script type="text/javascript">
$(function () {
$(".leftnav h2").click(function () {
$(this).next().slideToggle(200);
$(this).toggleClass("on");
})
$(".leftnav ul li a").click(function () {
$("#a_leader_txt").text($(this).text());
$(".leftnav ul li a").removeClass("on");
$(this).addClass("on");
})
});
</script>
<script src="../static/js/jquery-1.12.4.min.js"></script>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>后台管理系统模版</title>
<link href="../static/css/bootstrap.min.css" rel="stylesheet"/>
<link href="../static/css/background.css" rel="stylesheet"/>
</head>
<body>
<div class="tables_header">
<h1>您可在此查看业务部门信息,请谨慎操作,谢谢合作~!</h1>
</div>
<table class="navbar_table">
<thead>
<tr>
<th class="navbar_table_th" id="navbar_table_th">id</th>
<th class="navbar_table_th" id="navbar_table_th">部门名称</th>
</tr>
</thead>
<tbody class="navbar_table_tbody">
{% for row in business_info %}
<tr>
<td class="navbar_table_td" id="navbar_table_td">{{ row.id }} </td>
<td class="navbar_table_td" id="navbar_table_td">{{ row.caption }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
business.html