Django请求生命周期

首先:对于所有的web框架来说本质就是一个socket服务端,浏览器是socket客户端

Python之路【第十六篇续】Django进阶篇_html

路由系统

在Django的urls中我们可以根据一个URL对应一个函数名来定义路由规则如下:



Python之路【第十六篇续】Django进阶篇_中间件_02

from cmdb import views
urlpatterns = [
url(r'^login/$', views.login),
url(r'^index/$', views.index),
url(r'^lists/$', views.lists),
url(r'^add/$', views.add),
]


Python之路【第十六篇续】Django进阶篇_django_03


2、默认URL

上面一个URL对应一个函数!我们可以在整个的url最下面里设置一个默认的URL,当用户访问我们的网站的时候没有指定详细的URL的时候我们默认让他们跳转到一个URL。



Python之路【第十六篇续】Django进阶篇_django_04

urlpatterns = [
url(r'^login/$', views.login),
url(r'^index/$', views.index),
url(r'^lists/$', views.lists),
url(r'^add/$', views.add),
url(r'^$', views.login),
]


Python之路【第十六篇续】Django进阶篇_django_05


这里需要注意下:当客户访问过来请求的时候,到达路由系统后是根据正则来匹配的,如果上面的匹配成功了,后面的路由规则将不会继续匹配,需要注意!!!!所以我们在后面都加一个$来做结尾

3、动态URL

3.1、动态URL传参

咱们看下园的分页连接如下图:

Python之路【第十六篇续】Django进阶篇_ajax_06

问:如果有这么多的RUL难道我们都要给他写一个路由规则吗?当然不是,会累死的,那他是怎么实现的呢?在上面的默认的URL中我们就说过他的路由功能是支持“正则表达式”的!

所以我们可以这么写:



url(r'^user_list/(\d+)$', views.user_list),


views.user_list



def user_list(request,chose_id):
return HttpResponse(chose_id)


这里当用户点击的时候login后的数字,会自动的传给views.user_list作为参数,因为这个是Django调用的,不是咱们调用的。

他这里会做两步操作:

1、获取user_list后面的这个值

2、运行views.user_list这个函数,并把获取的值自动传给views.user_list作为参数他的参数

3.2、动态URL传多个参数

问:我是否可以传多个参数?

可以传多个参数它是已/来分割的。



url(r'^user_list/(\d+)/(\d+)$', views.user_list),


views.user_list



def user_list(request,chose_id,chose_id2):
return HttpResponse(chose_id+chose_id2)


输入:http://127.0.0.1:8000/user_list/8/10  效果就是:810

他的顺序是:正序的,你先给他传那个值,第一个参数就是那个

3.3、动态URL传参数以Key:value的形式

通过正则表达式的分组来做!



url(r'^user_list/(?P<v1>\d+)/(?P<V2>\d+)$', views.user_list),


这里?p<v1>这里的v1就是key,vlaue就是传进去的值,



def user_list(request,v2,v1):
print v2 , v1
return HttpResponse(v1+v2)


这样我们就不必按照顺序去取了,可以通过key,value的方式来取传进来的值

4、URL中专(分级匹配)

在实际的生产环境中有这么一种情况:在一个project下面有很多APP,那么我们的路由规则只能写在一个文件里吗?

Python之路【第十六篇续】Django进阶篇_ajax_07

当然不是,我们可以通过下面的方式来把他分开:



url(r'^app01/', include("app01.urls")),


然后在app01内创建一个文件urls,不要忘记注册app。然后在访问app01里的url的时候通过:hostip:port/app01/index  or  hostip:port/app01/login

5、基于反射实现动态路由设计

有很多的WEB框架,他和Django不太一样。比如mvc他会将所有的URL做成一个分类的形式。在Django中咱们一般是一个url对应一个函数。

但是在其他的WEB框架中他们也把url也进行用正则处理了。比如下面:



Python之路【第十六篇续】Django进阶篇_中间件_08

url(r'^(?P<controller>\w+)/(?P<action>\w+)', mp),
#咱们给他做个定义mp中第一个是文件比如
#home.py 第二个参数是文件中的函数 def index
#
#/home/index/
#/login/index/
#/update/index/


Python之路【第十六篇续】Django进阶篇_html_09


但是上面的方法仅仅是通过反射来实现的,通过文件找到里面的函数然后执行!

但是在Django中不建议使用此方法。因为不同的WEB框架建议你使用不同的方式,Django就不建议使用反射

中间件

中间件定义:

  中间件是一个、一个的管道,如果相对任何所有的通过Django的请求进行管理都需要自定义中间件

  中间件可以对进来的请求和出去的请求进行控制

  中间件是一类。

Python之路【第十六篇续】Django进阶篇_html

看下面的代码在settings里中间件的类:



Python之路【第十六篇续】Django进阶篇_django_11

MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


Python之路【第十六篇续】Django进阶篇_html_12


当有请求过来的时候,默认从上倒下执行!然后在返回的时候从下面在返回回去,如下图:

2、自定义中间件

中间件中可以定义四个方法,分别是:

  • process_request(self,request)
  • process_view(self, request, callback, callback_args, callback_kwargs)
  • process_exception(self, request, exception)
  • process_response(self, request, response)

process_exception 这个方法只有在出现错误的时候才会触发

先写一个自定义中间件,然后在看他的原理和源码:

2.1、自定义中间件

\



Python之路【第十六篇续】Django进阶篇_ajax_13

class Testmiddle(object):
def process_request(self,request):
print 'Testmiddle process_request'
def process_view(self, request, callback, callback_args, callback_kwargs):
print 'Testmiddle process_view'
def process_exception(self, request, exception):
pass
def process_response(self, request, response):
print 'Testmiddle process_response'
return response

class Nextmiddle(object):
def process_request(self,request):
print 'Nextmiddle process_request'
def process_view(self, request, callback, callback_args, callback_kwargs):
print 'Nextmiddle process_view'
def process_exception(self, request, exception):
pass
def process_response(self, request, response):
print 'Nextmiddle process_response'
return response


Python之路【第十六篇续】Django进阶篇_django_14


2.2、注册中间件



Python之路【第十六篇续】Django进阶篇_django_15

MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'middleware.middle.Testmiddle',
'middleware.middle.Nextmiddle',
]


Python之路【第十六篇续】Django进阶篇_数据_16


2.3、测试使用url和views



from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/$', views.index),

]



def index(request):
print 'This app01 Views.index'
return HttpResponse('OK')


2.4、查看输出结果:



Python之路【第十六篇续】Django进阶篇_html_17

'''
Testmiddle process_request
Nextmiddle process_request
Testmiddle process_view
Nextmiddle process_view
This app01 Views.index
Nextmiddle process_response
Testmiddle process_response
'''


Python之路【第十六篇续】Django进阶篇_django_18


从输出结果可以看出:

他是先执行Testmiddle 的request 方法又执行了Nextmiddle的 process_request方法。。。。

2.5、原理:

当请求进来了到达中间件

去settings里面找到MIDDLEWARE_CLASSES,MIDDLEWARE_CLASSES是一个元组

有4个列表:



Python之路【第十六篇续】Django进阶篇_中间件_19

process_request_lsit = []
process_view_list = []
process_response_list = []
然后他循环MIDDLEWARE_CLASSES这个类:
for 类 in MIDDLEWARE_CLASSES:
  obj = 类()
  if obj里有process_request方法:
    process_request_lsit.append(obj.process_request)


Python之路【第十六篇续】Django进阶篇_中间件_20


然后循环后后执行:



for i in process_request_list:
  i() #加括号执行方法

for i in process_view_list:
i()
............


源码:


Python之路【第十六篇续】Django进阶篇_ajax_21Python之路【第十六篇续】Django进阶篇_html_22


def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE_CLASSES.

Must be called after the environment is fixed (see __call__ in subclasses).
"""
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []

request_middleware = []
for middleware_path in settings.MIDDLEWARE_CLASSES:
mw_class = import_string(middleware_path)
try:
mw_instance = mw_class()
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue

if hasattr(mw_instance, 'process_request'):
request_middleware.append(mw_instance.process_request)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.append(mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.insert(0, mw_instance.process_template_response)
if hasattr(mw_instance, 'process_response'):
self._response_middleware.insert(0, mw_instance.process_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.insert(0, mw_instance.process_exception)

# We only assign to this when initialization is complete as it is used
# as a flag for initialization being complete.
self._request_middleware = request_middleware

middleware

3、中间件的流程梳理

首先看下自定义的中间件中的process_response方法他是有返回值的其他的是没有返回值的。这个return response是什么呢?

这个response就是咱们自定义的views.index返回的结果!



def process_response(self, request, response):
print 'Testmiddle process_response'
return response


如果在其他的没有返回值得,仅有process_response有返回值得话他的请求流程是这样的:

Python之路【第十六篇续】Django进阶篇_django_23

但是如果在process_request或者process_view又返回值得话那么流程就完全不一样了!

举例:如果有m1和m2两个中间件,如果我在m1中的request方法中设置了,如果访问为1.1.1.1那么返回要一个404,那么他的访问流程是这样的:

Python之路【第十六篇续】Django进阶篇_html_24

 

process_exception  什么时候触发呢?咱们定义的views.index出错的时候他就会捕捉到然后执行咱们定义的process_exception方法如下图:

Python之路【第十六篇续】Django进阶篇_django_25

Django缓存

由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者Redis中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。

举个例子来说:如果访问量比较大的时候,有很多相同的操作比如:有时候请求的数据比如访问同一条数据,或者同一个页面的时候,其实是没必要的。

Django支持,mysql,Redis、Memecache、文件的方式做缓存,并且可以设置超时时间。

settings配置:



Python之路【第十六篇续】Django进阶篇_html_26

CACHES = {
'default': {
#定义已文件的方式进行cache
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
#cache文件的存放路径
'LOCATION': os.path.join(BASE_DIR, 'cache'),
#超时时间为600妙
'TIMEOUT': 600,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}


Python之路【第十六篇续】Django进阶篇_中间件_27


给请求应用,就是使用装饰器



Python之路【第十六篇续】Django进阶篇_ajax_28

from django.views.decorators.cache import cache_page

#这里设置的是 60秒 * 15 ,15分钟之后
@cache_page(60 * 15)
def cache_page(request):
current = str(time.time())
return HttpResponse(current)


Python之路【第十六篇续】Django进阶篇_django_29


Session&Cookie

Cookie就是一段字符串,保存于本机电脑上。

session 保存于服务器,用来保存用户的会话信息,依赖于Cookies

1、流程

举个例子,咱们在登录一个网站后,拿JD举例,如果我登录进去之后,在想点击订单的时候。server断怎么判断我是“我”,而不是其他人呢?

Http是短连接,那么Server端肯定有一个保存我登录状态的地方(session),那server怎么判断是我发送过来的请求呢?就是通过Cookie!

Python之路【第十六篇续】Django进阶篇_ajax_30

当客户端访问过来后,server端会在IE里生成一个Cookie,当访问过来的时候就可以通过Cookie进行判断

2、结构

1、自动生成一段字符串

2、将字符串发送到客户端的浏览器,同时把字符串当做key放在session里。(可以理解为session就是一个字典)

3、在用户的session对应的value里设置任意值

Python之路【第十六篇续】Django进阶篇_数据_31

Python之路【第十六篇续】Django进阶篇_数据_32

3、操作

3.1、操作session

  • 获取session:request.session[key]
  • 设置session:reqeust.session[key] = value
  • 删除session:del request[key]



request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。


实例:



Python之路【第十六篇续】Django进阶篇_django_33

def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'shuai' and password == '123':
result = request.session.get('IS_LOGIN', None)
print result
request.session['IS_LOGIN'] = True
return redirect('/index/')
obj = forms.LoginForm()
# 如果登录成功,写入session,跳转index
return render(request, 'account/login.html', {'model': obj})


def index(request):
'''
如果用户已经登录
'''
is_login = request.session.get('IS_LOGIN',False)
if is_login:
return render(request, 'home/index.html')
else:
return redirect('/login/')


Python之路【第十六篇续】Django进阶篇_html_34


注:这里需要注意在session中,我们可以设置多个key:value的值,方便我们做很多事情,比如判断哪个用户:

  如果用户登录后,那么他肯定有一个cookie那么他在访问购物车的时候,怎么判断是哪个用户呢?我们可以在session设置,当用户登录的时候,我们把的用户名,增加到session中,那么用户携带cookie访问的时候,我们就能判断是哪个一用来访问的!

比如下面的对应关系:

user1



cookie :aaaa


server session(举例格式)



{session:aaaa{'IS_LOGIN':'True',username:'shuaige'}}


 4、Session和Cookie好处

使用Session和Cookie的好处:Cookie可以理解为一个身份证ID,你只能拿着他去和Server端进行通信,如果你没有这个ID那么server端也不知道你是谁!

实例:(0 0 !)

我在写博客的时候在做Cookie和Session的实验,把Cookie删掉了!当我保存的时候直接给我提出来了,为什么呢?就是因为,server端不知道我是谁了,我已经没有密钥了。

 

所以,只要Session和Cookie任意一方失效,就可以理解为:

Cookie失效就相当于身份证ID过期,需要重新认证才可以继续使用。Session失效就相当于银行里的数据标识此ID无效,也需要重新申请。

Django Form表单

在实际的生产环境中比如登录和验证的时候,我们一般都使用Jquery+ajax来判断用户的输入是否为空,假如JS被禁用的话,咱们这个认证屏障是不是就消失了呢?(虽然一般不会禁用掉但是还是存在风险)

所以我们一般做两种认证一种是前端做一遍认证,在后端做一遍认证

首先咱们看一下下面的案例:



Python之路【第十六篇续】Django进阶篇_ajax_35

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render

# Create your views here.


def user_list(request):
host = request.POST.get('host')
port = request.POST.get('port')
mail = request.POST.get('mail')
mobile = request.POST.get('mobile')
#这里有个问题,如果,这个from表单有20个input,你在这里是不是的取20次?

#验证:
#输入不能为空,并且有的可以为空有的不可以为空
#如果email = 11123123 这样合法吗?
#如果mobile = 11123123 这样合法吗?
#如果ip = 11123123 这样合法吗?
'''
你在这里是不是需要做一大堆的输入验证啊?并且有很多这种页面会存在这种情况,如果每个函数都这样做估计就累死了
'''
return render(request,'user_list.html')


Python之路【第十六篇续】Django进阶篇_数据_36


在样能解决这个问题呢?通过Django的form来实现,其他语言也有叫做(模型绑定)

Django的form的作用:

1、生成html标签

2、用来做用户提交的验证

1、生成html标签

views

 



Python之路【第十六篇续】Django进阶篇_中间件_37

from django import forms

class UserInfo(forms.Form):
email = forms.EmailField(required=False) #required是否可以为空,如果为False说明可以为空
host = forms.CharField() #如果required不写默认为Ture
port = forms.CharField()
mobile = forms.CharField()

def user_list(request):
obj = UserInfo() #创建了这个对象
return render(request,'user_list.html',{'obj':obj})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_数据_38


 

html调用



Python之路【第十六篇续】Django进阶篇_django_39

<form action="/user_list/" method="post">
<p>主机:{{ obj.host }}</p>
<p>端口:{{ obj.port }}</p>
<p>邮箱:{{ obj.email }}</p>
<p>手机:{{ obj.mobile }}</p>
<input type="submit" value="submit"/>
</form>


Python之路【第十六篇续】Django进阶篇_django_40


把我们的对象穿进去,html在引用的时候直接obj.host就可以自动生成html标签,然后看下html显示:

Python之路【第十六篇续】Django进阶篇_数据_41

2、简单的form表单验证用户输入的内容



Python之路【第十六篇续】Django进阶篇_ajax_42

def user_list(request):
obj = UserInfo() #创建了这个对象
if request.method == 'POST':
#获取用户输入一句话就搞定
user_input_obj = UserInfo(request.POST)
'''
咱们把post过来的数据当参数传给UserInfo咱们定义的这个类,UserInfo会自动会去你提交的数据
email/host/port/mobile 自动的封装到user_input_obj里,封装到这个对象里我们就可以判断输入是否合法
'''
print user_input_obj.is_valid() #

return render(request,'user_list.html',{'obj':obj})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_中间件_43


当我们输入不合法的时候,(在创建类设置的需求)为空、或者不是email格式的时候!

Python之路【第十六篇续】Django进阶篇_django_44

这样在后端我们是不是就有一套验证的机制?就可以通过is_valid()来判断用户输入是否合法!如果不合法就把返回信息发送过去,如果合法获取数据操作即可!

捕获错误信息并返回

 



Python之路【第十六篇续】Django进阶篇_ajax_45

from django import forms

class UserInfo(forms.Form):
email = forms.EmailField(required=True) #required是否可以为空,如果为False说明可以为空
host = forms.CharField() #如果required不写默认为Ture
port = forms.CharField()
mobile = forms.CharField()

def user_list(request):
obj = UserInfo() #创建了这个对象
if request.method == 'POST':
#获取用户输入一句话就搞定
user_input_obj = UserInfo(request.POST)
'''
咱们把post过来的数据当参数传给UserInfo咱们定义的这个类,UserInfo会自动会去你提交的数据
email/host/port/mobile 自动的封装到user_input_obj里,封装到这个对象里我们就可以判断输入是否合法
'''
if user_input_obj.is_valid(): #判断用户输入是否合法
data = user_input_obj.clean() #获取用户输入
print data
else:
#如果发生错误,捕捉错误
error_msg = user_input_obj.errors
print error_msg #打印一下然后看下他的类型
'''
<ul class="errorlist">
<li>mobile<ul class="errorlist"><li>This field is required.
</li></ul></li>
<li>host<ul class="errorlist"><li>This field is required.</li></ul></li>
<li>port<ul class="errorlist"><li>This field is required.</li></ul></li>
</ul>
'''
#然后把错误信息返回
return render(request,'user_list.html',{'obj':obj,'errors':error_msg,})#然后把对象传给html,在把错误信息传递过去
return render(request,'user_list.html',{'obj':obj,})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_html_46


 

html标签,使用error输出



Python之路【第十六篇续】Django进阶篇_html_47

<form action="/user_list/" method="post">
<p>主机:{{ obj.host }}<span>{{ errors.host }}</span></p>
<p>端口:{{ obj.port }}<span>{{ errors.port }}</span></p>
<p>邮箱:{{ obj.email }}<span>{{ errors.email }}</span></p>
<p>手机:{{ obj.mobile }}<span>{{ errors.mobile }}</span></p>
<input type="submit" value="submit"/>
</form>


Python之路【第十六篇续】Django进阶篇_中间件_48


现在在去点击下看下效果:

Python之路【第十六篇续】Django进阶篇_django_49

这样如果,我都按照要求提交,就可以取到数据了?这样咱们就不用自己去拿数据是,NICE,NICE~~



{'mobile': u'123456789', 'host': u'1.1.1.1', 'email': u'shuaige@qq.com', 'port': u'8000'}


3、form表单定制化

3.1、自定义报错内容

  在form里有一个参数:error_messages  在他这里就可以定义报错内容



class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})
mobile = forms.CharField(error_messages={'required':u'手机不能为空'})


效果:

Python之路【第十六篇续】Django进阶篇_ajax_50

3.2、我想给form表单添加一个属性



Python之路【第十六篇续】Django进阶篇_中间件_51

class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})
mobile = forms.CharField(error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)


Python之路【第十六篇续】Django进阶篇_django_52


看下效果:

Python之路【第十六篇续】Django进阶篇_中间件_53

3.3、在给他增加一个备注



Python之路【第十六篇续】Django进阶篇_html_54

class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})
mobile = forms.CharField(error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)
#咱们在新增一个备注
memo = forms.CharField(required=False,
widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备注'})

)


Python之路【第十六篇续】Django进阶篇_中间件_55


html代码



Python之路【第十六篇续】Django进阶篇_ajax_56

<form action="/user_list/" method="post">
<p>主机:{{ obj.host }}<span>{{ errors.host }}</span></p>
<p>端口:{{ obj.port }}<span>{{ errors.port }}</span></p>
<p>邮箱:{{ obj.email }}<span>{{ errors.email }}</span></p>
<p>手机:{{ obj.mobile }}<span>{{ errors.mobile }}</span></p>
<p>备注:{{ obj.memo }}<span>{{ errors.memo }}</span></p>

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


Python之路【第十六篇续】Django进阶篇_html_57


4、自定义正则表达式增加判断规则



Python之路【第十六篇续】Django进阶篇_ajax_58

import re
from django import forms
from django.core.exceptions import ValidationError


#自定义方法
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') #正则匹配
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误') #如果没有匹配到主动触发一个错误


class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})

#默认mobile里有一个默认为空的机制,我们在原有的参数里增加怎们自定义的方法
mobile = forms.CharField(validators=[mobile_validate,],#应用咱们自己定义的规则
error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)
#咱们在新增一个备注
memo = forms.CharField(required=False,
widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备注'})

)


Python之路【第十六篇续】Django进阶篇_数据_59


效果:

如果为空的话会提示,不能为空如果格式不对的话会提示:

Python之路【第十六篇续】Django进阶篇_ajax_60

5、生成select标签



Python之路【第十六篇续】Django进阶篇_django_61

class UserInfo(forms.Form):

user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),)

user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,attrs={'class':'form-control'}))

。。。。。。。。。


Python之路【第十六篇续】Django进阶篇_ajax_62


html内



Python之路【第十六篇续】Django进阶篇_中间件_63

<form action="/user_list/" method="post">
<p>用户类型:{{ obj.user_type }}<span>{{ errors.user_type }}</span></p>
<p>主机:{{ obj.host }}<span>{{ errors.host }}</span></p>
<p>端口:{{ obj.port }}<span>{{ errors.port }}</span></p>
<p>邮箱:{{ obj.email }}<span>{{ errors.email }}</span></p>
<p>手机:{{ obj.mobile }}<span>{{ errors.mobile }}</span></p>
<p>备注:{{ obj.memo }}<span>{{ errors.memo }}</span></p>

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


Python之路【第十六篇续】Django进阶篇_html_64


6、关于后端验证

这个后端验证是必须要有验证机制的,前端可以不写但是后端必须要写!前端的JS是可以被禁用掉到。

6、Django form漂亮的显示错误信息

设置显示error的样式



error_msg = user_input_obj.errors.as_data()#这里原来什么都没写,默认是ul的样式,默认是as_ul(),如果我们写成as_data()返回的就是一个原生的字符串
#还有一个as_json


实例:

 



Python之路【第十六篇续】Django进阶篇_django_65

import re
from django import forms
from django.core.exceptions import ValidationError

#自定义方法
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') #正则匹配
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误') #如果没有匹配到主动出发一个错误


class UserInfo(forms.Form):

user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),)

user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,attrs={'class':'form-control'}))

email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})

#默认mobile里有一个默认为空的机制,我们在原有的参数里增加怎们自定义的方法
mobile = forms.CharField(validators=[mobile_validate,],#应用咱们自己定义的规则
error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)
#咱们在新增一个备注
memo = forms.CharField(required=False,
widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备注'}))

def user_list(request):
obj = UserInfo() #创建了这个对象
if request.method == 'POST':
#获取用户输入一句话就搞定
user_input_obj = UserInfo(request.POST)

if user_input_obj.is_valid(): #判断用户输入是否合法
data = user_input_obj.clean() #获取用户输入
print data
else:
#如果发生错误,捕捉错误
error_msg = user_input_obj.errors.as_data()#这里原来什么都没写,默认是ul的样式,默认是as_ul(),如果我们写成as_data()返回的就是一个原生的字符串
#还有一个as_json

print error_msg #打印一下然后看下他的类型
#然后把错误信息返回
return render(request,'user_list.html',{'obj':obj,'errors':error_msg,})#然后把对象传给html,在把错误信息传递过去
return render(request,'user_list.html',{'obj':obj,})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_中间件_66


这里在html中如果不进行处理默认显示的是:

Python之路【第十六篇续】Django进阶篇_数据_67

看下他的实际是什么内容:



{'mobile': [ValidationError([u'\u624b\u673a\u4e0d\u80fd\u4e3a\u7a7a'])], 
'host': [ValidationError([u'\u4e3b\u673a\u4e0d\u80fd\u4e3a\u7a7a'])],
'email': [ValidationError([u'\u90ae\u7bb1\u4e0d\u80fd\u4e3a\u7a7a'])],
'port': [ValidationError([u'\u7aef\u53e3\u4e0d\u80fd\u4e3a\u7a7a'])]}


所以我们自定义一个模板语言对其进行修饰:

Python之路【第十六篇续】Django进阶篇_html_68

然后在html中调用



Python之路【第十六篇续】Django进阶篇_django_69

{% load cmdb_tag %}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shuai</title>
</head>
<body>
<form action="/user_list/" method="post">
<p>用户类型:{{ obj.user_type }}<span>{% error_message errors.user_type %}</span></p>
<p>主机:{{ obj.host }}<span>{% error_message errors.host %}</span></p>
<p>端口:{{ obj.port }}<span>{% error_message errors.port %}</span></p>
<p>邮箱:{{ obj.email }}<span>{% error_message errors.email %}</span></p>
<p>手机:{{ obj.mobile }}<span>{% error_message errors.mobile %}</span></p>
<p>备注:{{ obj.memo }}<span>{% error_message errors.memo %}</span></p>
<input type="submit" value="submit"/>
</form>
</body>
</html>


Python之路【第十六篇续】Django进阶篇_django_70


显示效果如下:

Python之路【第十六篇续】Django进阶篇_数据_71

因为模板语言不支持用索引的方式取值,所以我们通过自定义simp_tag来进行取值

Ajax

1、单条数据提交

在上面的原有例子中的html中新增下面html内容



Python之路【第十六篇续】Django进阶篇_ajax_72

<form action="/user_list/" method="post">
<input type="button" onclick="Ajaxsubmit();" value="提交"/>
<table>
<thead>
<tr>
<th>主机名</th>
<th>端口</th>
</tr>
</thead>
<tbody>
<tr>
<td>1.1.1.1</td>
<td>80000</td>
</tr>
<tr>
<td>1.1.1.1</td>
<td>80000</td>
</tr>
</tbody>
</table>
</form>
<script type="text/javascript" src="/static/jquery-2.2.1.min.js"></script>
<script>
function Ajaxsubmit(){
var host = '1.1.1.1';
var port = '8000';

$.ajax({
url:"/ajax_data/",
type:'POST',
data:{h:host,p:port},
success:function(arg){

}
})
}
</script>


Python之路【第十六篇续】Django进阶篇_django_73


注释:



Python之路【第十六篇续】Django进阶篇_ajax_74

$.ajax({
url:"/ajax_data/", #目标URL
type:'POST', #请求方式
data:{h:host,p:port}, 以h和p为key用户的输入为value:<QueryDict: {u'h': [u'1.1.1.1'], u'p': [u'8000']}>
success:function(arg){

}


Python之路【第十六篇续】Django进阶篇_ajax_75


增加URL和views



url(r'^ajax_data/', views.ajax_data),



def ajax_data(request):
print request.POST
return HttpResponse('OK')


2、ajax多条数据提交

在原来的基础上修改Jquery



Python之路【第十六篇续】Django进阶篇_数据_76

<script>
function Ajaxsubmit(){
var array_users = [
{'username':'shuaige','arg':18},
{'username':'tianshuai','arg':18},
{'username':'shuai','arg':18},

];
$.ajax({
url:"/ajax_mdata/",
type:'POST',
data:{data:array_users},
success:function(arg){

}
})
}
</script>


Python之路【第十六篇续】Django进阶篇_数据_77


添加urls&views



url(r'^ajax_mdata/$', views.ajax_mdata),


views



def ajax_mdata(request):
print request.POST
return HttpResponse('OK')


点击提交看下(在server端打印看下):



<QueryDict: {u'data[1][username]': [u'tianshuai'], u'data[0][username]': [u'shuaige'], u'data[0][arg]': [u'18'], u'data[1][arg]': [u'18'], u'data[2][username]': [u'shuai'], u'data[2][arg]': [u'18']}>


上面的结果数据是有问题的!他给咱们做了个加工,咱们没给他传data[1],data[0]了吗?

所以咱们的在ajax增加参数



Python之路【第十六篇续】Django进阶篇_中间件_78

<script>
function Ajaxsubmit(){
var array_users = [
{'username':'shuaige','arg':18},
{'username':'tianshuai','arg':18},
{'username':'shuai','arg':18},

];
$.ajax({
url:"/ajax_mdata/",
type:'POST',
tradition: true,
data:{data:JSON.stringify(array_users)},
success:function(arg){

}
})
}
</script>


Python之路【第十六篇续】Django进阶篇_django_79


增加了两项:



#以原生的模式传过去
tradition: true,

#把数组做一步处理转成字符串
data:{data:JSON.stringify(array_users)},


3、在一个Ajax请求之后,返回信息应该更职业化,不能单单发送一个字符串

看下面的就不像程序员:



def ajax_data(request):
print request.POST
return HttpResponse('OK')


应该这么写:(下面的例子先用json来做,不过还有一个json response)



Python之路【第十六篇续】Django进阶篇_django_80

import json

def ajax_data(request):
ret = {'status':True,'error':''}
try:
print request.POST
except Exception,e:
ret['status'] = False
ret['error'] = str(e)
#在上面如果他出错我就把他ret[status] = False
return HttpResponse(json.dumps(ret))


Python之路【第十六篇续】Django进阶篇_中间件_81


html的js也得修改下:



Python之路【第十六篇续】Django进阶篇_ajax_82

<script>
function Ajaxsubmit(){
var array_users = [
{'username':'shuaige','arg':18},
{'username':'tianshuai','arg':18},
{'username':'shuai','arg':18},

];
$.ajax({
url:"/ajax_mdata/",
type:'POST',
tradition: true,
data:{data:JSON.stringify(array_users)},
success:function(arg){
var callback_dict = $.parseJSON(arg);//这里把字符串转换为对象
//然后咱们就可以判断
if(callback_dict){//执行成功了
//简单测试
alert('提交成功')
}else{//如果为False执行失败了
alert(callback_dict.error)
}

}
})
}
</script>


Python之路【第十六篇续】Django进阶篇_html_83


参考链接:本人骚师:http://www.cnblogs.com/wupeiqi/articles/5237704.html 



Django请求生命周期

首先:对于所有的web框架来说本质就是一个socket服务端,浏览器是socket客户端

Python之路【第十六篇续】Django进阶篇_html

路由系统

在Django的urls中我们可以根据一个URL对应一个函数名来定义路由规则如下:



Python之路【第十六篇续】Django进阶篇_ajax_85

from cmdb import views
urlpatterns = [
url(r'^login/$', views.login),
url(r'^index/$', views.index),
url(r'^lists/$', views.lists),
url(r'^add/$', views.add),
]


Python之路【第十六篇续】Django进阶篇_django_86


2、默认URL

上面一个URL对应一个函数!我们可以在整个的url最下面里设置一个默认的URL,当用户访问我们的网站的时候没有指定详细的URL的时候我们默认让他们跳转到一个URL。



Python之路【第十六篇续】Django进阶篇_ajax_87

urlpatterns = [
url(r'^login/$', views.login),
url(r'^index/$', views.index),
url(r'^lists/$', views.lists),
url(r'^add/$', views.add),
url(r'^$', views.login),
]


Python之路【第十六篇续】Django进阶篇_html_88


这里需要注意下:当客户访问过来请求的时候,到达路由系统后是根据正则来匹配的,如果上面的匹配成功了,后面的路由规则将不会继续匹配,需要注意!!!!所以我们在后面都加一个$来做结尾

3、动态URL

3.1、动态URL传参

咱们看下园的分页连接如下图:

Python之路【第十六篇续】Django进阶篇_ajax_06

问:如果有这么多的RUL难道我们都要给他写一个路由规则吗?当然不是,会累死的,那他是怎么实现的呢?在上面的默认的URL中我们就说过他的路由功能是支持“正则表达式”的!

所以我们可以这么写:



url(r'^user_list/(\d+)$', views.user_list),


views.user_list



def user_list(request,chose_id):
return HttpResponse(chose_id)


这里当用户点击的时候login后的数字,会自动的传给views.user_list作为参数,因为这个是Django调用的,不是咱们调用的。

他这里会做两步操作:

1、获取user_list后面的这个值

2、运行views.user_list这个函数,并把获取的值自动传给views.user_list作为参数他的参数

3.2、动态URL传多个参数

问:我是否可以传多个参数?

可以传多个参数它是已/来分割的。



url(r'^user_list/(\d+)/(\d+)$', views.user_list),


views.user_list



def user_list(request,chose_id,chose_id2):
return HttpResponse(chose_id+chose_id2)


输入:http://127.0.0.1:8000/user_list/8/10  效果就是:810

他的顺序是:正序的,你先给他传那个值,第一个参数就是那个

3.3、动态URL传参数以Key:value的形式

通过正则表达式的分组来做!



url(r'^user_list/(?P<v1>\d+)/(?P<V2>\d+)$', views.user_list),


这里?p<v1>这里的v1就是key,vlaue就是传进去的值,



def user_list(request,v2,v1):
print v2 , v1
return HttpResponse(v1+v2)


这样我们就不必按照顺序去取了,可以通过key,value的方式来取传进来的值

4、URL中专(分级匹配)

在实际的生产环境中有这么一种情况:在一个project下面有很多APP,那么我们的路由规则只能写在一个文件里吗?

Python之路【第十六篇续】Django进阶篇_ajax_07

当然不是,我们可以通过下面的方式来把他分开:



url(r'^app01/', include("app01.urls")),


然后在app01内创建一个文件urls,不要忘记注册app。然后在访问app01里的url的时候通过:hostip:port/app01/index  or  hostip:port/app01/login

5、基于反射实现动态路由设计

有很多的WEB框架,他和Django不太一样。比如mvc他会将所有的URL做成一个分类的形式。在Django中咱们一般是一个url对应一个函数。

但是在其他的WEB框架中他们也把url也进行用正则处理了。比如下面:



Python之路【第十六篇续】Django进阶篇_html_91

url(r'^(?P<controller>\w+)/(?P<action>\w+)', mp),
#咱们给他做个定义mp中第一个是文件比如
#home.py 第二个参数是文件中的函数 def index
#
#/home/index/
#/login/index/
#/update/index/


Python之路【第十六篇续】Django进阶篇_html_92


但是上面的方法仅仅是通过反射来实现的,通过文件找到里面的函数然后执行!

但是在Django中不建议使用此方法。因为不同的WEB框架建议你使用不同的方式,Django就不建议使用反射

中间件

中间件定义:

  中间件是一个、一个的管道,如果相对任何所有的通过Django的请求进行管理都需要自定义中间件

  中间件可以对进来的请求和出去的请求进行控制

  中间件是一类。

Python之路【第十六篇续】Django进阶篇_html

看下面的代码在settings里中间件的类:



Python之路【第十六篇续】Django进阶篇_ajax_94

MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


Python之路【第十六篇续】Django进阶篇_中间件_95


当有请求过来的时候,默认从上倒下执行!然后在返回的时候从下面在返回回去,如下图:

2、自定义中间件

中间件中可以定义四个方法,分别是:

  • process_request(self,request)
  • process_view(self, request, callback, callback_args, callback_kwargs)
  • process_exception(self, request, exception)
  • process_response(self, request, response)

process_exception 这个方法只有在出现错误的时候才会触发

先写一个自定义中间件,然后在看他的原理和源码:

2.1、自定义中间件

\



Python之路【第十六篇续】Django进阶篇_html_96

class Testmiddle(object):
def process_request(self,request):
print 'Testmiddle process_request'
def process_view(self, request, callback, callback_args, callback_kwargs):
print 'Testmiddle process_view'
def process_exception(self, request, exception):
pass
def process_response(self, request, response):
print 'Testmiddle process_response'
return response

class Nextmiddle(object):
def process_request(self,request):
print 'Nextmiddle process_request'
def process_view(self, request, callback, callback_args, callback_kwargs):
print 'Nextmiddle process_view'
def process_exception(self, request, exception):
pass
def process_response(self, request, response):
print 'Nextmiddle process_response'
return response


Python之路【第十六篇续】Django进阶篇_django_97


2.2、注册中间件



Python之路【第十六篇续】Django进阶篇_数据_98

MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'middleware.middle.Testmiddle',
'middleware.middle.Nextmiddle',
]


Python之路【第十六篇续】Django进阶篇_ajax_99


2.3、测试使用url和views



from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/$', views.index),

]



def index(request):
print 'This app01 Views.index'
return HttpResponse('OK')


2.4、查看输出结果:



Python之路【第十六篇续】Django进阶篇_中间件_100

'''
Testmiddle process_request
Nextmiddle process_request
Testmiddle process_view
Nextmiddle process_view
This app01 Views.index
Nextmiddle process_response
Testmiddle process_response
'''


Python之路【第十六篇续】Django进阶篇_html_101


从输出结果可以看出:

他是先执行Testmiddle 的request 方法又执行了Nextmiddle的 process_request方法。。。。

2.5、原理:

当请求进来了到达中间件

去settings里面找到MIDDLEWARE_CLASSES,MIDDLEWARE_CLASSES是一个元组

有4个列表:



Python之路【第十六篇续】Django进阶篇_中间件_102

process_request_lsit = []
process_view_list = []
process_response_list = []
然后他循环MIDDLEWARE_CLASSES这个类:
for 类 in MIDDLEWARE_CLASSES:
  obj = 类()
  if obj里有process_request方法:
    process_request_lsit.append(obj.process_request)


Python之路【第十六篇续】Django进阶篇_django_103


然后循环后后执行:



for i in process_request_list:
  i() #加括号执行方法

for i in process_view_list:
i()
............


源码:


Python之路【第十六篇续】Django进阶篇_ajax_21Python之路【第十六篇续】Django进阶篇_html_22


def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE_CLASSES.

Must be called after the environment is fixed (see __call__ in subclasses).
"""
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []

request_middleware = []
for middleware_path in settings.MIDDLEWARE_CLASSES:
mw_class = import_string(middleware_path)
try:
mw_instance = mw_class()
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue

if hasattr(mw_instance, 'process_request'):
request_middleware.append(mw_instance.process_request)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.append(mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.insert(0, mw_instance.process_template_response)
if hasattr(mw_instance, 'process_response'):
self._response_middleware.insert(0, mw_instance.process_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.insert(0, mw_instance.process_exception)

# We only assign to this when initialization is complete as it is used
# as a flag for initialization being complete.
self._request_middleware = request_middleware

middleware

3、中间件的流程梳理

首先看下自定义的中间件中的process_response方法他是有返回值的其他的是没有返回值的。这个return response是什么呢?

这个response就是咱们自定义的views.index返回的结果!



def process_response(self, request, response):
print 'Testmiddle process_response'
return response


如果在其他的没有返回值得,仅有process_response有返回值得话他的请求流程是这样的:

Python之路【第十六篇续】Django进阶篇_django_23

但是如果在process_request或者process_view又返回值得话那么流程就完全不一样了!

举例:如果有m1和m2两个中间件,如果我在m1中的request方法中设置了,如果访问为1.1.1.1那么返回要一个404,那么他的访问流程是这样的:

Python之路【第十六篇续】Django进阶篇_html_24

 

process_exception  什么时候触发呢?咱们定义的views.index出错的时候他就会捕捉到然后执行咱们定义的process_exception方法如下图:

Python之路【第十六篇续】Django进阶篇_django_25

Django缓存

由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者Redis中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。

举个例子来说:如果访问量比较大的时候,有很多相同的操作比如:有时候请求的数据比如访问同一条数据,或者同一个页面的时候,其实是没必要的。

Django支持,mysql,Redis、Memecache、文件的方式做缓存,并且可以设置超时时间。

settings配置:



Python之路【第十六篇续】Django进阶篇_html_109

CACHES = {
'default': {
#定义已文件的方式进行cache
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
#cache文件的存放路径
'LOCATION': os.path.join(BASE_DIR, 'cache'),
#超时时间为600妙
'TIMEOUT': 600,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}


Python之路【第十六篇续】Django进阶篇_ajax_110


给请求应用,就是使用装饰器



Python之路【第十六篇续】Django进阶篇_中间件_111

from django.views.decorators.cache import cache_page

#这里设置的是 60秒 * 15 ,15分钟之后
@cache_page(60 * 15)
def cache_page(request):
current = str(time.time())
return HttpResponse(current)


Python之路【第十六篇续】Django进阶篇_html_112


Session&Cookie

Cookie就是一段字符串,保存于本机电脑上。

session 保存于服务器,用来保存用户的会话信息,依赖于Cookies

1、流程

举个例子,咱们在登录一个网站后,拿JD举例,如果我登录进去之后,在想点击订单的时候。server断怎么判断我是“我”,而不是其他人呢?

Http是短连接,那么Server端肯定有一个保存我登录状态的地方(session),那server怎么判断是我发送过来的请求呢?就是通过Cookie!

Python之路【第十六篇续】Django进阶篇_ajax_30

当客户端访问过来后,server端会在IE里生成一个Cookie,当访问过来的时候就可以通过Cookie进行判断

2、结构

1、自动生成一段字符串

2、将字符串发送到客户端的浏览器,同时把字符串当做key放在session里。(可以理解为session就是一个字典)

3、在用户的session对应的value里设置任意值

Python之路【第十六篇续】Django进阶篇_数据_31

Python之路【第十六篇续】Django进阶篇_数据_32

3、操作

3.1、操作session

  • 获取session:request.session[key]
  • 设置session:reqeust.session[key] = value
  • 删除session:del request[key]



request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。


实例:



Python之路【第十六篇续】Django进阶篇_ajax_116

def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'shuai' and password == '123':
result = request.session.get('IS_LOGIN', None)
print result
request.session['IS_LOGIN'] = True
return redirect('/index/')
obj = forms.LoginForm()
# 如果登录成功,写入session,跳转index
return render(request, 'account/login.html', {'model': obj})


def index(request):
'''
如果用户已经登录
'''
is_login = request.session.get('IS_LOGIN',False)
if is_login:
return render(request, 'home/index.html')
else:
return redirect('/login/')


Python之路【第十六篇续】Django进阶篇_数据_117


注:这里需要注意在session中,我们可以设置多个key:value的值,方便我们做很多事情,比如判断哪个用户:

  如果用户登录后,那么他肯定有一个cookie那么他在访问购物车的时候,怎么判断是哪个用户呢?我们可以在session设置,当用户登录的时候,我们把的用户名,增加到session中,那么用户携带cookie访问的时候,我们就能判断是哪个一用来访问的!

比如下面的对应关系:

user1



cookie :aaaa


server session(举例格式)



{session:aaaa{'IS_LOGIN':'True',username:'shuaige'}}


 4、Session和Cookie好处

使用Session和Cookie的好处:Cookie可以理解为一个身份证ID,你只能拿着他去和Server端进行通信,如果你没有这个ID那么server端也不知道你是谁!

实例:(0 0 !)

我在写博客的时候在做Cookie和Session的实验,把Cookie删掉了!当我保存的时候直接给我提出来了,为什么呢?就是因为,server端不知道我是谁了,我已经没有密钥了。

 

所以,只要Session和Cookie任意一方失效,就可以理解为:

Cookie失效就相当于身份证ID过期,需要重新认证才可以继续使用。Session失效就相当于银行里的数据标识此ID无效,也需要重新申请。

Django Form表单

在实际的生产环境中比如登录和验证的时候,我们一般都使用Jquery+ajax来判断用户的输入是否为空,假如JS被禁用的话,咱们这个认证屏障是不是就消失了呢?(虽然一般不会禁用掉但是还是存在风险)

所以我们一般做两种认证一种是前端做一遍认证,在后端做一遍认证

首先咱们看一下下面的案例:



Python之路【第十六篇续】Django进阶篇_html_118

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render

# Create your views here.


def user_list(request):
host = request.POST.get('host')
port = request.POST.get('port')
mail = request.POST.get('mail')
mobile = request.POST.get('mobile')
#这里有个问题,如果,这个from表单有20个input,你在这里是不是的取20次?

#验证:
#输入不能为空,并且有的可以为空有的不可以为空
#如果email = 11123123 这样合法吗?
#如果mobile = 11123123 这样合法吗?
#如果ip = 11123123 这样合法吗?
'''
你在这里是不是需要做一大堆的输入验证啊?并且有很多这种页面会存在这种情况,如果每个函数都这样做估计就累死了
'''
return render(request,'user_list.html')


Python之路【第十六篇续】Django进阶篇_中间件_119


在样能解决这个问题呢?通过Django的form来实现,其他语言也有叫做(模型绑定)

Django的form的作用:

1、生成html标签

2、用来做用户提交的验证

1、生成html标签

views

 



Python之路【第十六篇续】Django进阶篇_django_120

from django import forms

class UserInfo(forms.Form):
email = forms.EmailField(required=False) #required是否可以为空,如果为False说明可以为空
host = forms.CharField() #如果required不写默认为Ture
port = forms.CharField()
mobile = forms.CharField()

def user_list(request):
obj = UserInfo() #创建了这个对象
return render(request,'user_list.html',{'obj':obj})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_数据_121


 

html调用



Python之路【第十六篇续】Django进阶篇_数据_122

<form action="/user_list/" method="post">
<p>主机:{{ obj.host }}</p>
<p>端口:{{ obj.port }}</p>
<p>邮箱:{{ obj.email }}</p>
<p>手机:{{ obj.mobile }}</p>
<input type="submit" value="submit"/>
</form>




把我们的对象穿进去,html在引用的时候直接obj.host就可以自动生成html标签,然后看下html显示:

Python之路【第十六篇续】Django进阶篇_数据_41

2、简单的form表单验证用户输入的内容



Python之路【第十六篇续】Django进阶篇_ajax_124

def user_list(request):
obj = UserInfo() #创建了这个对象
if request.method == 'POST':
#获取用户输入一句话就搞定
user_input_obj = UserInfo(request.POST)
'''
咱们把post过来的数据当参数传给UserInfo咱们定义的这个类,UserInfo会自动会去你提交的数据
email/host/port/mobile 自动的封装到user_input_obj里,封装到这个对象里我们就可以判断输入是否合法
'''
print user_input_obj.is_valid() #

return render(request,'user_list.html',{'obj':obj})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_ajax_125


当我们输入不合法的时候,(在创建类设置的需求)为空、或者不是email格式的时候!

Python之路【第十六篇续】Django进阶篇_django_44

这样在后端我们是不是就有一套验证的机制?就可以通过is_valid()来判断用户输入是否合法!如果不合法就把返回信息发送过去,如果合法获取数据操作即可!

捕获错误信息并返回

 



Python之路【第十六篇续】Django进阶篇_html_127

from django import forms

class UserInfo(forms.Form):
email = forms.EmailField(required=True) #required是否可以为空,如果为False说明可以为空
host = forms.CharField() #如果required不写默认为Ture
port = forms.CharField()
mobile = forms.CharField()

def user_list(request):
obj = UserInfo() #创建了这个对象
if request.method == 'POST':
#获取用户输入一句话就搞定
user_input_obj = UserInfo(request.POST)
'''
咱们把post过来的数据当参数传给UserInfo咱们定义的这个类,UserInfo会自动会去你提交的数据
email/host/port/mobile 自动的封装到user_input_obj里,封装到这个对象里我们就可以判断输入是否合法
'''
if user_input_obj.is_valid(): #判断用户输入是否合法
data = user_input_obj.clean() #获取用户输入
print data
else:
#如果发生错误,捕捉错误
error_msg = user_input_obj.errors
print error_msg #打印一下然后看下他的类型
'''
<ul class="errorlist">
<li>mobile<ul class="errorlist"><li>This field is required.
</li></ul></li>
<li>host<ul class="errorlist"><li>This field is required.</li></ul></li>
<li>port<ul class="errorlist"><li>This field is required.</li></ul></li>
</ul>
'''
#然后把错误信息返回
return render(request,'user_list.html',{'obj':obj,'errors':error_msg,})#然后把对象传给html,在把错误信息传递过去
return render(request,'user_list.html',{'obj':obj,})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_django_128


 

html标签,使用error输出



Python之路【第十六篇续】Django进阶篇_django_129

<form action="/user_list/" method="post">
<p>主机:{{ obj.host }}<span>{{ errors.host }}</span></p>
<p>端口:{{ obj.port }}<span>{{ errors.port }}</span></p>
<p>邮箱:{{ obj.email }}<span>{{ errors.email }}</span></p>
<p>手机:{{ obj.mobile }}<span>{{ errors.mobile }}</span></p>
<input type="submit" value="submit"/>
</form>


Python之路【第十六篇续】Django进阶篇_ajax_130


现在在去点击下看下效果:

Python之路【第十六篇续】Django进阶篇_django_49

这样如果,我都按照要求提交,就可以取到数据了?这样咱们就不用自己去拿数据是,NICE,NICE~~



{'mobile': u'123456789', 'host': u'1.1.1.1', 'email': u'shuaige@qq.com', 'port': u'8000'}


3、form表单定制化

3.1、自定义报错内容

  在form里有一个参数:error_messages  在他这里就可以定义报错内容



class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})
mobile = forms.CharField(error_messages={'required':u'手机不能为空'})


效果:

Python之路【第十六篇续】Django进阶篇_ajax_50

3.2、我想给form表单添加一个属性



Python之路【第十六篇续】Django进阶篇_数据_133

class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})
mobile = forms.CharField(error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)


Python之路【第十六篇续】Django进阶篇_ajax_134


看下效果:

Python之路【第十六篇续】Django进阶篇_中间件_53

3.3、在给他增加一个备注



Python之路【第十六篇续】Django进阶篇_数据_136

class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})
mobile = forms.CharField(error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)
#咱们在新增一个备注
memo = forms.CharField(required=False,
widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备注'})

)


Python之路【第十六篇续】Django进阶篇_数据_137


html代码



Python之路【第十六篇续】Django进阶篇_html_138

<form action="/user_list/" method="post">
<p>主机:{{ obj.host }}<span>{{ errors.host }}</span></p>
<p>端口:{{ obj.port }}<span>{{ errors.port }}</span></p>
<p>邮箱:{{ obj.email }}<span>{{ errors.email }}</span></p>
<p>手机:{{ obj.mobile }}<span>{{ errors.mobile }}</span></p>
<p>备注:{{ obj.memo }}<span>{{ errors.memo }}</span></p>

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


Python之路【第十六篇续】Django进阶篇_数据_139


4、自定义正则表达式增加判断规则



Python之路【第十六篇续】Django进阶篇_django_140

import re
from django import forms
from django.core.exceptions import ValidationError


#自定义方法
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') #正则匹配
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误') #如果没有匹配到主动触发一个错误


class UserInfo(forms.Form):
email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})

#默认mobile里有一个默认为空的机制,我们在原有的参数里增加怎们自定义的方法
mobile = forms.CharField(validators=[mobile_validate,],#应用咱们自己定义的规则
error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)
#咱们在新增一个备注
memo = forms.CharField(required=False,
widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备注'})

)


Python之路【第十六篇续】Django进阶篇_html_141


效果:

如果为空的话会提示,不能为空如果格式不对的话会提示:

Python之路【第十六篇续】Django进阶篇_ajax_60

5、生成select标签



Python之路【第十六篇续】Django进阶篇_ajax_143

class UserInfo(forms.Form):

user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),)

user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,attrs={'class':'form-control'}))

。。。。。。。。。


Python之路【第十六篇续】Django进阶篇_ajax_144


html内



Python之路【第十六篇续】Django进阶篇_django_145

<form action="/user_list/" method="post">
<p>用户类型:{{ obj.user_type }}<span>{{ errors.user_type }}</span></p>
<p>主机:{{ obj.host }}<span>{{ errors.host }}</span></p>
<p>端口:{{ obj.port }}<span>{{ errors.port }}</span></p>
<p>邮箱:{{ obj.email }}<span>{{ errors.email }}</span></p>
<p>手机:{{ obj.mobile }}<span>{{ errors.mobile }}</span></p>
<p>备注:{{ obj.memo }}<span>{{ errors.memo }}</span></p>

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


Python之路【第十六篇续】Django进阶篇_ajax_146


6、关于后端验证

这个后端验证是必须要有验证机制的,前端可以不写但是后端必须要写!前端的JS是可以被禁用掉到。

6、Django form漂亮的显示错误信息

设置显示error的样式



error_msg = user_input_obj.errors.as_data()#这里原来什么都没写,默认是ul的样式,默认是as_ul(),如果我们写成as_data()返回的就是一个原生的字符串
#还有一个as_json


实例:

 



Python之路【第十六篇续】Django进阶篇_中间件_147

import re
from django import forms
from django.core.exceptions import ValidationError

#自定义方法
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') #正则匹配
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误') #如果没有匹配到主动出发一个错误


class UserInfo(forms.Form):

user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),)

user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,attrs={'class':'form-control'}))

email = forms.EmailField(required=True,error_messages={'required':u'邮箱不能为空'}) #required是否可以为空,如果为False说明可以为空
host = forms.CharField(error_messages={'required':u'主机不能为空'}) #如果required不写默认为Ture
port = forms.CharField(error_messages={'required':u'端口不能为空'})

#默认mobile里有一个默认为空的机制,我们在原有的参数里增加怎们自定义的方法
mobile = forms.CharField(validators=[mobile_validate,],#应用咱们自己定义的规则
error_messages={'required':u'手机不能为空'},
widget=forms.TextInput(attrs={'class':'form-control','placeholder':u'手机号码'})
#这里默认是TextInput,标签
)
#咱们在新增一个备注
memo = forms.CharField(required=False,
widget=forms.Textarea(attrs={'class':'form-control','placeholder':u'备注'}))

def user_list(request):
obj = UserInfo() #创建了这个对象
if request.method == 'POST':
#获取用户输入一句话就搞定
user_input_obj = UserInfo(request.POST)

if user_input_obj.is_valid(): #判断用户输入是否合法
data = user_input_obj.clean() #获取用户输入
print data
else:
#如果发生错误,捕捉错误
error_msg = user_input_obj.errors.as_data()#这里原来什么都没写,默认是ul的样式,默认是as_ul(),如果我们写成as_data()返回的就是一个原生的字符串
#还有一个as_json

print error_msg #打印一下然后看下他的类型
#然后把错误信息返回
return render(request,'user_list.html',{'obj':obj,'errors':error_msg,})#然后把对象传给html,在把错误信息传递过去
return render(request,'user_list.html',{'obj':obj,})#然后把对象传给html


Python之路【第十六篇续】Django进阶篇_django_148


这里在html中如果不进行处理默认显示的是:

Python之路【第十六篇续】Django进阶篇_数据_67

看下他的实际是什么内容:



{'mobile': [ValidationError([u'\u624b\u673a\u4e0d\u80fd\u4e3a\u7a7a'])], 
'host': [ValidationError([u'\u4e3b\u673a\u4e0d\u80fd\u4e3a\u7a7a'])],
'email': [ValidationError([u'\u90ae\u7bb1\u4e0d\u80fd\u4e3a\u7a7a'])],
'port': [ValidationError([u'\u7aef\u53e3\u4e0d\u80fd\u4e3a\u7a7a'])]}


所以我们自定义一个模板语言对其进行修饰:

Python之路【第十六篇续】Django进阶篇_html_68

然后在html中调用



Python之路【第十六篇续】Django进阶篇_中间件_151

{% load cmdb_tag %}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shuai</title>
</head>
<body>
<form action="/user_list/" method="post">
<p>用户类型:{{ obj.user_type }}<span>{% error_message errors.user_type %}</span></p>
<p>主机:{{ obj.host }}<span>{% error_message errors.host %}</span></p>
<p>端口:{{ obj.port }}<span>{% error_message errors.port %}</span></p>
<p>邮箱:{{ obj.email }}<span>{% error_message errors.email %}</span></p>
<p>手机:{{ obj.mobile }}<span>{% error_message errors.mobile %}</span></p>
<p>备注:{{ obj.memo }}<span>{% error_message errors.memo %}</span></p>
<input type="submit" value="submit"/>
</form>
</body>
</html>


Python之路【第十六篇续】Django进阶篇_django_152


显示效果如下:

Python之路【第十六篇续】Django进阶篇_数据_71

因为模板语言不支持用索引的方式取值,所以我们通过自定义simp_tag来进行取值

Ajax

1、单条数据提交

在上面的原有例子中的html中新增下面html内容



Python之路【第十六篇续】Django进阶篇_中间件_154

<form action="/user_list/" method="post">
<input type="button" onclick="Ajaxsubmit();" value="提交"/>
<table>
<thead>
<tr>
<th>主机名</th>
<th>端口</th>
</tr>
</thead>
<tbody>
<tr>
<td>1.1.1.1</td>
<td>80000</td>
</tr>
<tr>
<td>1.1.1.1</td>
<td>80000</td>
</tr>
</tbody>
</table>
</form>
<script type="text/javascript" src="/static/jquery-2.2.1.min.js"></script>
<script>
function Ajaxsubmit(){
var host = '1.1.1.1';
var port = '8000';

$.ajax({
url:"/ajax_data/",
type:'POST',
data:{h:host,p:port},
success:function(arg){

}
})
}
</script>


Python之路【第十六篇续】Django进阶篇_html_155


注释:



Python之路【第十六篇续】Django进阶篇_html_156

$.ajax({
url:"/ajax_data/", #目标URL
type:'POST', #请求方式
data:{h:host,p:port}, 以h和p为key用户的输入为value:<QueryDict: {u'h': [u'1.1.1.1'], u'p': [u'8000']}>
success:function(arg){

}


Python之路【第十六篇续】Django进阶篇_ajax_157


增加URL和views



url(r'^ajax_data/', views.ajax_data),



def ajax_data(request):
print request.POST
return HttpResponse('OK')


2、ajax多条数据提交

在原来的基础上修改Jquery



Python之路【第十六篇续】Django进阶篇_django_158

<script>
function Ajaxsubmit(){
var array_users = [
{'username':'shuaige','arg':18},
{'username':'tianshuai','arg':18},
{'username':'shuai','arg':18},

];
$.ajax({
url:"/ajax_mdata/",
type:'POST',
data:{data:array_users},
success:function(arg){

}
})
}
</script>


Python之路【第十六篇续】Django进阶篇_中间件_159


添加urls&views



url(r'^ajax_mdata/$', views.ajax_mdata),


views



def ajax_mdata(request):
print request.POST
return HttpResponse('OK')


点击提交看下(在server端打印看下):



<QueryDict: {u'data[1][username]': [u'tianshuai'], u'data[0][username]': [u'shuaige'], u'data[0][arg]': [u'18'], u'data[1][arg]': [u'18'], u'data[2][username]': [u'shuai'], u'data[2][arg]': [u'18']}>


上面的结果数据是有问题的!他给咱们做了个加工,咱们没给他传data[1],data[0]了吗?

所以咱们的在ajax增加参数



Python之路【第十六篇续】Django进阶篇_html_160

<script>
function Ajaxsubmit(){
var array_users = [
{'username':'shuaige','arg':18},
{'username':'tianshuai','arg':18},
{'username':'shuai','arg':18},

];
$.ajax({
url:"/ajax_mdata/",
type:'POST',
tradition: true,
data:{data:JSON.stringify(array_users)},
success:function(arg){

}
})
}
</script>


Python之路【第十六篇续】Django进阶篇_数据_161


增加了两项:



#以原生的模式传过去
tradition: true,

#把数组做一步处理转成字符串
data:{data:JSON.stringify(array_users)},


3、在一个Ajax请求之后,返回信息应该更职业化,不能单单发送一个字符串

看下面的就不像程序员:



def ajax_data(request):
print request.POST
return HttpResponse('OK')


应该这么写:(下面的例子先用json来做,不过还有一个json response)



Python之路【第十六篇续】Django进阶篇_ajax_162

import json

def ajax_data(request):
ret = {'status':True,'error':''}
try:
print request.POST
except Exception,e:
ret['status'] = False
ret['error'] = str(e)
#在上面如果他出错我就把他ret[status] = False
return HttpResponse(json.dumps(ret))


Python之路【第十六篇续】Django进阶篇_数据_163


html的js也得修改下:



Python之路【第十六篇续】Django进阶篇_中间件_164

<script>
function Ajaxsubmit(){
var array_users = [
{'username':'shuaige','arg':18},
{'username':'tianshuai','arg':18},
{'username':'shuai','arg':18},

];
$.ajax({
url:"/ajax_mdata/",
type:'POST',
tradition: true,
data:{data:JSON.stringify(array_users)},
success:function(arg){
var callback_dict = $.parseJSON(arg);//这里把字符串转换为对象
//然后咱们就可以判断
if(callback_dict){//执行成功了
//简单测试
alert('提交成功')
}else{//如果为False执行失败了
alert(callback_dict.error)
}

}
})
}
</script>