Python之路【第十六篇續】Django進階篇


Django請求生命周期

首先:對於所有的web框架來說本質就是一個socket服務端,瀏覽器是socket客戶端

路由系統

在Django的urls中我們可以根據一個URL對應一個函數名來定義路由規則如下:

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),
]

2、默認URL

上面一個URL對應一個函數!我們可以在整個的url最下面里設置一個默認的URL,當用戶訪問我們的網站的時候沒有指定詳細的URL的時候我們默認讓他們跳轉到一個URL。

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),
]

這里需要注意下:當客戶訪問過來請求的時候,到達路由系統后是根據正則來匹配的,如果上面的匹配成功了,后面的路由規則將不會繼續匹配,需要注意!!!!所以我們在后面都加一個$來做結尾

3、動態URL

3.1、動態URL傳參

咱們看下園的分頁連接如下圖:

問:如果有這么多的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,那么我們的路由規則只能寫在一個文件里嗎?

當然不是,我們可以通過下面的方式來把他分開:

    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也進行用正則處理了。比如下面:

    url(r'^(?P<controller>\w+)/(?P<action>\w+)', mp),
    #咱們給他做個定義mp中第一個是文件比如
    #home.py   第二個參數是文件中的函數 def index
    #
    #/home/index/
    #/login/index/
    #/update/index/

但是上面的方法僅僅是通過反射來實現的,通過文件找到里面的函數然后執行!

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

中間件

中間件定義:

  中間件是一個、一個的管道,如果相對任何所有的通過Django的請求進行管理都需要自定義中間件

  中間件可以對進來的請求和出去的請求進行控制

  中間件是一類。

看下面的代碼在settings里中間件的類:

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 application order

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、自定義中間件

\

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

2.2、注冊中間件

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',
]

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、查看輸出結果:

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

從輸出結果可以看出:

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

2.5、原理:

當請求進來了到達中間件

去settings里面找到MIDDLEWARE_CLASSES,MIDDLEWARE_CLASSES是一個元組

有4個列表:

process_request_lsit = []
process_view_list = []
process_response_list = []
然后他循環MIDDLEWARE_CLASSES這個類:
forin MIDDLEWARE_CLASSES:
  obj = 類()
  if obj里有process_request方法:
    process_request_lsit.append(obj.process_request)

然后循環后后執行:

for i in process_request_list:
  i() #加括號執行方法

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

源碼:

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有返回值得話他的請求流程是這樣的:

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

舉例:如果有m1和m2兩個中間件,如果我在m1中的request方法中設置了,如果訪問為1.1.1.1那么返回要一個404,那么他的訪問流程是這樣的:

 

process_exception  什么時候觸發呢?咱們定義的views.index出錯的時候他就會捕捉到然后執行咱們定義的process_exception方法如下圖:

Django緩存

由於Django是動態網站,所有每次請求均會去數據進行相應的操作,當程序訪問量大時,耗時必然會更加明顯,最簡單解決方式是使用:緩存,緩存將一個某個views的返回值保存至內存或者Redis中,5分鍾內再有人來訪問時,則不再去執行view中的操作,而是直接從內存或者Redis中之前緩存的內容拿到,並返回。

舉個例子來說:如果訪問量比較大的時候,有很多相同的操作比如:有時候請求的數據比如訪問同一條數據,或者同一個頁面的時候,其實是沒必要的。

Django支持,mysql,Redis、Memecache、文件的方式做緩存,並且可以設置超時時間。

settings配置:

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
        }
    }
}

給請求應用,就是使用裝飾器

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)

Session&Cookie

Cookie就是一段字符串,保存於本機電腦上。

session 保存於服務器,用來保存用戶的會話信息,依賴於Cookies

1、流程

舉個例子,咱們在登錄一個網站后,拿JD舉例,如果我登錄進去之后,在想點擊訂單的時候。server斷怎么判斷我是“我”,而不是其他人呢?

Http是短連接,那么Server端肯定有一個保存我登錄狀態的地方(session),那server怎么判斷是我發送過來的請求呢?就是通過Cookie!

當客戶端訪問過來后,server端會在IE里生成一個Cookie,當訪問過來的時候就可以通過Cookie進行判斷

2、結構

1、自動生成一段字符串

2、將字符串發送到客戶端的瀏覽器,同時把字符串當做key放在session里。(可以理解為session就是一個字典)

3、在用戶的session對應的value里設置任意值

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失效策略。

實例:

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/')

注:這里需要注意在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被禁用的話,咱們這個認證屏障是不是就消失了呢?(雖然一般不會禁用掉但是還是存在風險)

所以我們一般做兩種認證一種是前端做一遍認證,在后端做一遍認證

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

#/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')

在樣能解決這個問題呢?通過Django的form來實現,其他語言也有叫做(模型綁定)

Django的form的作用:

1、生成html標簽

2、用來做用戶提交的驗證

1、生成html標簽

views

 

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

 

html調用

    <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顯示:

2、簡單的form表單驗證用戶輸入的內容

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

當我們輸入不合法的時候,(在創建類設置的需求)為空、或者不是email格式的時候!

這樣在后端我們是不是就有一套驗證的機制?就可以通過is_valid()來判斷用戶輸入是否合法!如果不合法就把返回信息發送過去,如果合法獲取數據操作即可!

捕獲錯誤信息並返回

 

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

 

html標簽,使用error輸出

    <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>

現在在去點擊下看下效果:

這樣如果,我都按照要求提交,就可以取到數據了?這樣咱們就不用自己去拿數據是,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'手機不能為空'})

效果:

3.2、我想給form表單添加一個屬性

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,標簽
                              )

看下效果:

3.3、在給他增加一個備注

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'備注'})

     )

html代碼

    <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>

4、自定義正則表達式增加判斷規則

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'備注'})

     )

效果:

如果為空的話會提示,不能為空如果格式不對的話會提示:

5、生成select標簽

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'}))

。。。。。。。。。

html內

    <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>

6、關於后端驗證

這個后端驗證是必須要有驗證機制的,前端可以不寫但是后端必須要寫!前端的JS是可以被禁用掉到。

6、Django form漂亮的顯示錯誤信息

設置顯示error的樣式

error_msg = user_input_obj.errors.as_data()#這里原來什么都沒寫,默認是ul的樣式,默認是as_ul(),如果我們寫成as_data()返回的就是一個原生的字符串
            #還有一個as_json

實例:

 

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

這里在html中如果不進行處理默認顯示的是:

看下他的實際是什么內容:

{'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'])]}

所以我們自定義一個模板語言對其進行修飾:

然后在html中調用

{% 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>

顯示效果如下:

因為模板語言不支持用索引的方式取值,所以我們通過自定義simp_tag來進行取值

Ajax

1、單條數據提交

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

    <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>

注釋:

            $.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){

                }

增加URL和views

    url(r'^ajax_data/', views.ajax_data),
def ajax_data(request):
    print request.POST
    return HttpResponse('OK')

2、ajax多條數據提交

在原來的基礎上修改Jquery

    <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>

添加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增加參數

    <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>

增加了兩項:

#以原生的模式傳過去
tradition: true,  

#把數組做一步處理轉成字符串
data:{data:JSON.stringify(array_users)},

3、在一個Ajax請求之后,返回信息應該更職業化,不能單單發送一個字符串

看下面的就不像程序員:

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

應該這么寫:(下面的例子先用json來做,不過還有一個json response)

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))

html的js也得修改下:

    <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>

參考鏈接:本人騷師:http://www.cnblogs.com/wupeiqi/articles/5237704.html 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM