Django項目知識點彙總

目錄

1、wsgi接口

2、中間件

3、URL路由系統

4、Template模板

5、Views視圖

6、Model&ORM

7、Admin相關

8、Http協議

9、COOKIE 與 SESSION

10、Django的用戶認證

11、From與ModelForm

12、分頁

十3、緩存

十4、信號

十5、序列化

十6、Ajax

十7、數據庫性能相關

0、Django的生命週期

 

1、wsgi接口

瞭解了HTTP協議和HTML文檔,咱們其實就明白了一個Web應用的本質就是:javascript

  1. 瀏覽器發送一個HTTP請求;css

  2. 服務器收到請求,生成一個HTML文檔;html

  3. 服務器把HTML文檔做爲HTTP響應的Body發送給瀏覽器;前端

  4. 瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。java

因此,最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。Apache、Nginx、Lighttpd等這些常見的靜態服務器就是幹這件事情的。node

若是要動態生成HTML,就須要把上述步驟本身來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,若是咱們本身來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。python

正確的作法是底層代碼由專門的服務器軟件實現,咱們用Python專一於生成HTML文檔。由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。jquery

這個接口就是WSGI:Web Server Gateway Interface。git

WSGI接口定義很是簡單,它只要求Web開發者實現一個函數,就能夠響應HTTP請求。咱們來看一個最簡單的Web版本的「Hello, web!」:web

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, web!</h1>'

  

上面的application()函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數:

  • environ:一個包含全部HTTP請求信息的dict對象;

  • start_response:一個發送HTTP響應的函數。

application()函數中,調用:

start_response('200 OK', [('Content-Type', 'text/html')])

就發送了HTTP響應的Header,注意Header只能發送一次,也就是隻能調用一次start_response()函數。start_response()函數接收兩個參數,一個是HTTP響應碼,一個是一組list表示的HTTP Header,每一個Header用一個包含兩個strtuple表示。

一般狀況下,都應該把Content-Type頭髮送給瀏覽器。其餘不少經常使用的HTTP Header也應該發送。

而後,函數的返回值'<h1>Hello, web!</h1>'將做爲HTTP響應的Body發送給瀏覽器。

有了WSGI,咱們關心的就是如何從environ這個dict對象拿到HTTP請求信息,而後構造HTML,經過start_response()發送Header,最後返回Body。

整個application()函數自己沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不須要咱們本身編寫,咱們只負責在更高層次上考慮如何響應請求就能夠了。

不過,等等,這個application()函數怎麼調用?若是咱們本身調用,兩個參數environstart_response咱們無法提供,返回的str也無法發給瀏覽器。

因此application()函數必須由WSGI服務器來調用。有不少符合WSGI規範的服務器,咱們能夠挑選一個來用。可是如今,咱們只想儘快測試一下咱們編寫的application()函數真的能夠把HTML輸出到瀏覽器,因此,要趕忙找一個最簡單的WSGI服務器,把咱們的Web應用程序跑起來。

好消息是Python內置了一個WSGI服務器,這個模塊叫wsgiref,它是用純Python編寫的WSGI服務器的參考實現。所謂「參考實現」是指該實現徹底符合WSGI標準,可是不考慮任何運行效率,僅供開發和測試使用。

運行WSGI服務

咱們先編寫hello.py,實現Web應用程序的WSGI處理函數:

# hello.py

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, web!</h1>'

而後,再編寫一個server.py,負責啓動WSGI服務器,加載application()函數:

# server.py
# 從wsgiref模塊導入:
from wsgiref.simple_server import make_server
# 導入咱們本身編寫的application函數:
from hello import application

# 建立一個服務器,IP地址爲空,端口是8000,處理函數是application:
httpd = make_server('', 8000, application)
print ("Serving HTTP on port 8000...")
# 開始監聽HTTP請求:
httpd.serve_forever()

確保以上兩個文件在同一個目錄下,而後在命令行輸入python server.py來啓動WSGI服務器:

 

注意:若是8000端口已被其餘程序佔用,啓動將失敗,請修改爲其餘端口。

啓動成功後,打開瀏覽器,輸入http://localhost:8000/,就能夠看到結果了:

整個application()函數自己沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不須要咱們本身編寫,
咱們只負責在更高層次上考慮如何響應請求就能夠了。

application()函數必須由WSGI服務器來調用。有不少符合WSGI規範的服務器,咱們能夠挑選一個來用。

Python內置了一個WSGI服務器,這個模塊叫wsgiref    
    
    
application()函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數:

        //environ:一個包含全部HTTP請求信息的dict對象;
        
        //start_response:一個發送HTTP響應的函數。

在application()函數中,調用:

start_response('200 OK', [('Content-Type', 'text/html')])

就發送了HTTP響應的Header,注意Header只能發送一次,也就是隻能調用一次start_response()函數。
start_response()函數接收兩個參數,一個是HTTP響應碼,一個是一組list表示的HTTP Header,每
個Header用一個包含兩個str的tuple表示。

一般狀況下,都應該把Content-Type頭髮送給瀏覽器。其餘不少經常使用的HTTP Header也應該發送。

而後,函數的返回值b'<h1>Hello, web!</h1>'將做爲HTTP響應的Body發送給瀏覽器。

有了WSGI,咱們關心的就是如何從environ這個dict對象拿到HTTP請求信息,而後構造HTML,
經過start_response()發送Header,最後返回Body。
注意

 

回到頂部

2、中間件

django 中的中間件(middleware),在django中,中間件其實就是一個類,在請求到來和結束後,django會根據本身的規則在合適的時機執行中間件中相應的方法。

在django項目的settings模塊中,有一個 MIDDLEWARE_CLASSES 變量,其中每個元素就是一箇中間件

中間件中一共有五個方法:

process_request

- 有,直接執行當前中間件和上方中間件的process_response

- 應用: 用戶登陸受權(排除不須要登陸的url)

process_view

 

process_exception

- process_tempalte_response
- 必須有返回值
- 必須對象中要有render方法

process_response

    - 必須有返回值
中間件五種方法

 

中間件之process_request,process_response

process_request(self,request)

process_response(self, request, response)

當用戶發起請求的時候會依次通過全部的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者

在django中叫中間件,在其餘web框架中,有的叫管道,httphandle

 

中間件之process_view執行過程:

當最後一箇中間的process_request到達路由關係映射以後,返回到中間件1的process_view,而後依次往下,到達views函數,最後經過process_response依次返回到達用戶

中間件之process_exception

process_exception(self, request, exception)

當views的函數中出現錯誤時,就會執行process_exception方法

若是在中間中添加了process_exception方法,工做圖示爲:

  這樣當用戶發起請求的時候到達中間件3的process_request以後會到達urls路由關係映射這裏,若是匹配到了就會到中間件1的process_view,而後依次傳遞到中間件3的process_view,到達view函數。若是view函數中有報錯,則會從中間件3依次向上判斷每一箇中間件的process_exception是否能匹配到這個錯誤信息,若是匹配到則直接返回到最後一箇中間件,這裏即中間件3的process_response,而後依次返回到用戶,若是沒有匹配到這個錯誤則直接在頁面顯示錯誤信息。若是view函數中沒有錯誤,則到中間3即最後一箇中間件3的process_response,而後依次向上,傳到用戶

中間件之process_template_responseprocess

process_template_response(self,request,response)

只有當views函數中返回的對象中具備render方法,是就會直接process_template_responseprocess

 中間件的應用

全部請求統一作處理時使用
- 登陸驗證

-添加訪問日誌等

 

自定義中間件

上述圖中的中間件都是django中的,咱們也能夠本身定義一箇中間件,咱們能夠本身寫一個類,可是必須繼承MiddlewareMixin

因此須要導入:from django.utils.deprecation import MiddlewareMixin

咱們在項目文件下建立一個Middle目錄,並在下面建立m1.py代碼例子以下

#AUTHOR:FAN
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Row1(MiddlewareMixin):
    def process_request(self,request):
        print("中間件1請求")
    def process_response(self,request,response):
        print("中間件1返回")
        return response

class Row2(MiddlewareMixin):
    def process_request(self,request):
        print("中間件2請求")
        # return HttpResponse("")
    def process_response(self,request,response):
        print("中間件2返回")
        return response

class Row3(MiddlewareMixin):
    def process_request(self,request):
        print("中間件3請求")
    def process_response(self,request,response):
        print("中間件3返回")
        return response
自定義中間件

配置settings.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'Middle.m1.Row1',
    'Middle.m1.Row2',
    'Middle.m1.Row3',
]
自定義中間件-配置setting.py

 

返回頂部 

 3、URL路由系統(URLconf)

URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL與要爲該URL調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。

 '''
    
    urlpatterns = [
         url(正則表達式, views視圖函數,參數,別名),
]


參數說明:

    一個正則表達式字符串
    一個可調用對象,一般爲一個視圖函數或一個指定視圖函數路徑的字符串
    可選的要傳遞給視圖函數的默認參數(字典形式)
    一個可選的name參數

    '''
URLconf說明

3.1 簡單配置

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
url配置實例
'''
    NOTE:
    1 一旦匹配成功則再也不繼續
    2 若要從URL 中捕獲一個值,只須要在它周圍放置一對圓括號。
    3 不須要添加一個前導的反斜槓,由於每一個URL 都有。例如,應該是^articles 而不是 ^/articles。
    4 每一個正則表達式前面的'r' 是可選的可是建議加上。

一些請求的例子:

    /articles/2005/3/ 不匹配任何URL 模式,由於列表中的第三個模式要求月份應該是兩個數字。
    /articles/2003/ 將匹配列表中的第一個模式不是第二個,由於模式按順序匹配,第一個會首先測試是否匹配。
    /articles/2005/03/ 請求將匹配列表中的第三個模式。Django 將調用函數
                       views.month_archive(request, '2005', '03')。

#設置項是否開啓URL訪問地址後面不爲/跳轉至帶有/的路徑
APPEND_SLASH=True

'''
url配置注意事項

3.2 有名分組(named group)

  上面的示例使用簡單的、沒有命名的正則表達式組(經過圓括號)來捕獲URL 中的值並以位置 參數傳遞給視圖。在更高級的用法中,能夠使用命名的正則表達式組來捕獲URL 中的值並以關鍵字 參數傳遞給視圖。

  在Python 正則表達式中,命名正則表達式組的語法是(?P<name>pattern),其中name 是組的名稱,pattern 是要匹配的模式。

下面是以上URLconf 使用命名組的重寫:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
url有名分組

這個實現與前面的示例徹底相同,只有一個細微的差異:捕獲的值做爲關鍵字參數而不是位置參數傳遞給視圖函數。例如:

/articles/2005/03/    
請求將調用views.month_archive(request, year='2005', month='03')函數
/articles/2003/03/03/ 
請求將調用函數views.article_detail(request, year='2003', month='03', day='03')。
url有名分組實例

在實際應用中,這意味你的URLconf 會更加明晰且不容易產生參數順序問題的錯誤 —— 你能夠在你的視圖函數定義中從新安排參數的順序。固然,這些好處是以簡潔爲代價;有些開發人員認爲命名組語法醜陋而繁瑣。

3.3 URLconf 在什麼上查找

URLconf 在請求的URL 上查找,將它當作一個普通的Python 字符串。不包括GET和POST參數以及域名。

例如,http://www.example.com/myapp/ 請求中,URLconf 將查找myapp/

在http://www.example.com/myapp/?page=3 請求中,URLconf 仍將查找myapp/

URLconf 不檢查請求的方法。換句話講,全部的請求方法 —— 同一個URL的POSTGETHEAD等等 —— 都將路由到相同的函數。

3.4 捕獲的參數永遠是字符串

每一個捕獲的參數都做爲一個普通的Python 字符串傳遞給視圖,不管正則表達式使用的是什麼匹配方式。例如,下面這行URLconf 中:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

views.year_archive() 的year 參數將是一個字符串

3.5 指定視圖參數的默認值

有一個方便的小技巧是指定視圖參數的默認值。 下面是一個URLconf 和視圖的示例

# URLconf
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

# View (in blog/views.py)
def page(request, num="1"):

    ...
View Code

 

  在上面的例子中,兩個URL模式指向同一個視圖views.page —— 可是第一個模式不會從URL 中捕獲任何值。若是第一個模式匹配,page() 函數將使用num參數的默認值"1"。若是第二個模式匹配,page() 將使用正則表達式捕獲的num 值。

3.1.6 路由分發( Including other URLconfs)

在項目中,若須要將某一應用下url統一跳轉,則能夠使用 Including other URLconfs

#At any point, your urlpatterns can 「include」 other URLconf modules. This
#essentially 「roots」 a set of URLs below other ones.

#For example, here’s an excerpt of the URLconf for the Django website itself.
#It includes a number of other URLconfs:


from django.conf.urls import include, url

urlpatterns = [
   url(r'^admin/', admin.site.urls),
   url(r'^blog/', include('blog.urls')),
]
路由分發(include other是url)

 返回頂部

4、Template模板

  python的模板:HTML代碼+邏輯控制代碼

4.1 模板支持的語法

4.1.1  變量(使用雙大括號來引用變量)

語法格式:       {{var_name}}

Django 模板解析很是快捷。 大部分的解析工做都是在後臺經過對簡短正則表達式一次性調用來完成。 這和基於 XML 的模板引擎造成鮮明對比,那些引擎承擔了 XML 解析器的開銷,且每每比 Django 模板渲染引擎要慢上幾個數量級。

from django.shortcuts import render,HttpResponse
from django.template.loader import get_template #記得導入
# Create your views here.


import datetime
from django.template import Template,Context

# def current_time(req):
    #原始的視圖函數
    # now=datetime.datetime.now()
    # html="<html><body>如今時刻:<h1>%s.</h1></body></html>" %now
    # return HttpResponse(html)

# def current_time(req):

      #django模板修改的視圖函數
#     now=datetime.datetime.now()
#     t=Template('<html><body>如今時刻是:<h1 style="color:red">{{current_date}}</h1></body></html>')
      #t=get_template('current_datetime.html')
#     c=Context({'current_date':now})
#     html=t.render(c)
#     return HttpResponse(html)

#另外一種寫法(推薦)

def current_time(req):

    now=datetime.datetime.now()

    return render(req, 'current_datetime.html', {'current_date':now})
Django模板使用比較

4.1.2 深度變量的查找(萬能的句點號)

在到目前爲止的例子中,咱們經過 context 傳遞的簡單參數值主要是字符串,然而,模板系統可以很是簡潔地處理更加複雜的數據結構,例如list、dictionary和自定義的對象。在 Django 模板中遍歷複雜數據結構的關鍵是句點字符 (.)。

#最好是用幾個例子來講明一下。
# 首先,句點可用於訪問列表索引,例如:

>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

#假設你要向模板傳遞一個 Python 字典。 要經過字典鍵訪問該字典的值,可以使用一個句點:
>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'

#一樣,也能夠經過句點來訪問對象的屬性。 比方說, Python 的 datetime.date 對象有
#year 、 month 和 day 幾個屬性,你一樣能夠在模板中使用句點來訪問這些屬性:

>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

# 這個例子使用了一個自定義的類,演示了經過實例變量加一點(dots)來訪問它的屬性,這個方法適
# 用於任意的對象。
>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

# 點語法也能夠用來引用對象的方法。 例如,每一個 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中能夠使用一樣的句點語法來調用它們:
>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

# 注意這裏調用方法時並* 沒有* 使用圓括號 並且也沒法給該方法傳遞參數;你只能調用不需參數的
# 方法。
句點號查找實例

4.1.3 變量的過濾器(filter)的使用

#語法格式:      {{obj|filter:param}}
 
# 1  add          :   給變量加上相應的值
# 2  addslashes   :    給變量中的引號前加上斜線
# 3  capfirst     :    首字母大寫
# 4  cut          :   從字符串中移除指定的字符
# 5  date         :   格式化日期字符串
# 6  default      :   若是值是False,就替換成設置的默認值,不然就是用原本的值
# 7  default_if_none:  若是值是None,就替換成設置的默認值,不然就使用原本的值
 
 
#實例:
 
#value1="aBcDe"
{{ value1|upper }}<br>
 
#value2=5
{{ value2|add:3 }}<br>
 
#value3='he  llo wo r ld'
{{ value3|cut:' ' }}<br>
 
#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}<br>
 
#value5=[]
{{ value5|default:'空的' }}<br>
 
#value6='<a href="#">跳轉</a>'
 
{{ value6 }}
 
{% autoescape off %}
  {{ value6 }}
{% endautoescape %}
 
{{ value6|safe }}<br>
 
{{ value6|striptags }}
 
#value7='1234'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>
 
#value8='http://www.baidu.com/?a=1&b=3'
{{ value8|urlencode }}<br>
    value9='hello I am yuan'
filter過濾器使用說明

4.1.4自定義filter和simple_tag

a、在app中建立templatetags模塊(必須的)

b、建立任意 .py 文件,如:my_tags.py

from django import template
from django.utils.safestring import mark_safe

register = template.Library()   #register的名字是固定的,不可改變


@register.filter
def filter_multi(v1,v2):
    return  v1 * v2


@register.simple_tag
def simple_tag_multi(v1,v2):
    return  v1 * v2


@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    return mark_safe(result)
自定義過濾器

c、在使用自定義simple_tag和filter的html文件中導入以前建立的 my_tags.py :{% load my_tags %}

d、使用simple_tag和filter(如何調用)

-------------------------------.html
{% load my_tags %}   #首行
    
    
 # num=12
{{ num|filter_multi:2 }} #24

{{ num|filter_multi:"[22,333,4444]" }}

{% simple_tag_multi 2 5 %}  參數不限,但不能放在if for語句中
{% simple_tag_multi num 5 %}
調用自定義過濾器

e、在settings中的INSTALLED_APPS配置當前app,否則django沒法找到自定義的simple_tag.

注意:

filter能夠用在if等語句後,simple_tag不能夠

{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}
View Code

4.2 標籤(tag)的使用(使用大括號和百分比的組合來表示使用tag)

語法格式:    {% tags %}

{% if %} 的使用 

{% if %}標籤計算一個變量值,若是是「true」,即它存在、不爲空而且不是false的boolean值,系統則會顯示{% if %}和{% endif %}間的全部內容

{% if num >= 100 and 8 %}

    {% if num > 200 %}
        <p>num大於200</p>
    {% else %}
        <p>num大於100小於200</p>
    {% endif %}

{% elif num < 100%}
    <p>num小於100</p>

{% else %}
    <p>num等於100</p>

{% endif %}



{% if %} 標籤接受and,or或者not來測試多個變量值或者否認一個給定的變量
{% if %} 標籤不容許同一標籤裏同時出現and和or,不然邏輯容易產生歧義,例以下面的標籤是不合法的:

{% if obj1 and obj2 or obj3 %} 
{% if%}標籤使用

{% for %}的使用

{% for %}標籤容許你按順序遍歷一個序列中的各個元素,每次循環模板系統都會渲染{% for %}和{% endfor %}之間的全部內容

<ul>
{% for obj in list %}
    <li>{{ obj.name }}</li>
{% endfor %}
</ul>


#在標籤裏添加reversed來反序循環列表:

    {% for obj in list reversed %}
    ...
    {% endfor %}

#{% for %}標籤能夠嵌套:

    {% for country in countries %}
        <h1>{{ country.name }}</h1>
        <ul>
         {% for city in country.city_list %}
            <li>{{ city }}</li>
         {% endfor %}
        </ul>
    {% endfor %}


#系統不支持中斷循環,系統也不支持continue語句,{% for %}標籤內置了一個forloop模板變量,
#這個變量含有一些屬性能夠提供給你一些關於循環的信息

1,forloop.counter表示循環的次數,它從1開始計數,第一次循環設爲1:

    {% for item in todo_list %}
        <p>{{ forloop.counter }}: {{ item }}</p>
    {% endfor %}
2,forloop.counter0 相似於forloop.counter,但它是從0開始計數,第一次循環設爲0
3,forloop.revcounter
4,forloop.revcounter0
5,forloop.first當第一次循環時值爲True,在特別狀況下頗有用:

    
    {% for object in objects %}   
         {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}   
         {{ object }}   
        </li>  
    {% endfor %}  
    
# 富有魔力的forloop變量只能在循環中獲得,當模板解析器到達{% endfor %}時forloop就消失了
# 若是你的模板context已經包含一個叫forloop的變量,Django會用{% for %}標籤替代它
# Django會在for標籤的塊中覆蓋你定義的forloop變量的值
# 在其餘非循環的地方,你的forloop變量仍然可用


#{% empty %}

{{li }}
      {%  for i in li %}
          <li>{{ forloop.counter0 }}----{{ i }}</li>
      {% empty %}
          <li>this is empty!</li>
      {% endfor %}

#         [11, 22, 33, 44, 55]
#            0----11
#            1----22
#            2----33
#            3----44
#            4----55
{% for %}標籤使用

csrf_token標籤

用於生成csrf_token的標籤,用於防治跨站攻擊驗證。 其實,這裏是會生成一個input標籤,和其餘表單標籤一塊兒提交給後臺的。

{% url %}

引用路由配置的地址

<form action="{% url "bieming"%}" >
          <input type="text">
          <input type="submit"value="提交">
          {%csrf_token%}
</form>
{% url %}標籤使用

{% with %}

用更簡單的變量名替代複雜的變量名

{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
{% with %}標籤使用

4.3 extend模板繼承

步驟:

a、新建 base.html,放在template目錄下

b、使用模板標籤: {% block %}分塊,用於子模板繼承 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    {% block content %}{% endblock %}
    {% block footer %}
    <hr>
    <p>Thanks for visiting my site.</p>
    {% endblock %}
</body>
</html>
新建母版文件(base.html)

c、子模版集成(即重載block塊)

{% extends "base.html" %}
 
{% block title %}The current time{% endblock %}
 
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
子母版繼承

繼承的常見三層法:

   <1> 建立 base.html 模板,在其中定義站點的主要外觀感覺。 這些都是不常修改甚至從不修改的部分。
   <2> 爲網站的每一個區域建立 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。這些模板對base.html 進行拓展,
       幷包含區域特定的風格與設計。
   <3> 爲每種類型的頁面建立獨立的模板,例如論壇頁面或者圖片庫。 這些模板拓展相應的區域模板。
繼承的常見三層法

模板繼承的一些訣竅

<1>若是在模板中使用 {% extends %} ,必須保證其爲模板中的第一個模板標記。 不然,模板繼承將不起做用。

 <2>通常來講,基礎模板中的 {% block %} 標籤越多越好。 記住,子模板沒必要定義父模板中全部的代碼塊,所以
    你能夠用合理的缺省值對一些代碼塊進行填充,而後只對子模板所需的代碼塊進行(重)定義。 俗話說,鉤子越
    多越好。

 <3>若是發覺本身在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。
    若是你須要訪問父模板中的塊的內容,使用 {{ block.super }}這個標籤吧,這一個魔法變量將會表現出父模
    板中的內容。 若是隻想在上級代碼塊基礎上添加內容,而不是所有重載,該變量就顯得很是有用了。

 <4>不容許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是由於block 標籤的工做方式是雙向的。
    也就是說,block 標籤不只挖了一個要填的坑,也定義了在父模板中這個坑所填充的內容。若是模板中出現了兩個
    相同名稱的 {% block %} 標籤,父模板將無從得知要使用哪一個塊的內容。
模板繼承的訣竅

 返回頂部

 5、Views視圖

django中請求處理方式有2種:FBV 和 CBV

 5.1 CBV(class base views)

CBV(class base views) 就是在視圖裏使用類處理請求。

5.1.1. 基本使用

使用方法爲:urls.py 修改成以下:

from mytest import views

urlpatterns = [
    # url(r‘^index/‘, views.index),
    url(r‘^index/‘, views.Index.as_view()),
]
#注:url(r‘^index/‘, views.Index.as_view()),  是固定用法。
CBV中urls.py文件

views.py 修改成以下:

from django.views import View

class Index(View):

    def dispatch(self, request, *args, **kwargs):
        res = super(AuthView,self).dispatch(request, *args, **kwargs)
        return res

    def get(self, req):
        print(‘method is :‘ + req.method)
        return render(req, ‘index.html‘)

    def post(self, req):
        print(‘method is :‘ + req.method)
        return render(req, ‘index.html‘)
#注:1、類要繼承 View ,類中函數名必須小寫。
        2、類中首先執行的是  dispatch方法
CBV中views.py文件

5.1.2. 基於dispatch和繼承實現用戶登陸代碼

  一、寫一個登陸驗證類

class AuthView(object):
    def dispatch(self, request, *args, **kwargs):
        if not request.session.get('user_info'):
            return redirect('/login.html')
        res = super(AuthView,self).dispatch(request, *args, **kwargs)
        return res
CBV實現用戶登陸驗證

二、修改views.py並繼承驗證

from django.views import View

class Index(AuthView,View):

    def get(self, req):
        print(‘method is :‘ + req.method)
        return render(req, ‘index.html‘)

    def post(self, req):
        print(‘method is :‘ + req.method)
        return render(req, ‘index.html‘)
#繼承AuthView驗證類便可
CBV實現用戶登陸驗證

5.1.3. 裝飾器

一、get,post方法上,給任意函數添加

from django.utils.decorators import method_decorator
from django.views import View

def test(func):
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner


class Index(View):
    def dispatch(self, request, *args, **kwargs):
        res = super(AuthView,self).dispatch(request, *args, **kwargs)
        return res
    
    @method_decorator(test)
    def get(self, req):
        print(‘method is :‘ + req.method)
        return render(req, ‘index.html‘)

    @method_decorator(test
    def post(self, req):
        print(‘method is :‘ + req.method)
        return render(req, ‘index.html‘)

     
get,post方法上

二、dispatch方法上,同時給get、post添加裝飾器

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from django.utils.decorators import method_decorator

class LoginView(View):
    @method_decorator(test)
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)

    def get(self,request):
        return render(request,'login.html')


    def post(self,request):
    return redirect('/index.html')
dispatch方法上

三、類上

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from django.utils.decorators import method_decorator

@method_decorator(test,name='get')
class LoginView(View):

    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)

    def get(self,request):
        return render(request,'login.html')

    def post(self,request):
        return redirect('/index.html')
#類上添加裝飾器,name後面有哪些函數,哪些函數就添加了裝飾器 
類上

四、特殊:CSRF Token只能加到dispatch

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from django.utils.decorators import method_decorator

from django.views.decorators.csrf import csrf_exempt,csrf_protect


class LoginView(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(LoginView,self).dispatch(request, *args, **kwargs)

    def get(self,request):
        return render(request,'login.html')

    def post(self,request):
        return redirect('/index.html')
#csrf_exempt 其餘須要,加此裝飾器的不須要
#csrf_protect 其餘不須要須要,加此裝飾器的須要
CSRF Token只能加到dispatch

5.2 FBV(function base views)

FBV(function base views) 就是在視圖裏使用函數處理請求。

5.2.1. 基本使用

一、使用方法爲:urls.py 修改成以下:

from django.conf.urls import url, include

# from django.contrib import admin
from mytest import views

urlpatterns = [
    # url(r‘^admin/‘, admin.site.urls),
    url(r‘^index/‘, views.index),
]
FBV中urls.py文件

二、views.py 修改成以下:

from django.shortcuts import render


def index(req):
    if req.method == ‘POST‘:
        print(‘method is :‘ + req.method)
    elif req.method == ‘GET‘:
        print(‘method is :‘ + req.method)
    return render(req, ‘index.html‘)

#注意此處定義的是函數【def index(req):】
FBV中views.py文件

三、index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
    <form action="" method="post">
        <input type="text" name="A" />
        <input type="submit" name="b" value="提交" />
    </form>
</body>
</html>
index.html

5.2.2快捷函數

render函數

---------------render(request, template_name[, context])

結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 對象。

參數:
     request: 用於生成響應的請求對象。

     template_name:要使用的模板的完整名稱,可選的參數

     context:添加到模板上下文的一個字典。默認是一個空字典。若是字典中的某個值是可調用的,視圖將在渲染模板以前調用它。

     content_type:生成的文檔要使用的MIME類型。默認爲DEFAULT_CONTENT_TYPE 設置的值。

     status:響應的狀態碼。默認爲200。
複製代碼
render函數

 redirect函數

-----------------------------------url.py

 url(r"login",   views.login),
 url(r"yuan_back",   views.yuan_back),

-----------------------------------views.py
def login(req):
    if req.method=="POST":
        if 1:
            # return redirect("/yuan_back/")
            name="yuanhao"

            return render(req,"my backend.html",locals())

    return render(req,"login.html",locals())


def yuan_back(req):

    name="苑昊"

    return render(req,"my backend.html",locals())

-----------------------------------login.html

<form action="/login/" method="post">
    <p>姓名<input type="text" name="username"></p>
    <p>性別<input type="text" name="sex"></p>
    <p>郵箱<input type="text" name="email"></p>
    <p><input type="submit" value="submit"></p>
</form>
-----------------------------------my backend.html
<h1>用戶{{ name }}你好</h1>

#總結: render和redirect的區別:
#   1 if render的頁面須要模板語言渲染,須要的將數據庫的數據加載到html,那麼全部的這一部分
#     除了寫在yuan_back的視圖函數中,必須還要寫在login中,代碼重複,沒有解耦.

#   2 the most important: url沒有跳轉到/yuan_back/,而是還在/login/,因此當刷新後
#     又得從新登陸.
redirect函數

返回頂部

6、Model&ORM

6.一、建立表

一、基本結構

from django.db import models
   
class userinfo(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()
    memo = models.TextField()
基本結構
    AutoField(Field)
        - int自增列,必須填入參數 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必須填入參數 primary_key=True

        注:當model中若是沒有自增列,則自動會建立一個列名爲id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自動建立一個列名爲id的且爲自增的整數列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定義自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整數 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整數 0 ~ 32767
    IntegerField(Field)
        - 整數列(有符號的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整數 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807

    自定義無符號整數字段

        class UnsignedIntegerField(models.IntegerField):
            def db_type(self, connection):
                return 'integer UNSIGNED'

        PS: 返回值爲字段在數據庫中的屬性,Django字段默認的值爲:
            'AutoField': 'integer AUTO_INCREMENT',
            'BigAutoField': 'bigint AUTO_INCREMENT',
            'BinaryField': 'longblob',
            'BooleanField': 'bool',
            'CharField': 'varchar(%(max_length)s)',
            'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
            'DateField': 'date',
            'DateTimeField': 'datetime',
            'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
            'DurationField': 'bigint',
            'FileField': 'varchar(%(max_length)s)',
            'FilePathField': 'varchar(%(max_length)s)',
            'FloatField': 'double precision',
            'IntegerField': 'integer',
            'BigIntegerField': 'bigint',
            'IPAddressField': 'char(15)',
            'GenericIPAddressField': 'char(39)',
            'NullBooleanField': 'bool',
            'OneToOneField': 'integer',
            'PositiveIntegerField': 'integer UNSIGNED',
            'PositiveSmallIntegerField': 'smallint UNSIGNED',
            'SlugField': 'varchar(%(max_length)s)',
            'SmallIntegerField': 'smallint',
            'TextField': 'longtext',
            'TimeField': 'time',
            'UUIDField': 'char(32)',

    BooleanField(Field)
        - 布爾值類型

    NullBooleanField(Field):
        - 能夠爲空的布爾值

    CharField(Field)
        - 字符類型
        - 必須提供max_length參數, max_length表示字符長度

    TextField(Field)
        - 文本類型

    EmailField(CharField):
        - 字符串類型,Django Admin以及ModelForm中提供驗證機制

    IPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制

    GenericIPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
        - 參數:
            protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 若是指定爲True,則輸入::ffff:192.0.2.1時候,可解析爲192.0.2.1,開啓刺功能,須要protocol="both"

    URLField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 URL

    SlugField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下劃線、鏈接符(減號)

    CommaSeparatedIntegerField(CharField)
        - 字符串類型,格式必須爲逗號分割的數字

    UUIDField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
        - 參數:
                path,                      文件夾路徑
                match=None,                正則匹配
                recursive=False,           遞歸下面的文件夾
                allow_files=True,          容許文件
                allow_folders=False,       容許文件夾

    FileField(Field)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
            width_field=None,   上傳圖片的高度保存的數據庫字段名(字符串)
            height_field=None   上傳圖片的寬度保存的數據庫字段名(字符串)

    DateTimeField(DateField)
        - 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 時間格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 長整數,時間間隔,數據庫中按照bigint存儲,ORM中獲取的值爲datetime.timedelta類型

    FloatField(Field)
        - 浮點型

    DecimalField(Field)
        - 10進制小數
        - 參數:
            max_digits,小數總長度
            decimal_places,小數位長度

    BinaryField(Field)
        - 二進制類型
字段
    null                數據庫中字段是否能夠爲空
    db_column           數據庫中字段的列名
    db_tablespace
    default             數據庫中字段的默認值
    primary_key         數據庫中字段是否爲主鍵
    db_index            數據庫中字段是否能夠創建索引
    unique              數據庫中字段是否能夠創建惟一索引
    unique_for_date     數據庫中字段【日期】部分是否能夠創建惟一索引
    unique_for_month    數據庫中字段【月】部分是否能夠創建惟一索引
    unique_for_year     數據庫中字段【年】部分是否能夠創建惟一索引

    verbose_name        Admin中顯示的字段名稱
    blank               Admin中是否容許用戶輸入爲空
    editable            Admin中是否能夠編輯
    help_text           Admin中該字段的提示信息
    choices             Admin中顯示選擇框的內容,用不變更的數據放在內存中從而避免跨表操做
                        如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

    error_messages      自定義錯誤信息(字典類型),從而定製想要顯示的錯誤信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                        如:{'null': "不能爲空.", 'invalid': '格式錯誤'}

    validators          自定義錯誤驗證(列表類型),從而定製想要的驗證規則
                        from django.core.validators import RegexValidator
                        from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
                        MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                        如:
                            test = models.CharField(
                                max_length=32,
                                error_messages={
                                    'c1': '優先錯信息1',
                                    'c2': '優先錯信息2',
                                    'c3': '優先錯信息3',
                                },
                                validators=[
                                    RegexValidator(regex='root_\d+', message='錯誤了', code='c1'),
                                    RegexValidator(regex='root_112233\d+', message='又錯誤了', code='c2'),
                                    EmailValidator(message='又錯誤了', code='c3'), ]
                            )
參數
    class UserInfo(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32)
        class Meta:
            # 數據庫中生成的表名稱 默認 app名稱 + 下劃線 + 類名
            db_table = "table_name"

            # 聯合索引
            index_together = [
                ("pub_date", "deadline"),
            ]

            # 聯合惟一索引
            unique_together = (("driver", "restaurant"),)

            # admin中顯示的表名稱
            verbose_name

            # verbose_name加s
            verbose_name_plural
        
    更多:https://docs.djangoproject.com/en/1.10/ref/models/options/
元信息
    1.觸發Model中的驗證和錯誤提示有兩種方式:
        a. Django Admin中的錯誤信息會優先根據Admiin內部的ModelForm錯誤信息提示,若是都成功,纔來檢查Model的字段並顯示指定錯誤信息
        b. 調用Model對象的 clean_fields 方法,如:
            # models.py
            class UserInfo(models.Model):
                nid = models.AutoField(primary_key=True)
                username = models.CharField(max_length=32)

                email = models.EmailField(error_messages={'invalid': '格式錯了.'})

            # views.py
            def index(request):
                obj = models.UserInfo(username='11234', email='uu')
                try:
                    print(obj.clean_fields())
                except Exception as e:
                    print(e)
                return HttpResponse('ok')

           # Model的clean方法是一個鉤子,可用於定製操做,如:上述的異常處理。

    2.Admin中修改錯誤提示
        # admin.py
        from django.contrib import admin
        from model_club import models
        from django import forms


        class UserInfoForm(forms.ModelForm):
            username = forms.CharField(error_messages={'required': '用戶名不能爲空.'})
            email = forms.EmailField(error_messages={'invalid': '郵箱格式錯誤.'})
            age = forms.IntegerField(initial=1, error_messages={'required': '請輸入數值.', 'invalid': '年齡必須爲數值.'})

            class Meta:
                model = models.UserInfo
                # fields = ('username',)
                fields = "__all__"


        class UserInfoAdmin(admin.ModelAdmin):
            form = UserInfoForm


        admin.site.register(models.UserInfo, UserInfoAdmin)
拓展知識

二、連表結構

  • 一對多:models.ForeignKey(其餘表)
  • 多對多:models.ManyToManyField(其餘表)
  • 一對一:models.OneToOneField(其餘表)
應用場景:

一對多:當一張表中建立一行數據時,有一個單選的下拉框(能夠被重複選擇)
例如:建立用戶信息時候,須要選擇一個用戶類型【普通用戶】【金牌用戶】【鉑金用戶】等。
多對多:在某表中建立一行數據是,有一個能夠多選的下拉框
例如:建立用戶信息,須要爲用戶指定多個愛好
一對一:在某表中建立一行數據時,有一個單選的下拉框(下拉框中的內容被用過一次就消失了
例如:原有含10列數據的一張表保存相關信息,通過一段時間以後,10列沒法知足需求,須要爲原來的表再添加5列數據
應用場景:
    ForeignKey(ForeignObject) # ForeignObject(RelatedField)
        to,                         # 要進行關聯的表名
        to_field=None,              # 要關聯的表中的字段名稱
        on_delete=None,             # 當刪除關聯表中的數據時,當前表與其關聯的行的行爲
                                        - models.CASCADE,刪除關聯數據,與之關聯也刪除
                                        - models.DO_NOTHING,刪除關聯數據,引起錯誤IntegrityError
                                        - models.PROTECT,刪除關聯數據,引起錯誤ProtectedError
                                        - models.SET_NULL,刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空)
                                        - models.SET_DEFAULT,刪除關聯數據,與之關聯的值設置爲默認值(前提FK字段須要設置默認值)
                                        - models.SET,刪除關聯數據,
                                                      a. 與之關聯的值設置爲指定值,設置:models.SET(值)
                                                      b. 與之關聯的值設置爲可執行對象的返回值,設置:models.SET(可執行對象)

                                                        def func():
                                                            return 10

                                                        class MyModel(models.Model):
                                                            user = models.ForeignKey(
                                                                to="User",
                                                                to_field="id"
                                                                on_delete=models.SET(func),)
        related_name=None,          # 反向操做時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all()
        related_query_name=None,    # 反向操做時,使用的鏈接前綴,用於替換【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
        limit_choices_to=None,      # 在Admin或ModelForm中顯示關聯數據時,提供的條件:
                                    # 如:
                                            - limit_choices_to={'nid__gt': 5}
                                            - limit_choices_to=lambda : {'nid__gt': 5}

                                            from django.db.models import Q
                                            - limit_choices_to=Q(nid__gt=10)
                                            - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                                            - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
        db_constraint=True          # 是否在數據庫中建立外鍵約束
        parent_link=False           # 在Admin中是否顯示關聯數據


    OneToOneField(ForeignKey)
        to,                         # 要進行關聯的表名
        to_field=None               # 要關聯的表中的字段名稱
        on_delete=None,             # 當刪除關聯表中的數據時,當前表與其關聯的行的行爲

                                    ###### 對於一對一 ######
                                    # 1. 一對一其實就是 一對多 + 惟一索引
                                    # 2.當兩個類之間有繼承關係時,默認會建立一個一對一字段
                                    # 以下會在A表中額外增長一個c_ptr_id列且惟一:
                                            class C(models.Model):
                                                nid = models.AutoField(primary_key=True)
                                                part = models.CharField(max_length=12)

                                            class A(C):
                                                id = models.AutoField(primary_key=True)
                                                code = models.CharField(max_length=1)

    ManyToManyField(RelatedField)
        to,                         # 要進行關聯的表名
        related_name=None,          # 反向操做時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all()
        related_query_name=None,    # 反向操做時,使用的鏈接前綴,用於替換【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
        limit_choices_to=None,      # 在Admin或ModelForm中顯示關聯數據時,提供的條件:
                                    # 如:
                                            - limit_choices_to={'nid__gt': 5}
                                            - limit_choices_to=lambda : {'nid__gt': 5}

                                            from django.db.models import Q
                                            - limit_choices_to=Q(nid__gt=10)
                                            - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                                            - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
        symmetrical=None,           # 僅用於多對多自關聯時,symmetrical用於指定內部是否建立反向操做的字段
                                    # 作以下操做時,不一樣的symmetrical會有不一樣的可選字段
                                        models.BB.objects.filter(...)

                                        # 可選字段有:code, id, m1
                                            class BB(models.Model):

                                            code = models.CharField(max_length=12)
                                            m1 = models.ManyToManyField('self',symmetrical=True)

                                        # 可選字段有: bb, code, id, m1
                                            class BB(models.Model):

                                            code = models.CharField(max_length=12)
                                            m1 = models.ManyToManyField('self',symmetrical=False)

        through=None,               # 自定義第三張表時,使用字段用於指定關係表
        through_fields=None,        # 自定義第三張表時,使用字段用於指定關係表中那些字段作多對多關係表
                                        from django.db import models

                                        class Person(models.Model):
                                            name = models.CharField(max_length=50)

                                        class Group(models.Model):
                                            name = models.CharField(max_length=128)
                                            members = models.ManyToManyField(
                                                Person,
                                                through='Membership',
                                                through_fields=('group', 'person'),
                                            )

                                        class Membership(models.Model):
                                            group = models.ForeignKey(Group, on_delete=models.CASCADE)
                                            person = models.ForeignKey(Person, on_delete=models.CASCADE)
                                            inviter = models.ForeignKey(
                                                Person,
                                                on_delete=models.CASCADE,
                                                related_name="membership_invites",
                                            )
                                            invite_reason = models.CharField(max_length=64)
        db_constraint=True,         # 是否在數據庫中建立外鍵約束
        db_table=None,              # 默認建立第三張表時,數據庫中表的名稱
字段以及參數

6.二、操做表

一、基本操做

# 增
    #
    # models.Tb1.objects.create(c1='xx', c2='oo')  增長一條數據,能夠接受字典類型數據 **kwargs

    # obj = models.Tb1(c1='xx', c2='oo')
    # obj.save()

    # 查
    #
    # models.Tb1.objects.get(id=123)         # 獲取單條數據,不存在則報錯(不建議)
    # models.Tb1.objects.all()               # 獲取所有
    # models.Tb1.objects.filter(name='seven') # 獲取指定條件的數據

    # 刪
    #
    # models.Tb1.objects.filter(name='seven').delete() # 刪除指定條件的數據

    # 改
    # models.Tb1.objects.filter(name='seven').update(gender='0')  # 將指定條件的數據更新,均支持 **kwargs
    # obj = models.Tb1.objects.get(id=1)
    # obj.c1 = '111'
    # obj.save()                                                 # 修改單條數據
    # save是更改全部字段,即便更改一個字段,也會將全部字段從新賦值, 不推薦
    # update更改,只更改修改的字段,推薦使用

    # update方式修改不能用get的緣由是:update是QuerySet對象的方法,get返回的是一個model對象,它沒有update方法,而filter返回的是一個QuerySet對象(filter裏面的條件可能有多個條件符合,好比name='alvin',可能有兩個name='alvin'的行數據)
基本操做

二、查詢相關API

# 查詢相關API:

#  <1>filter(**kwargs):      它包含了與所給篩選條件相匹配的對象

#  <2>all():                 查詢全部結果

#  <3>get(**kwargs):         返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。

#-----------下面的方法都是對查詢的結果再進行處理:好比 objects.filter.values()--------

#  <4>values(*field):        返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列 model的實例化對象,而是一個可迭代的字典序列
                                     
#  <5>exclude(**kwargs):     它包含了與所給篩選條件不匹配的對象

#  <6>order_by(*field):      對查詢結果排序

#  <7>reverse():             對查詢結果反向排序

#  <8>distinct():            從返回結果中剔除重複紀錄

#  <9>values_list(*field):   它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列

#  <10>count():              返回數據庫中匹配查詢(QuerySet)的對象數量。

#  <11>first():               返回第一條記錄

#  <12>last():                返回最後一條記錄

#  <13>exists():             若是QuerySet包含數據,就返回True,不然返回False
查詢相關API

三、進階操做(了不得的雙下劃線)

利用雙下劃線將字段和對應的操做鏈接起來

        # 獲取個數
        #
        # models.Tb1.objects.filter(name='seven').count()

        # 大於,小於
        #
        # models.Tb1.objects.filter(id__gt=1)              # 獲取id大於1的值
        # models.Tb1.objects.filter(id__gte=1)              # 獲取id大於等於1的值
        # models.Tb1.objects.filter(id__lt=10)             # 獲取id小於10的值
        # models.Tb1.objects.filter(id__lte=10)             # 獲取id小於10的值
        # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值

        # in
        #
        # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 獲取id等於十一、2二、33的數據
        # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

        # isnull
        # Entry.objects.filter(pub_date__isnull=True)

        # contains
        #
        # models.Tb1.objects.filter(name__contains="ven")
        # models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
        # models.Tb1.objects.exclude(name__icontains="ven")

        # range
        #
        # models.Tb1.objects.filter(id__range=[1, 2])   # 範圍bettwen and

        # 其餘相似
        #
        # startswith,istartswith, endswith, iendswith,

        # order by
        #
        # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
        # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

        # group by
        #
        # from django.db.models import Count, Min, Max, Sum
        # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
        # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

        # limit 、offset
        #
        # models.Tb1.objects.all()[10:20]

        # regex正則匹配,iregex 不區分大小寫
        #
        # Entry.objects.get(title__regex=r'^(An?|The) +')
        # Entry.objects.get(title__iregex=r'^(an?|the) +')

        # date
        #
        # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
        # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

        # year
        #
        # Entry.objects.filter(pub_date__year=2005)
        # Entry.objects.filter(pub_date__year__gte=2005)

        # month
        #
        # Entry.objects.filter(pub_date__month=12)
        # Entry.objects.filter(pub_date__month__gte=6)

        # day
        #
        # Entry.objects.filter(pub_date__day=3)
        # Entry.objects.filter(pub_date__day__gte=3)

        # week_day
        #
        # Entry.objects.filter(pub_date__week_day=2)
        # Entry.objects.filter(pub_date__week_day__gte=2)

        # hour
        #
        # Event.objects.filter(timestamp__hour=23)
        # Event.objects.filter(time__hour=5)
        # Event.objects.filter(timestamp__hour__gte=12)

        # minute
        #
        # Event.objects.filter(timestamp__minute=29)
        # Event.objects.filter(time__minute=46)
        # Event.objects.filter(timestamp__minute__gte=29)

        # second
        #
        # Event.objects.filter(timestamp__second=31)
        # Event.objects.filter(time__second=2)
        # Event.objects.filter(timestamp__second__gte=31)
進階操做

四、連表操做(了不得的雙下劃線)

利用雙下劃線和 _set 將表之間的操做鏈接起來

class UserProfile(models.Model):
    user_info = models.OneToOneField('UserInfo')
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=64)

    def __unicode__(self):
        return self.username


class UserInfo(models.Model):
    user_type_choice = (
        (0, u'普通用戶'),
        (1, u'高級用戶'),
    )
    user_type = models.IntegerField(choices=user_type_choice)
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    address = models.CharField(max_length=128)

    def __unicode__(self):
        return self.name


class UserGroup(models.Model):

    caption = models.CharField(max_length=64)

    user_info = models.ManyToManyField('UserInfo')

    def __unicode__(self):
        return self.caption


class Host(models.Model):
    hostname = models.CharField(max_length=64)
    ip = models.GenericIPAddressField()
    user_group = models.ForeignKey('UserGroup')

    def __unicode__(self):
        return self.hostname
表結構實例
user_info_obj = models.UserInfo.objects.filter(id=1).first()
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password
 
user_info_obj = models.UserInfo.objects.filter(id=1).values('email', 'userprofile__username').first()
print user_info_obj.keys()
print user_info_obj.values()
一對一操做
相似一對一
1、搜索條件使用 __ 鏈接
二、獲取值時使用 .    鏈接
一對多
user_info_obj = models.UserInfo.objects.get(name=u'武沛齊')
user_info_objs = models.UserInfo.objects.all()
 
group_obj = models.UserGroup.objects.get(caption='CEO')
group_objs = models.UserGroup.objects.all()
 
# 添加數據
#group_obj.user_info.add(user_info_obj)
#group_obj.user_info.add(*user_info_objs)
 
# 刪除數據
#group_obj.user_info.remove(user_info_obj)
#group_obj.user_info.remove(*user_info_objs)
 
# 添加數據
#user_info_obj.usergroup_set.add(group_obj)
#user_info_obj.usergroup_set.add(*group_objs)
 
# 刪除數據
#user_info_obj.usergroup_set.remove(group_obj)
#user_info_obj.usergroup_set.remove(*group_objs)
 
# 獲取數據
#print group_obj.user_info.all()
#print group_obj.user_info.all().filter(id=1)
 
# 獲取數據
#print user_info_obj.usergroup_set.all()
#print user_info_obj.usergroup_set.all().filter(caption='CEO')
#print user_info_obj.usergroup_set.all().filter(caption='DBA')
多對多操做

五、聚合查詢和分組查詢

<1> aggregate(*args,**kwargs):

   經過對QuerySet進行計算,返回一個聚合值的字典。aggregate()中每個參數都指定一個包含在字典中的返回值。即在查詢集上生成聚合。

from django.db.models import Avg,Min,Sum,Max

從整個查詢集生成統計值。好比,你想要計算全部在售書的平均價錢。Django的查詢語法提供了一種方式描述全部
圖書的集合。

>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

aggregate()子句的參數描述了咱們想要計算的聚合值,在這個例子中,是Book模型中price字段的平均值

aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的
標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。若是你想要爲聚合值指定
一個名稱,能夠向聚合子句提供它:
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}


若是你也想知道全部圖書價格的最大值和最小值,能夠這樣查詢:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
aggregate查詢

<2> annotate(*args,**kwargs):

   能夠經過計算查詢結果中每個對象所關聯的對象集合,從而得出總計值(也能夠是平均值或總和),即爲查詢集的每一項生成聚合。

#1、統計每個出版社的最便宜的書
方法一:
publishList=Publish.objects.annotate(MinPrice=Min("book__price"))
 
for publish_obj in publishList:
    print(publish_obj.name,publish_obj.MinPrice)
方法二:
queryResult=Book.objects.values("publish__name").annotate(MinPrice=Min('price'))
#2、統計每一本以py開頭的書籍的做者個數
 queryResult=Book.objects
           .filter(title__startswith="Py")
           .annotate(num_authors=Count('authors'))
#3、 統計不止一個做者的圖書:

queryResult=Book.objects
          .annotate(num_authors=Count('authors'))
          .filter(num_authors__gt=1)
#4、根據一本圖書做者數量的多少對查詢集 QuerySet進行排序:

Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

 #5、查詢各個做者出的書的總價格:
    # 按author表的全部字段 group by
        queryResult=Author.objects
              .annotate(SumPrice=Sum("book__price"))
              .values_list("name","SumPrice")
        print(queryResult)
    
    #按authors__name group by
        queryResult2=Book.objects.values("authors__name")
              .annotate(SumPrice=Sum("price"))
              .values_list("authors__name","SumPrice")
       print(queryResult2)
分組:annotate()查詢

六、F查詢和Q查詢

# F 使用查詢條件的值,專門取對象中某列值的操做

    # from django.db.models import F
    # models.Tb1.objects.update(num=F('num')+1)


# Q 構建搜索條件
    from django.db.models import Q

    #1 Q對象(django.db.models.Q)能夠對關鍵字參數進行封裝,從而更好地應用多個查詢
    q1=models.Book.objects.filter(Q(title__startswith='P')).all()
    print(q1)#[<Book: Python>, <Book: Perl>]

    # 2、能夠組合使用&,|操做符,當一個操做符是用於兩個Q的對象,它產生一個新的Q對象。
    Q(title__startswith='P') | Q(title__startswith='J')

    # 3、Q對象能夠用~操做符放在前面表示否認,也可容許否認與不否認形式的組合
    Q(title__startswith='P') | ~Q(pub_date__year=2005)

    # 4、應用範圍:

    # Each lookup function that takes keyword-arguments (e.g. filter(),
    #  exclude(), get()) can also be passed one or more Q objects as
    # positional (not-named) arguments. If you provide multiple Q object
    # arguments to a lookup function, the arguments will be 「AND」ed
    # together. For example:

    Book.objects.get(
        Q(title__startswith='P'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )

    #sql:
    # SELECT * from polls WHERE question LIKE 'P%'
    #     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

    # import datetime
    # e=datetime.date(2005,5,6)  #2005-05-06

    # 5、Q對象能夠與關鍵字參數查詢一塊兒使用,不過必定要把Q對象放在關鍵字參數查詢的前面。
    # 正確:
    Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        title__startswith='P')
    # 錯誤:
    Book.objects.get(
        question__startswith='P',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
F查詢和Q查詢

 返回頂部

 

7、Admin相關

django amdin是django提供的一個後臺管理頁面,改管理頁面提供完善的html和css,使得你在經過Model建立完數據庫表以後,就能夠對數據進行增刪改查,而使用django admin 則須要如下步驟:

  • 建立後臺管理員
  • 配置url
  • 註冊和配置django admin後臺管理頁面

一、建立後臺管理員

python manage.py createsuperuser

二、配置後臺管理url

url(r'^admin/', include(admin.site.urls))

三、註冊和配置django admin 後臺管理頁面

  a、在admin中執行以下配置

from django.contrib import admin
 
from app01 import  models
 
admin.site.register(models.UserType)
admin.site.register(models.UserInfo)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
配置admin,添加Model表

b、設置數據表名稱

class UserType(models.Model):
    name = models.CharField(max_length=50)
 
    class Meta:
        verbose_name = '用戶類型'
        verbose_name_plural = '用戶類型'
設置數據表名稱

c、一些經常使用的設置技巧(在admin.py文件中配置

list_display:     指定要顯示的字段
search_fields:  指定搜索的字段
list_filter:        指定列表過濾器
ordering:       指定排序字段
一些經常使用的設置技巧

d、打開表以後,設定默認顯示,須要在model中做以下配置(在admin.py文件中配置

class UserType(models.Model):
    name = models.CharField(max_length=50)
  
    def __unicode__(self):
        return self.name



from django.contrib import admin
from app01 import  models

class UserInfoAdmin(admin.ModelAdmin):
    list_display = ('username', 'password', 'email')
  
  
admin.site.register(models.UserType)
admin.site.register(models.UserInfo,UserInfoAdmin)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
設定默認顯示

e、爲數據表添加搜索功能(在admin.py文件中配置

from django.contrib import admin
  
from app01 import  models
  
class UserInfoAdmin(admin.ModelAdmin):
    list_display = ('username', 'password', 'email')
    search_fields = ('username', 'email')
  
admin.site.register(models.UserType)
admin.site.register(models.UserInfo,UserInfoAdmin)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
爲數據表添加搜索功能

f、添加快速過濾(在admin.py文件中配置

from django.contrib import admin
 
from app01 import  models
 
class UserInfoAdmin(admin.ModelAdmin):
    list_display = ('username', 'password', 'email')
    search_fields = ('username', 'email')
    list_filter = ('username', 'email')
     
 
 
admin.site.register(models.UserType)
admin.site.register(models.UserInfo,UserInfoAdmin)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
添加快速過濾

返回頂部

8、Http協議

8.1 HTTP概述

HTTPhypertext transport protocol),即超文本傳輸協議。這個協議詳細規定了瀏覽器和萬維網服務器之間互相通訊的規則。

HTTP就是一個通訊規則,通訊規則規定了客戶端發送給服務器的內容格式,也規定了服務器發送給客戶端的內容格式。其實咱們要學習的就是這個兩個格式!客戶端發送給服務器的格式叫請求協議;服務器發送給客戶端的格式叫響應協議

特色:

  • HTTP叫超文本傳輸協議,基於請求/響應模式的!
  • HTTP是無狀態協議。

URL:統一資源定位符,就是一個網址:協議名://域名:端口/路徑,例如:http://www.oldboy.cn:80/index.html

8.2 請求協議

請求協議的格式以下:

請求首行;  // 請求方式 請求路徑 協議和版本,例如:GET /index.html HTTP/1.1
請求頭信息;// 請求頭名稱:請求頭內容,即爲key:value格式,例如:Host:localhost
空行;     // 用來與請求體分隔開
請求體。   // GET沒有請求體,只有POST有請求體。爲一個個鍵值對

實例:
GET請求:
    "GET /index.html http1.1\r\nUser-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x6..\r\n\r\nAccept-Encoding:gzip\r\n\r\n"
POST請求:
    "POST /index.html http1.1\r\nUser-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x6..\r\n\r\nAccept-Encoding:gzip\r\n\r\nuser=cqz&hobby=lihao"

請求頭以\r\n劃分
請求體以\r\n\r\n劃分
請求協議格式

瀏覽器發送給服務器的內容就這個格式的,若是不是這個格式服務器將沒法解讀!在HTTP協議中,請求有不少請求方法,其中最爲經常使用的就是GETPOST

8.2.1 GET請求

HTTP默認的請求方法就是GET

* 沒有請求體
* 數據必須在1K以內!
* GET請求數據會暴露在瀏覽器的地址欄中

GET請求經常使用的操做:

1. 在瀏覽器的地址欄中直接給出URL,那麼就必定是GET請求
2. 點擊頁面上的超連接也必定是GET請求
3. 提交表單時,表單默認使用GET請求,但能夠設置爲POST
GET請求經常使用的操做:

8.2.2 POST請求

(1). 數據不會出如今地址欄中
(2). 數據的大小沒有上限
(3). 有請求體
(4). 請求體中若是存在中文,會使用URL編碼!
POST請求特色
咱們都知道Http協議中參數的傳輸是"key=value"這種簡直對形式的,若是要傳多個參數就須要用「&」符號對鍵值對進行分割。如"?name1=value1&name2=value2",這樣在服務端在收到這種字符串的時候,會用「&」分割出每個參數,而後再用「=」來分割出參數值。

 

針對「name1=value1&name2=value2」咱們來講一下客戶端到服務端的概念上解析過程: 
  上述字符串在計算機中用ASCII嗎表示爲: 
  6E616D6531 3D 76616C756531 26 6E616D6532 3D 76616C756532。 
   6E616D6531:name1 
   3D:= 
   76616C756531:value1 
   26:&
   6E616D6532:name2 
   3D:= 
   76616C756532:value2 
   服務端在接收到該數據後就能夠遍歷該字節流,首先一個字節一個字節的吃,當吃到3D這字節後,服務端就知道前面吃得字節表示一個key,再想後吃,若是遇到26,說明從剛纔吃的3D到26子節之間的是上一個key的value,以此類推就能夠解析出客戶端傳過來的參數。

   如今有這樣一個問題,若是個人蔘數值中就包含=或&這種特殊字符的時候該怎麼辦。 
好比說「name1=value1」,其中value1的值是「va&lu=e1」字符串,那麼實際在傳輸過程當中就會變成這樣「name1=va&lu=e1」。咱們的本意是就只有一個鍵值對,可是服務端會解析成兩個鍵值對,這樣就產生了奇異。

如何解決上述問題帶來的歧義呢?解決的辦法就是對參數進行URL編碼 
   URL編碼只是簡單的在特殊字符的各個字節前加上%,例如,咱們對上述會產生奇異的字符進行URL編碼後結果:「name1=va%26lu%3D」,這樣服務端會把緊跟在「%」後的字節當成普通的字節,就是不會把它當成各個參數或鍵值對的分隔符。

爲何要進行URL編碼
注意

8.3 接收自定義http頭部(headers)

用Python的httplib庫來作模擬客戶端,參考網上寫出模擬代碼以下:

#coding=utf8
import httplib
httpClient = None
try:
    myheaders = { "category": "Books",
                  "id": "21",
                  'My-Agent': "Super brower"
              }
    httpClient = httplib.HTTPConnection('10.14.1XX.XXX',8086,timeout=30)
    httpClient.request('GET','/headinfo/',headers=myheaders)
    response = httpClient.getresponse()
    print response.status
    print response.reason
    print response.read()
except Exception, e:
    print e
finally:
    if httpClient:
        httpClient.close()
模擬客戶端client.py

其中'/headinfo/'爲服務器的響應目錄。

而後是服務端的響應代碼,一個可以返回客戶端自定義頭部的模塊

from django.http import HttpResponse
def headinfo(request):
    category = request.META.get('HTTP_CATEGORY', 'unkown')
    id = request.META.get('HTTP_ID','unkown')
    agent = request.META.get('MY-AGENT','unkown')
    html = "<html><body>Category is %s, id is %s, agent is %s</body></html>" % (category, id, agent)
    return HttpResponse(html)
server.py

結果:

結果

注意點:自定義請求頭時,Django自動在頭部META字典中的key值都會被加上「HTTP_」的前綴,category 會變成 HTTP_CATEGORY ,auth-api 會變成 HTTP_AUTH_API

8.4 http請求request詳解

屬性    描述
path    表示提交請求頁面完整地址的字符串, 不包括域名,如 "/music/bands/the_beatles/" 。
method    
表示提交請求使用的HTTP方法。 它老是大寫的。例如:

if request.method == 'GET':
    do_something()
elif request.method == 'POST':
    do_something_else()
GET    一個類字典對象,包含全部的HTTP的GET參數的信息。 見 QueryDict 文檔。
POST    
一個類字典對象,包含全部的HTTP的POST參數的信息。 見 QueryDict 文檔。

經過POST提交的請求有可能包含一個空的 POST 字典, 也就是說, 一個經過POST方法提交的表單可能不包含數據。 所以,不該該使用 if request.POST 來判斷POST方法的使用, 而是使用if request.method == "POST" (見表中的 method 條目)。

注意: POST 並 不 包含文件上傳信息。 見 FILES 。

REQUEST    
爲了方便而建立,這是一個類字典對象,先搜索 POST , 再搜索 GET 。 靈感來自於PHP的$_REQEUST 。

例如, 若 GET = {"name": "john"} , POST = {"age": '34'} , REQUEST["name"] 會是 "john" , REQUEST["age"] 會是 "34" 。

強烈建議使用 GET 和 POST ,而不是 REQUEST 。 這是爲了向前兼容和更清楚的表示。

COOKIES    一個標準的Python字典,包含全部cookie。 鍵和值都是字符串。cookie使用的更多信息見第12章。
FILES    
一個類字典對象,包含全部上傳的文件。 FILES 的鍵來自 <input type="file" name="" />中的 name 。 FILES 的值是一個標準的Python字典, 包含如下三個鍵:

filename :字符串,表示上傳文件的文件名。
content-type :上傳文件的內容類型。
content :上傳文件的原始內容。
注意 FILES 只在請求的方法是 POST ,而且提交的 <form> 包含enctype="multipart/form-data" 時 才包含數據。不然, FILES 只是一個空的類字典對象。

META    
一個標準的Python字典,包含全部有效的HTTP頭信息。 有效的頭信息與客戶端和服務器有關。 這裏有幾個例子:

CONTENT_LENGTH
CONTENT_TYPE
QUERY_STRING :未解析的原始請求字符串。
REMOTE_ADDR :客戶端IP地址。
REMOTE_HOST :客戶端主機名。
SERVER_NAME :服務器主機名。
SERVER_PORT :服務器端口號。
在 META 中有效的任一HTTP頭信息都是帶有 HTTP_ 前綴的 鍵,例如:

HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE
HTTP_HOST :客戶端發送的 Host 頭信息。
HTTP_REFERER :被指向的頁面,若是存在的。
HTTP_USER_AGENT :客戶端的user-agent字符串。
HTTP_X_BENDER : X-Bender 頭信息的值, 若是已設的話。
user    
一個 django.contrib.auth.models.User 對象表示 當前登陸用戶。 若當前用戶還沒有登陸,user 會設爲 django.contrib.auth.models.AnonymousUser 的一個實例。 能夠將它們與is_authenticated() 區別開:

if request.user.is_authenticated():
    # Do something for logged-in users.
else:
    # Do something for anonymous users.
user 僅當Django激活 AuthenticationMiddleware 時有效。

關於認證和用戶的完整細節,見第12章。

session    一個可讀寫的類字典對象,表示當前session。 僅當Django已激活session支持時有效。 見 http://djangobook.py3k.cn/chapter12/
raw_post_data    POST的原始數據。 用於對數據的複雜處理。
HttpRequest實例的屬性

 8.5 HttpRequest 的方法

方法                                 描述
__getitem__(key)    請求所給鍵的GET/POST值,先查找POST,而後是GET。 若鍵不存在,則引起異常KeyError 。該方法使用戶能夠以訪問字典方式來訪問一個 HttpRequest 實例。例如, request["foo"] 和先檢查 request.POST["foo"] 再檢查request.GET["foo"] 一 樣。

has_key()                 返回 True 或 False , 標識 request.GET 或 request.POST 是否包含所給的 鍵。

get_full_path()          返回 path ,若請求字符串有效,則附加於其後。 例如,"/music/bands/the_beatles/?print=true" 。

is_secure()         若是請求是安全的,則返回 True 。 也就是說,請求是以HTTPS的形式提交的。
HttpRequest 的方法

返回頂部

9、COOKIE 與 SESSION

9.1簡介

1、cookie不屬於http協議範圍,因爲http協議沒法保持狀態,但實際狀況,咱們卻又須要「保持狀態」,所以cookie就是在這樣一個場景下誕生。

cookie的工做原理是:由服務器產生內容,瀏覽器收到請求後保存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上cookie,這樣服務器就能經過cookie的內容來判斷這個是「誰」了。

2、cookie雖然在必定程度上解決了「保持狀態」的需求,可是因爲cookie自己最大支持4096字節,以及cookie自己保存在客戶端,可能被攔截或竊取,所以就須要有一種新的東西,它能支持更多的字節,而且他保存在服務器,有較高的安全性。這就是session。

問題來了,基於http協議的無狀態特徵,服務器根本就不知道訪問者是「誰」。那麼上述的cookie就起到橋接的做用。

咱們能夠給每一個客戶端的cookie分配一個惟一的id,這樣用戶在訪問時,經過cookie,服務器就知道來的人是「誰」。而後咱們再根據不一樣的cookie的id,在服務器上保存一段時間的私密資料,如「帳號密碼」等等。

3、總結而言:cookie彌補了http無狀態的不足,讓服務器知道來的人是「誰」;可是cookie以文本的形式保存在本地,自身安全性較差;因此咱們就經過cookie識別不一樣的用戶,對應的在session裏保存私密的信息以及超過4096字節的文本。

4、另外,上述所說的cookie和session實際上是共通性的東西,不限於語言和框架
簡介

9.2認證機制

  製做一個登錄頁面,在驗證了用戶名和密碼的正確性後跳轉到後臺的頁面。可是測試後也發現,若是繞過登錄頁面。直接輸入後臺的url地址也能夠直接訪問的。這個顯然是不合理的。其實咱們缺失的就是cookie和session配合的驗證。有了這個驗證過程,咱們就能夠實現和其餘網站同樣必須登陸才能進入後臺頁面了。

      先說一下這種認證的機制。每當咱們使用一款瀏覽器訪問一個登錄頁面的時候,一旦咱們經過了認證。服務器端就會發送一組隨機惟一的字符串(假設是123abc)到瀏覽器端,這個被存儲在瀏覽端的東西就叫cookie。而服務器端也會本身存儲一下用戶當前的狀態,好比login=true,username=hahaha之類的用戶信息。可是這種存儲是以字典形式存儲的,字典的惟一key就是剛纔發給用戶的惟一的cookie值。那麼若是在服務器端查看session信息的話,理論上就會看到以下樣子的字典

{'123abc':{'login':true,'username:hahaha'}}

由於每一個cookie都是惟一的,因此咱們在電腦上換個瀏覽器再登錄同一個網站也須要再次驗證。那麼爲何說咱們只是理論上看到這樣子的字典呢?由於處於安全性的考慮,其實對於上面那個大字典不光key值123abc是被加密的,value值{'login':true,'username:hahaha'}在服務器端也是同樣被加密的。因此咱們服務器上就算打開session信息看到的也是相似與如下樣子的東西

{'123abc':dasdasdasd1231231da1231231}

知道了原理,咱們下面就來用代碼實現
認證的機制

9.3認證應用

第一步: 先在templates目錄下建立兩個html,login.html負責登陸頁面。backend頁面表明後臺頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <link rel="stylesheet" href="http://830909.blog.51cto.com/static/plugins/bootstrap-3.3.5-dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <form action="login.html" method="post">
            <div class="form-group">
                <label class="sr-only">username</label>
                <input type="text" class="form-control" name="username" placeholder="用戶名"/>
            </div>
            <div class="form-group">
                <label class="sr-only">Password</label>
                <input type="password" class="form-control" name="passwd" placeholder="密碼"/>
            </div>
            <div class="form-group">
                <input class="btn btn-primary" type="submit" value="http://830909.blog.51cto.com/8311014/Submit">
            </div>
        </form>
</div>
<script type="application/Javascript" src="http://830909.blog.51cto.com/static/js/jquery-2.2.1.min.js"></script>
<script type="application/javascript" src="http://830909.blog.51cto.com/static/plugins/bootstrap-3.3.5-dist/js/bootstrap.min.js"></script>
</body>
</html>
login.html
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>backend</title>
    <link rel="stylesheet" href="http://830909.blog.51cto.com/static/plugins/bootstrap-3.3.5-dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="http://830909.blog.51cto.com/static/css/commons.css">
</head>
<body>
<div class="container">
    <h2>cookie 內容是 {{ cookie_content }}</h2>
    <h2>session 內容是 {{ session_content }}</h2>
    <h2>登陸用戶名 :{{ username }}</h2>
    <a href="http://830909.blog.51cto.com/logout/">註銷</a>
</div>
<script type="application/javascript" src="http://830909.blog.51cto.com/static/js/jquery-2.2.1.min.js"></script>
<script type="application/javascript" src="http://830909.blog.51cto.com/static/plugins/bootstrap-3.3.5-dist/js/bootstrap.min.js"></script>
</body>
</html>
backend.html

 第二步:編輯app01應用下的views.py文件,編寫代碼邏輯部分

# /usr/bin/env python
# coding:utf-8
from django.shortcuts import render
from django.shortcuts import redirect
def login(request):
    if request.method=="POST":
        username=request.POST['username']
        pwd=request.POST['passwd']
        if username=='abc' and pwd=='123':
            #設置session內部的字典內容
            request.session['is_login']='true'
            request.session['username']='abc'
            #登陸成功就將url重定向到後臺的url
            return redirect('/backend/')
    #登陸不成功或第一訪問就停留在登陸頁面
    return render(request,'login.html')
def backend(request):
    """
    這裏必須用讀取字典的get()方法把is_login的value缺省設置爲False,
    當用戶訪問backend這個url先嚐試獲取這個瀏覽器對應的session中的
    is_login的值。若是對方登陸成功的話,在login裏就已經把is_login
    的值修改成了True,反之這個值就是False的
    """
    is_login=request.session.get('is_login',False)
    #若是爲真,就說明用戶是正常登錄的
    if is_login:
        #獲取字典的內容並傳入頁面文件
        cookie_content=request.COOKIES
        session_content=request.session
        username=request.session['username']
        return render(request,'backend.html',
                      {
            'cookie_content':cookie_content,
            'session_content':session_content,
            'username':username
                      })
    else:
        """
        若是訪問的時候沒有攜帶正確的session,
        就直接被重定向url回login頁面
        """
        return redirect('/login/')
def logout(request):
    """
    直接經過request.session['is_login']回去返回的時候,
    若是is_login對應的value值不存在會致使程序異常。因此
    須要作異常處理
    """
    try:
        #刪除is_login對應的value值
        del request.session['is_login']
    except KeyError:
        pass
    #點擊註銷以後,直接重定向回登陸頁面
    return redirect('/login/')
views.py

第三步,編輯mydjango目錄下的urls.py文件。設置函數與頁面的綁定關係

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^backend/', views.backend),
    url(r'^logout/', views.logout),
]
urls.py

最後打開瀏覽器直接訪問/backend/頁面的時候直接就被重定向到了/login/

 總結

1、login頁面正確登陸的話,後臺頁面能夠獲取到瀏覽器攜帶的cookie的。

2、第一行的sessionid其實就是cookie值

3、session的內容是加密的,從客戶端獲取不到session的內容

4、服務端能夠經過預設的key值取出session的內容並打印到前段

5、django的session默認是存儲在數據庫裏的,咱們再到數據庫查看一下真正session內容
總結

9.4cookie知識點:

# 1、獲取Cookie:
# request.COOKIES['key']
# request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
#     參數:
#         default: 默認值
#            salt: 加密鹽
#         max_age: 後臺控制過時時間



# 2、設置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: '/' });
Cookie知識點

9.5session知識點:

Django中默認支持Session,其內部提供了5種類型的Session供開發者使用:

一、數據庫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")
 
        ...
View Code

二、緩存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. 使用
 
    同上
View Code

三、文件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. 使用
 
    同上
View Code

四、緩存+數據庫Session

數據庫用於作持久化,緩存用於提升效率
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
 
b. 使用
 
    同上
View Code

五、加密cookie Session

a. 配置 settings.py
     
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
 
b. 使用
 
    同上
View Code

擴展:Session用戶驗證

def login(func):
    def wrap(request, *args, **kwargs):
        # 若是未登錄,跳轉到指定頁面
        if request.path == '/test/':
            return redirect('http://www.baidu.com')
        return func(request, *args, **kwargs)
    return wrap
View Code

 返回頂部

10、Django的用戶認證

10.1認證登陸

from django.contrib import auth

django.contrib.auth中提供了許多方法,這裏主要介紹其中的三個:

10.1.1  authenticate()   

提供了用戶認證,即驗證用戶名以及密碼是否正確,通常須要username  password兩個關鍵字參數

若是認證信息有效,會返回一個  User  對象。authenticate()會在User 對象上設置一個屬性標識那種認證後端認證了該用戶,且該信息在後面的登陸過程當中是須要的。當咱們試圖登錄一個從數據庫中直接取出來不通過authenticate()的User對象會報錯的!!

user=authenticate(username='someone',password='somepassword')
authenticate

10.1.2  login(HttpRequest, user)  

該函數接受一個HttpRequest對象,以及一個認證了的User對象

此函數使用django的session框架給某個已認證的用戶附加上session id等信息。

from django.contrib.auth import authenticate, login
 
def my_view(request):
  username = request.POST['username']
  password = request.POST['password']
  user = authenticate(username=username, password=password)
  if user is not None:
    login(request, user)
    # Redirect to a success page.
    ...
  else:
    # Return an 'invalid login' error message.
    ...
login

10.1.3  logout(request) 註銷用戶  

from django.contrib.auth import logout
 
def logout_view(request):
  logout(request)
  # Redirect to a success page.
該函數接受一個HttpRequest對象,無返回值。當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯
logout

 

10.1.4 user對象的 is_authenticated()

要求:

1  用戶登錄後才能訪問某些頁面,
2  若是用戶沒有登陸就訪問該頁面的話直接跳到登陸頁面
3  用戶在跳轉的登錄界面中完成登錄後,自動訪問跳轉到以前訪問的地址

方法1:

def my_view(request):
  if not request.user.is_authenticated():
    return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))

方法2:login_required函數

django已經爲咱們設計好了一個用於此種狀況的裝飾器:login_requierd()

from django.contrib.auth.decorators import login_required 
    
@login_required
def my_view(request): 
  ...

若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' (這個值能夠在settings文件中經過LOGIN_URL進行修改)。並傳遞  當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。 

+++++++++++++++++++

10.2 User對象

User 對象屬性:username, password(必填項)password用哈希算法保存到數據庫

is_staff : 用戶是否擁有網站的管理權限.

is_active : 是否容許用戶登陸, 設置爲``False``,能夠不用刪除用戶來禁止 用戶登陸

10.3 User 對象方法  

10.3.1  is_authenticated()

若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。
經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。 這個方法很重要, 在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name

10.3.2  建立用戶

使用 create_user 輔助函數建立用戶:

from django.contrib.auth.models import User
user = User.objects.create_user(username='',password='',email=''
使用 create_user 輔助函數建立用戶

10.3.3  set_password(passwd)

這個方法是用來更改密碼的,使用步驟:
user=User.objects.get(username='')
user.set_password(passeord='')
user.save
更改密碼

10.3.4  check_password(passwd)

用戶須要修改密碼的時候 首先要讓他輸入原來的密碼 ,若是給定的字符串經過了密碼檢查,返回 True

10.3.5 修改密碼

使用 set_password() 來修改密碼

user = User.objects.get(username='')
user.set_password(password='')
user.save 
使用 set_password() 來修改密碼

示例:註冊

def sign_up(request):


    state = None

    if request.method == 'POST':

        password = request.POST.get('password', '')
        repeat_password = request.POST.get('repeat_password', '')
        email=request.POST.get('email', '')
        if password == '' or repeat_password == '':
            state = 'empty'
        elif password != repeat_password:
            state = 'repeat_error'
        else:
            username = request.POST.get('username', '')
            if User.objects.filter(username=username):
                state = 'user_exist'
            else:
                new_user = User.objects.create_user(username=username, password=password,email=email)
                new_user.save()
                new_my_user = MyUser(user=new_user, telephone=request.POST.get('telephone', ''))
                new_my_user.save()

                return redirect('/book/')
    content = {
        'state': state,
        'user': None,
    }
    return render(request, 'book/sign_up.html', content)
View Code

示例:改密碼

@login_required
def set_password(request):
    user = request.user
    state = None
    if request.method == 'POST':
        old_password = request.POST.get('old_password', '')
        new_password = request.POST.get('new_password', '')
        repeat_password = request.POST.get('repeat_password', '')
        if user.check_password(old_password):
            if not new_password:
                state = 'empty'
            elif new_password != repeat_password:
                state = 'repeat_error'
            else:
                user.set_password(new_password)
                user.save()
                return redirect("/log_in/")
        else:
            state = 'password_error'
    content = {
        'user': user,
        'state': state,
    }
    return render(request, 'book/set_password.html', content)
View Code

 返回頂部

 

11、Form與ModelForm

11.1 From

  Django會將表單的提交變得更加簡單和安全,包括重構數據後在頁面間傳遞,建立前端的HTML頁面以及接收和處理客戶端傳來的數據。實施上,你只須要事先定義好form表單的各類屬性,在前端頁面簡單調用便可。固然,Django爲Form提供了許多屬性,方便自定義,甚至你能夠重寫其中的一些方法。

應用:使用註冊表完成驗證並註冊

一、urls.py

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^register/', views.register),

]
urls.py

二、models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
    """
    用戶信息
    """
    nid = models.BigAutoField(primary_key=True)
    nickname = models.CharField(verbose_name='暱稱', max_length=32)
    telephone = models.CharField(max_length=11, blank=True, null=True, unique=True, verbose_name='手機號碼')
    avatar = models.FileField(verbose_name='頭像', upload_to='upload/avatar/',default="/upload/avatar/default_avatar.jpg")
    create_time = models.DateTimeField(verbose_name='建立時間',auto_now_add=True)

    fans = models.ManyToManyField(verbose_name='粉絲們',
                                  to='UserInfo',
                                  through='UserFans',
                                  through_fields=('user', 'follower'))

    def __str__(self):
        return self.username
models.py

三、在app 目錄下新建 form.py

from django import forms
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
class RegForm(forms.Form):

    username=forms.CharField(min_length=2,error_messages={"required":"*用戶名不能爲空!","min_length":"*用戶名過短!"},
                             widget=forms.TextInput(attrs={"id":"username","placeholder":"用戶爲2到12位數字或字母"}))


    password=forms.CharField(error_messages={"required":"*密碼不能爲空!"},
                             widget=forms.PasswordInput(attrs={"id":"passwordsignup","placeholder":"eg. 密碼不能少於6位且不能爲純數字"}))

    repeat_password=forms.CharField(error_messages={"required":"*密碼不能爲空!"},
                                    widget=forms.PasswordInput(attrs={"id":"passwordsignup_confirm","placeholder":"eg. 密碼不能少於6位且不能爲純數字"}))

    email=forms.EmailField(error_messages={"required":"*郵箱不能爲空!","invalid":"*郵箱格式不正確"},
                           widget=forms.EmailInput(attrs={"id":"emailsignup","placeholder":"mysupermail@mail.com"}))

    validcode=forms.CharField(error_messages={"required":"*驗證碼不能爲空!"},
                              widget=forms.TextInput(attrs={"class":"validcode_input","id":"validcode","placeholder":"valid_code"}))

    def __init__(self,request,*args,**kwargs):
        super(RegForm,self).__init__(*args,**kwargs)
        self.request=request


    def clean_username(self):

        # if self.cleaned_data["username"].isdigit() or self.cleaned_data["username"].isalpha():
        #     raise ValidationError("*用戶名必須包含數字與字母!")
        # if len(self.cleaned_data["username"]) <2:
        #     raise ValidationError("*用戶名過短!")
        if len(self.cleaned_data["username"]) >12:
            raise ValidationError("*用戶名太長!")
        else:
            return self.cleaned_data["username"]

    def clean_password(self):  #  關於密碼字段的鉤子
        if len(self.cleaned_data["password"]) <5:
            raise ValidationError("*密碼不能少於6位!")
        elif self.cleaned_data["password"].isdigit():
            raise ValidationError("*密碼不能爲純數字!")
        else:
            return self.cleaned_data["password"]

    def clean_repeat_password(self):  # 關於密碼字段的鉤子
        if len(self.cleaned_data["repeat_password"]) < 5:
            raise ValidationError("*密碼不能少於6位!")
        elif self.cleaned_data["repeat_password"].isdigit():
            raise ValidationError("*密碼不能爲純數字!")
        else:
            return self.cleaned_data["repeat_password"]


    def clean(self):
        if self.cleaned_data.get("password") == self.cleaned_data.get("repeat_password"):
            #self.cleaned_data.get("password") 與self.cleaned_data["password"] 的區別是後者取值是當值不存在時沒有值會報錯
            return self.cleaned_data
        else:
            raise ValidationError("*密碼不一致")

    def clean_validcode(self):
        if self.cleaned_data["validcode"].upper() == self.request.session["validcode"].upper():
            return self.cleaned_data["validcode"]
        else:
            raise ValidationError("驗證碼錯誤!")
form.py

四、views.py

def register(request):

    if request.is_ajax():
        data = {"tag": False, "error_message": ""}

        form_obj=forms.RegForm(request,request.POST)
        if form_obj.is_valid():
            username=form_obj.cleaned_data["username"]
            email = form_obj.cleaned_data["email"]
            password = form_obj.cleaned_data["password"]
            repeat_password = form_obj.cleaned_data["repeat_password"]
            user_validcode = form_obj.cleaned_data["validcode"].upper()
            validcode = request.session['validcode'].upper()
            file_obj = request.FILES.get("img")

           
            models.UserInfo.objects.create_user(username=username,email=email,password=password,avatar=file_obj)
            data["tag"]=True

        else:
            errors=form_obj.errors
            
            data["error_message"] = errors
       
        return HttpResponse(json.dumps(data))

    form_obj=forms.RegForm(request)
    return render(request,"register.html",{"form_obj":form_obj})
views.py

五、register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加我的信息</title>
    <style>
        .formset{
            color: rebeccapurple;
            border: dashed cadetblue;
        }
    </style>
</head>
<body>
                        <form action="/register/" method="post">
                        {% csrf_token %}
                        <h1>博客園-註冊</h1>
                        <p>
                            <label for="username" class="uname" data-icon="u">用戶名</label>
                            {{ form_obj.username }}
                            <span id="username_error" class="ajax_errors"></span>
                        </p>
                        <p>
                            <label for="emailsignup" class="youmail" data-icon="e"> 郵箱</label>
                            {{ form_obj.email }}
                            <span id="emailsignup_error" class="ajax_errors"></span>
                        </p>
                        <p>
                            <label for="passwordsignup" class="youpasswd" data-icon="p">密碼 </label>
                            {{ form_obj.password }}
                            <span id="passwordsignup_error" class="ajax_errors"></span>
                        </p>
                        <p>
                            <label for="passwordsignup_confirm" class="youpasswd" data-icon="p">確認密碼</label>
                            {{ form_obj.repeat_password }}
                            <span id="passwordsignup_confirm_error" class="ajax_errors"></span>
                        </p >

                        <p class="file_avatar"><label for="avatar" >頭像</label>
                            <img src="{% static 'images/default_avatar.jpg' %}" alt="" id="file_img" class="sol">
                            <input type="file" id="file_choose" class="sol"></p>



                        <p>
                            <label for="validcode" class="validcode" data-icon="p" style="display: block"> 驗證碼 </label>
                            {{ form_obj.validcode }}
                            <span class="valid_img_box">
                                <img src="/validcode/" alt="" class="valid_img">
                                <span>看不清楚?</span>
                            </span>
                        </p>

                        <p style="margin-bottom: 45px!important">
                            <spanp id="validcode_error" class="ajax_errors"></spanp>
                        </p>

                        <p class="signin button">
                            <input type="button" class="register_but" value="註冊" style="float: left"/>
                            <a href="/index/"><input type="button" value="返回"/></a>

                        </p>
                        <p class="change_link">
                            已經有帳號 ?
                            <a href="/login/" class="to_register"> 去登陸 </a>
                        </p>
                    </form>

</body>
</html>
register.html

11.2 ModelForm

 11.2.1ModelForm參數和使用

ModelForm
    a.  class Meta:
            model,                           # 對應Model的
            fields=None,                     # 字段
            exclude=None,                    # 排除字段
            labels=None,                     # 提示信息
            help_texts=None,                 # 幫助提示信息
            widgets=None,                    # 自定義插件
            error_messages=None,             # 自定義錯誤信息(總體錯誤信息from django.core.exceptions import NON_FIELD_ERRORS)
            field_classes=None               # 自定義字段類 (也能夠自定義字段)
            localized_fields=('birth_date',) # 本地化,如:根據不一樣時區顯示數據
            如:
                數據庫中
                    2016-12-27 04:10:57
                setting中的配置
                    TIME_ZONE = 'Asia/Shanghai'
                    USE_TZ = True
                則顯示:
                    2016-12-27 12:10:57
    b. 驗證執行過程
        is_valid -> full_clean -> 鉤子 -> 總體錯誤
 
    c. 字典字段驗證
        def clean_字段名(self):
            # 能夠拋出異常
            # from django.core.exceptions import ValidationError
            return "新值"
    d. 用於驗證
        model_form_obj = XXOOModelForm()
        model_form_obj.is_valid()
        model_form_obj.errors.as_json()
        model_form_obj.clean()
        model_form_obj.cleaned_data
    e. 用於建立
        model_form_obj = XXOOModelForm(request.POST)
        #### 頁面顯示,並提交 #####
        # 默認保存多對多
            obj = form.save(commit=True)
        # 不作任何操做,內部定義 save_m2m(用於保存多對多)
            obj = form.save(commit=False)
            obj.save()      # 保存單表信息
            obj.save_m2m()  # 保存關聯多對多信息
 
    f. 用於更新和初始化
        obj = model.tb.objects.get(id=1)
        model_form_obj = XXOOModelForm(request.POST,instance=obj)
        ...
 
        PS: 單純初始化
            model_form_obj = XXOOModelForm(initial={...})
ModelForm參數和使用

 

11.2.2 經過ModelForm完成三張表格的數據新增:

1)  在app01目錄下創建forms.py文件,從此將全部的form都寫在這個文件中,這個文件須要在views.py中導入,方便導入相應的FORM

2)  建立ModelForm:

forms.py:

# -*- coding: UTF-8 -*-  
from django.forms import ModelForm  
from .models import Node,Line,Device  
  
#定義Node的Form,Form名字爲 模式名+Form  
class NodeForm(ModelForm):  
    #自定義ModelForm的內容  
    class Meta:  
        #該ModelForm參照Model: Node  
        model = Node  
        #在Form中不顯示node_signer這個字段  
        exclude = ['node_signer']  
  
class LineForm(ModelForm):  
    class Meta:  
        model = Line  
        exclude = ['line_signer']  
  
class DeviceForm(ModelForm):  
    class Meta:  
        model = Device  
        exclude = ['device_signer']  
forms.py

 創建url與view的關聯關係

url(r'^add/', echo.views.add),  

在views.py中創建相應的函數

views.py:

# -*- coding: UTF-8 -*-  
from .models import Node,Line,Device  
from forms import NodeForm,LineForm,DeviceForm  
from django.shortcuts import render, redirect  
def add(request):  
    #獲取來自NodeForm的表單數據  
    form = NodeForm(request.POST or None)  
    #判斷form是否有效  
    if form.is_valid():  
        #建立實例,須要作些數據處理,暫不作保存  
        instance = form.save(commit=False)  
        #將登陸用戶做爲登記人  
        instance.node_signer = request.user  
        #保存該實例  
        instance.save()  
        #跳轉至列表頁面  
        return redirect('/lists/')  
  
    #建立context來集中處理須要傳遞到頁面的數據  
    context = {  
        'form': form,  
    }  
    #若是沒有有效提交,則仍留在原來頁面  
    return render(request, 'add.html',  context)  
views.py

在templates文件夾下創建HTML文件,add.html

add.html:

<!DOCTYPE html>  
<html>  
<head lang="en">  
    <meta charset="UTF-8">  
    <title></title>  
</head>  
<body>  
 <form method='POST' action=''>{% csrf_token %}  
        {{ form }}  
        <input type='submit' value='提交' />  
 </form>  
  
</body>  
</html>  
add.html

返回頂部

12、分頁

12.1分頁器的使用

>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)
 
>>> p.count        #數據總數
4
>>> p.num_pages    #總頁數
2
>>> type(p.page_range)  # `<type 'rangeiterator'>` in Python 2.
<class 'range_iterator'>
>>> p.page_range        #頁碼的列表
range(1, 3)     # =========[1,2]
 
>>> page1 = p.page(1)   #第1頁的page對象
>>> page1
<Page 1 of 2>
>>> page1.object_list   #第1頁的數據
['john', 'paul']
 
>>> page2 = p.page(2)
>>> page2.object_list    #第2頁的數據
['george', 'ringo']
>>> page2.has_next()     #是否有下一頁
False
>>> page2.has_previous() #是否有上一頁
True
>>> page2.has_other_pages() #是否有其餘頁
True
>>> page2.next_page_number() #下一頁的頁碼
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()  #上一頁的頁碼
1
>>> page2.start_index() # 本頁第一條記錄的序數(從1開始)
3
>>> page2.end_index() # 本頁最後錄一條記錄的序數(從1開始)
4
 
>>> p.page(0)       #錯誤的頁,拋出異常
Traceback (most recent call last):
...
EmptyPage: That page number is less than 1
>>> p.page(3)       #錯誤的頁,拋出異常
Traceback (most recent call last):
...
EmptyPage: That page contains no results
分頁器參數及使用

12.2實現一個分頁效果

index.html

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <link rel="stylesheet" href="{% static 'bootstrap.css' %}">

</head>
<body>

<div class="container">

    <h4>分頁器</h4>
    <ul>

    {% for book in book_list %}
         <li>{{ book.title }} {{ book.price }}</li>
    {% endfor %}

</ul>

    <ul class="pagination" id="pager">

                 {% if book_list.has_previous %}
                    <li class="previous"><a href="/blog/?page={{ book_list.previous_page_number }}">上一頁</a></li>
                 {% else %}
                    <li class="previous disabled"><a href="#">上一頁</a></li>
                 {% endif %}


                 {% for num in paginator.page_range %}

                     {% if num == currentPage %}
                       <li class="item active"><a href="/blog/?page={{ num }}">{{ num }}</a></li>
                     {% else %}
                       <li class="item"><a href="/blog/?page={{ num }}">{{ num }}</a></li>

                     {% endif %}
                 {% endfor %}


                 {% if book_list.has_next %}
                    <li class="next"><a href="/blog/?page={{ book_list.next_page_number }}">下一頁</a></li>
                 {% else %}
                    <li class="next disabled"><a href="#">下一頁</a></li>
                 {% endif %}

            </ul>
</div>

</body>
</html>
index.html

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from app01.models import *
def index(request):

    '''
    批量導入數據:

    Booklist=[]
    for i in range(100):
        Booklist.append(Book(title="book"+str(i),price=30+i*i))
    Book.objects.bulk_create(Booklist)

    '''

    book_list=Book.objects.all()

    paginator = Paginator(book_list, 10)
    page = request.GET.get('page',1)
    currentPage=int(page)


    try:
        print(page)
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)


    return render(request,"index.html",locals())
views.py

返回頂部

十3、緩存

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

Django中提供了6種緩存方式:

開發調試
內存
文件
數據庫
Memcache緩存(python-memcached模塊)
Memcache緩存(pylibmc模塊)
Django6種緩存方式

一、配置

a、開發調試

    # 此爲開始調試用,實際內部不作任何操做
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
                'TIMEOUT': 300,                                               # 緩存超時時間(默認300,None表示永不過時,0表示當即過時)
                'OPTIONS':{
                    'MAX_ENTRIES': 300,                                       # 最大緩存個數(默認300)
                    'CULL_FREQUENCY': 3,                                      # 緩存到達最大個數以後,剔除緩存個數的比例,即:1/CULL_FREQUENCY(默認3)
                },
                'KEY_PREFIX': '',                                             # 緩存key的前綴(默認空)
                'VERSION': 1,                                                 # 緩存key的版本(默認1)
                'KEY_FUNCTION' 函數名                                          # 生成key的函數(默認函數會生成爲:【前綴:版本:key】)
            }
        }


    # 自定義key
    def default_key_func(key, key_prefix, version):
        """
        Default function to generate keys.

        Constructs the key used by all other methods. By default it prepends
        the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
        function with custom key making behavior.
        """
        return '%s:%s:%s' % (key_prefix, version, key)

    def get_key_func(key_func):
        """
        Function to decide which key function to use.

        Defaults to ``default_key_func``.
        """
        if key_func is not None:
            if callable(key_func):
                return key_func
            else:
                return import_string(key_func)
        return default_key_func
View Code

b、內存

    # 此緩存將內容保存至內存的變量中
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                'LOCATION': 'unique-snowflake',
            }
        }

    # 注:其餘配置同開發調試版本
View Code

c、文件

    # 此緩存將內容保存至文件
    # 配置:

        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                'LOCATION': '/var/tmp/django_cache',
            }
        }
    # 注:其餘配置同開發調試版本
View Code

d、數據庫

    # 此緩存將內容保存至數據庫

    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
                'LOCATION': 'my_cache_table', # 數據庫表
            }
        }

    # 注:執行建立表命令 python manage.py createcachetable
View Code

e、Memcache緩存(python-memcached模塊)

# 此緩存使用python-memcached模塊鏈接memcache

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'unix:/tmp/memcached.sock',
        }
    }   

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }
View Code

f、Memcache緩存(pylibmc模塊)

    # 此緩存使用pylibmc模塊鏈接memcache
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '/tmp/memcached.sock',
        }
    }   

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }
View Code

二、應用

a. 全站使用

   使用中間件,通過一系列的認證等操做,若是內容在緩存中存在,則使用FetchFromCacheMiddleware獲取內容並返回給用戶,當返回給用戶以前,判斷緩存中是否已經存在,若是不存在則UpdateCacheMiddleware會將緩存保存至緩存,從而實現全站緩存

    MIDDLEWARE = [
        'django.middleware.cache.UpdateCacheMiddleware',
        # 其餘中間件...
        'django.middleware.cache.FetchFromCacheMiddleware',
    ]

    CACHE_MIDDLEWARE_ALIAS = ""
    CACHE_MIDDLEWARE_SECONDS = ""
    CACHE_MIDDLEWARE_KEY_PREFIX = ""
View Code

b. 單獨視圖緩存

    方式一:
        from django.views.decorators.cache import cache_page

        @cache_page(60 * 15)
        def my_view(request):
            ...

    方式二:
        from django.views.decorators.cache import cache_page

        urlpatterns = [
            url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
        ]
View Code

c、局部視圖使用

    a. 引入TemplateTag

        {% load cache %}

    b. 使用緩存

        {% cache 5000 緩存key %}
            緩存內容
        {% endcache %}
View Code

返回頂部

十4、信號

Django中提供了「信號調度」,用於在框架執行操做時解耦。通俗來說,就是一些動做發生的時候,信號容許特定的發送者去提醒一些接受者。

一、Django內置信號

Model signals
        pre_init                    # django的modal執行其構造方法前,自動觸發
        post_init                   # django的modal執行其構造方法後,自動觸發
        pre_save                    # django的modal對象保存前,自動觸發
        post_save                   # django的modal對象保存後,自動觸發
        pre_delete                  # django的modal對象刪除前,自動觸發
        post_delete                 # django的modal對象刪除後,自動觸發
        m2m_changed                 # django的modal中使用m2m字段操做第三張表(add,remove,clear)先後,自動觸發
        class_prepared              # 程序啓動時,檢測已註冊的app中modal類,對於每個類,自動觸發
    Management signals
        pre_migrate                 # 執行migrate命令前,自動觸發
        post_migrate                # 執行migrate命令後,自動觸發
    Request/response signals
        request_started             # 請求到來前,自動觸發
        request_finished            # 請求結束後,自動觸發
        got_request_exception       # 請求異常後,自動觸發
    Test signals
        setting_changed             # 使用test測試修改配置文件時,自動觸發
        template_rendered           # 使用test測試渲染模板時,自動觸發
    Database Wrappers
        connection_created          # 建立數據庫鏈接時,自動觸發
Django內置信號說明

 

對於Django內置的信號,僅需註冊指定信號,當程序執行相應操做時,自動觸發註冊函數:

    from django.core.signals import request_finished
    from django.core.signals import request_started
    from django.core.signals import got_request_exception

    from django.db.models.signals import class_prepared
    from django.db.models.signals import pre_init, post_init
    from django.db.models.signals import pre_save, post_save
    from django.db.models.signals import pre_delete, post_delete
    from django.db.models.signals import m2m_changed
    from django.db.models.signals import pre_migrate, post_migrate

    from django.test.signals import setting_changed
    from django.test.signals import template_rendered

    from django.db.backends.signals import connection_created


    def callback(sender, **kwargs):
        print("xxoo_callback")
        print(sender,kwargs)

    xxoo.connect(callback)
    # xxoo指上述導入的內容
註冊信號
from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")
信號應用舉例

二、自定義信號

a. 定義信號

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
定義信號

b. 註冊信號

def callback(sender, **kwargs):
            print("callback")
            print(sender,kwargs)

        pizza_done.connect(callback)
註冊信號

c. 觸發信號

from 路徑 import pizza_done

        pizza_done.send(sender='seven',toppings=123, size=456)
觸發信號

因爲內置信號的觸發者已經集成到Django中,因此其會自動調用,而對於自定義信號則須要開發者在任意位置觸發。

 

返回頂部

十5、序列化

序列化

在django中,在將數據庫中檢索的數據返回給客戶端用戶,數據類型爲QuerySet格式,而QuerySet格式不能被json格式化處理傳到前端,所以在處理此類數據時有兩種方法:

方法一:使用django自帶的系列化模塊 serializers模塊

一、serializers

from django.core import serializers
 
ret = models.BookType.objects.all()
 
data = serializers.serialize("json", ret)
使用serializers序列化

一、serializers序列化後的數據格式爲列表,列表爲每個查詢記錄

  二、serializers模塊不能跨表查詢,涉及一對多查詢時,只能查詢到關聯字段

方法二:查詢時轉換爲QuerySet字典或列表格式

二、json.dumps

import json
 
#ret = models.BookType.objects.all().values('caption')
ret = models.BookType.objects.all().values_list('caption')
 
ret=list(ret)  #將QuerySet進行轉化
 
result = json.dumps(ret)
json.dumps序列化

特殊的:

因爲json.dumps時沒法處理datetime日期,因此能夠經過自定義處理器來作擴展,如:
import json
from datetime import date
from datetime import datetime
    
class JsonCustomEncoder(json.JSONEncoder):
     
    def default(self, field):
      
        if isinstance(field, datetime):
            return o.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field, date):
            return o.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, field)
    
    
ds = json.dumps(d, cls=JsonCustomEncoder)
datetime日期序列化

返回頂部

十6、Ajax

AJAX的優缺點

優勢:

  • AJAX使用Javascript技術向服務器發送異步請求;
  • AJAX無須刷新整個頁面;
  • 由於服務器響應內容再也不是整個頁面,而是頁面中的局部,因此AJAX性能高;

缺點:

  • AJAX並不適合全部場景,不少時候仍是要使用同步交互;
  • AJAX雖然提升了用戶體驗,但無形中向服務器發送的請求次數增多了,致使服務器壓力增大;
  • 由於AJAX是在瀏覽器中使用Javascript技術完成的,因此還須要處理瀏覽器兼容性問題;

 js實現局部刷新

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        .error{
            color:red
        }
    </style>
</head>
<body>


<form class="Form">

    <p>姓名&nbsp;&nbsp;<input class="v1" type="text" name="username" mark="用戶名"></p>
    <p>密碼&nbsp;&nbsp;<input class="v1" type="text" name="email" mark="郵箱"></p>
    <p><input type="submit" value="submit"></p>

</form>

<script src="jquery-3.1.1.js"></script>

<script>

    $(".Form :submit").click(function(){

        flag=true;

        $("Form .v1").each(function(){

            var value=$(this).val();
            if (value.trim().length==0){
                 var mark=$(this).attr("mark");
                 var $span=$("<span>");
                 $span.html(mark+"不能爲空!");
                 $span.prop("class","error");
                 $(this).after($span);

                 setTimeout(function(){
                      $span.remove();
                 },800);

                 flag=false;
                 return flag;

            }
        });
        return flag
    });


</script>


</body>
</html>
js實現局部刷新

jquery實現的ajax

{% load staticfiles %}

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="{% static 'JS/jquery-3.1.1.js' %}"></script>
</head>
<body>

<button class="send_Ajax">send_Ajax</button>

<script>
      //$.ajax的兩種使用方式:

      //$.ajax(settings);
      //$.ajax(url,[settings]);


       $(".send_Ajax").click(function(){

           $.ajax({
               url:"/handle_Ajax/",
               type:"POST",
               data:{username:"Yuan",password:123},

               success:function(data){
                   alert(data)
               },

                 //=================== error============

                error: function (jqXHR, textStatus, err) {

                        // jqXHR: jQuery加強的xhr
                        // textStatus: 請求完成狀態
                        // err: 底層經過throw拋出的異常對象,值與錯誤類型有關
                        console.log(arguments);
                    },

                 //=================== complete============

                complete: function (jqXHR, textStatus) {
                    // jqXHR: jQuery加強的xhr
                    // textStatus: 請求完成狀態 success | error
                    console.log('statusCode: %d, statusText: %s', jqXHR.status, jqXHR.statusText);
                    console.log('textStatus: %s', textStatus);
                },

                //=================== statusCode============
                statusCode: {
                    '403': function (jqXHR, textStatus, err) {
                        console.log(arguments);  //注意:後端模擬errror方式:HttpResponse.status_code=500
                     },

                    '400': function () {
                    }
                }

           })

       })

</script>
</body>
</html>
jquery實現的ajax
import json

def index(request):

    return render(request,"index.html")

def handle_Ajax(request):

    username=request.POST.get("username")
    password=request.POST.get("password")

    print(username,password)

    return HttpResponse(json.dumps("Error Data!"))

views
views.py

$.ajax參數

######################------------data---------################

       data: 當前ajax請求要攜帶的數據,是一個json的object對象,ajax方法就會默認地把它編碼成某種格式
             (urlencoded:?a=1&b=2)發送給服務端;此外,ajax默認以get方式發送請求。

             function testData() {
               $.ajax("/test",{     //此時的data是一個json形式的對象
                  data:{
                    a:1,
                    b:2
                  }
               });                   //?a=1&b=2
######################------------processData---------################

processData:聲明當前的data數據是否進行轉碼或預處理,默認爲true,即預處理;if爲false,
             那麼對data:{a:1,b:2}會調用json對象的toString()方法,即{a:1,b:2}.toString()
             ,最後獲得一個[object,Object]形式的結果。
            
######################------------contentType---------################

contentType:默認值: "application/x-www-form-urlencoded"。發送信息至服務器時內容編碼類型。
             用來指明當前請求的數據編碼格式;urlencoded:?a=1&b=2;若是想以其餘方式提交數據,
             好比contentType:"application/json",即向服務器發送一個json字符串:
               $.ajax("/ajax_get",{
             
                  data:JSON.stringify({
                       a:22,
                       b:33
                   }),
                   contentType:"application/json",
                   type:"POST",
             
               });                          //{a: 22, b: 33}

             注意:contentType:"application/json"一旦設定,data必須是json字符串,不能是json對象


######################------------traditional---------################

traditional:通常是咱們的data數據有數組時會用到 :data:{a:22,b:33,c:["x","y"]},
              traditional爲false會對數據進行深層次迭代;
$.ajax參數

響應參數

/*

dataType:  預期服務器返回的數據類型,服務器端返回的數據會根據這個值解析後,傳遞給回調函數。
            默認不須要顯性指定這個屬性,ajax會根據服務器返回的content Type來進行轉換;
            好比咱們的服務器響應的content Type爲json格式,這時ajax方法就會對響應的內容
            進行一個json格式的轉換,if轉換成功,咱們在success的回調函數裏就會獲得一個json格式
            的對象;轉換失敗就會觸發error這個回調函數。若是咱們明確地指定目標類型,就能夠使用
            data Type。
            dataType的可用值:html|xml|json|text|script
            見下dataType實例

*/
響應參數

示例:

from django.shortcuts import render,HttpResponse
from django.views.decorators.csrf import csrf_exempt
# Create your views here.

import json

def login(request):

    return render(request,'Ajax.html')


def ajax_get(request):

    l=['alex','little alex']
    dic={"name":"alex","pwd":123}

    #return HttpResponse(l)      #元素直接轉成字符串alexlittle alex
    #return HttpResponse(dic)    #字典的鍵直接轉成字符串namepwd
    return HttpResponse(json.dumps(l))
    return HttpResponse(json.dumps(dic))# 傳到前端的是json字符串,要想使用,須要JSON.parse(data)

//---------------------------------------------------
    function testData() {

        $.ajax('ajax_get', {
           success: function (data) {
           console.log(data);
           console.log(typeof(data));
           //console.log(data.name);
           //JSON.parse(data);
           //console.log(data.name);
                                     },
           //dataType:"json",
                            }
                       )}

註解:Response Headers的content Type爲text/html,因此返回的是String;但若是咱們想要一個json對象
    設定dataType:"json"便可,至關於告訴ajax方法把服務器返回的數據轉成json對象發送到前端.結果爲object
    固然,
        return HttpResponse(json.dumps(a),content_type="application/json")

    這樣就不須要設定dataType:"json"了。
    content_type="application/json"和content_type="json"是同樣的!
示例

csrf跨站請求僞造

方法一

$.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
csrf跨站請求僞造

方法二:

var formData=new FormData();

var username= $("#username").val();
var email= $("#emailsignup").val();
var csrf=$("[name='csrfmiddlewaretoken']").val();

formData.append("username",username);
formData.append("email",email);
formData.append("csrfmiddlewaretoken",csrf);

$.ajax({
               url:"/handle_Ajax/",
               type:"POST",
              data: formData,,

               success:function(data){
                   alert(data)
               },   

           })
        
csrf跨站請求僞造方法二

跨域AJAX

因爲瀏覽器存在同源策略機制,同源策略阻止從一個源加載的文檔或腳本獲取或設置另外一個源加載的文檔的屬性。

特別的:因爲同源策略是瀏覽器的限制,因此請求的發送和響應是能夠進行,只不過瀏覽器不接受罷了。

瀏覽器同源策略並非對全部的請求均制約:

  • 制約: XmlHttpRequest
  • 不叼: img、iframe、script等具備src屬性的標籤

跨域,跨域名訪問,如:http://www.c1.com 域名向 http://www.c2.com域名發送請求。

一、JSONP實現跨域請求

JSONP(JSONP - JSON with Padding是JSON的一種「使用模式」),利用script標籤的src屬性(瀏覽器容許script標籤跨域)

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="button" onclick="Jsonp1();"  value='提交'/>
    </p>

    <p>
        <input type="button" onclick="Jsonp2();" value='提交'/>
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function Jsonp1(){
            var tag = document.createElement('script');
            tag.src = "http://c2.com:8000/test/";
            document.head.appendChild(tag);
            document.head.removeChild(tag);

        }

        function Jsonp2(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'JSONP',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>
基於JSONP實現跨域Ajax - Demo

二、CORS

隨着技術的發展,如今的瀏覽器能夠支持主動設置從而容許跨域請求,即:跨域資源共享(CORS,Cross-Origin Resource Sharing),其本質是設置響應頭,使得瀏覽器容許跨域請求。

 

* 簡單請求 OR 非簡單請求

條件:
    1、請求方式:HEAD、GET、POST
    2、請求頭信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 對應的值是如下三個中的任意一個
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain

注意:同時知足以上兩個條件時,則是簡單請求,不然爲複雜請求
* 簡單請求和非簡單請求的區別?

   簡單請求:一次請求
非簡單請求:兩次請求,在發送數據以前會先發一次請求用於作「預檢」,只有「預檢」經過後纔再發送一次請求用於數據傳輸。
簡單請求與非簡單請求
* 關於「預檢」

- 請求方式:OPTIONS
- 「預檢」其實作檢查,檢查若是經過則容許傳輸數據,檢查不經過則再也不發送真正想要發送的消息
- 如何「預檢」
     => 若是複雜請求是PUT等請求,則服務端須要設置容許某請求,不然「預檢」不經過
        Access-Control-Request-Method
     => 若是複雜請求設置了請求頭,則服務端須要設置容許某請求頭,不然「預檢」不經過
        Access-Control-Request-Headers
* 關於「預檢」

基於cors實現AJAX請求:

a、支持跨域,簡單請求

服務器設置響應頭:Access-Control-Allow-Origin = '域名' 或 '*'

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                }
            };
            xhr.open('GET', "http://c2.com:8000/test/", true);
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'text',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>
HTML
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.write('{"status": true, "data": "seven"}')
Torando

b、支持跨域,複雜請求

因爲複雜請求時,首先會發送「預檢」請求,若是「預檢」成功,則發送真實數據。

    • 「預檢」請求時,容許請求方式則需服務器設置響應頭:Access-Control-Request-Method
    • 「預檢」請求時,容許請求頭則需服務器設置響應頭:Access-Control-Request-Headers
    • 「預檢」緩存時間,服務器設置響應頭:Access-Control-Max-Age
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                }
            };
            xhr.open('PUT', "http://c2.com:8000/test/", true);
            xhr.setRequestHeader('k1', 'v1');
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'PUT',
                dataType: 'text',
                headers: {'k1': 'v1'},
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>
HTML
class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.write('{"status": true, "data": "seven"}')

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Headers', "k1,k2")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Max-Age', 10)
Tornado

c、跨域獲取響應頭

默認獲取到的全部響應頭只有基本信息,若是想要獲取自定義的響應頭,則須要再服務器端設置Access-Control-Expose-Headers。

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                    // 獲取響應頭
                    console.log(xhr.getAllResponseHeaders());
                }
            };
            xhr.open('PUT', "http://c2.com:8000/test/", true);
            xhr.setRequestHeader('k1', 'v1');
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'PUT',
                dataType: 'text',
                headers: {'k1': 'v1'},
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                    // 獲取響應頭
                    console.log(xmlHttpRequest.getAllResponseHeaders());
                }
            })
        }


    </script>
</body>
</html>
HTML
class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")

        self.set_header('xxoo', "seven")
        self.set_header('bili', "daobidao")

        self.set_header('Access-Control-Expose-Headers', "xxoo,bili")


        self.write('{"status": true, "data": "seven"}')

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Headers', "k1,k2")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Max-Age', 10)
Tornado

d、跨域傳輸cookie

在跨域請求中,默認狀況下,HTTP Authentication信息,Cookie頭以及用戶的SSL證書不管在預檢請求中或是在實際請求都是不會被髮送。

若是想要發送:

    • 瀏覽器端:XMLHttpRequest的withCredentials爲true
    • 服務器端:Access-Control-Allow-Credentials爲true
    • 注意:服務器端響應的 Access-Control-Allow-Origin 不能是通配符 *
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                }
            };

            xhr.withCredentials = true;

            xhr.open('PUT', "http://c2.com:8000/test/", true);
            xhr.setRequestHeader('k1', 'v1');
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'PUT',
                dataType: 'text',
                headers: {'k1': 'v1'},
                xhrFields:{withCredentials: true},
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>
HTML
class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Credentials', "true")
        
        self.set_header('xxoo', "seven")
        self.set_header('bili', "daobidao")
        self.set_header('Access-Control-Expose-Headers', "xxoo,bili")

        self.set_cookie('kkkkk', 'vvvvv');

        self.write('{"status": true, "data": "seven"}')

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Headers', "k1,k2")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Max-Age', 10)
Tornado
 

 返回頂部

 十7、數據庫性能相關

select_related 與 prefetch_related 區別

user_list = models.UserInfo.objects.all()
	for row in user_list:
		# 只去取當前表數據

select_related,主動連表查詢【FK】
	
	user_list = models.UserInfo.objects.all().select_related('FK字段')
	for row in user_list:
		# 只去取當前表數據和FK表關聯字段


	user_list = models.UserInfo.objects.values(...)
	for row in user_list:
		# 只去取當前表數據和FK表關聯字段

	==》 連表下降性能

prefetch_related 被動連表
	user_list = models.UserInfo.objects.all().prefetch_related('FK字段')

	# [obj,obj,obj]
	# 查詢用戶表models.UserInfo.objects.all() 1000
	# 把用戶表中全部的ut_id拿到, 用戶類型ID [1,2,3]
	# 把用戶表中全部的ut_id拿到, 用戶類型ID [21,21,31]
	# select * from UsetType where id in [1,2,3]
	# select * from xx where id in [21,21,31]
	user_list = models.UserInfo.objects.all().prefetch_related('ut','xx')
	for row in user_list:
		print(row.name, row.pwd, row.ut.caption)
補充:
	# [obj,obj,obj]
	# user_list = models.UserInfo.objects.all().only('name')   # 只取某個字段 select name from userinfo 
	# user_list = models.UserInfo.objects.all().defer('name')  # 排除當前字段	
	# for row in user_list:
	#     print(row.pwd)

select_related,主動連表查詢【FK】,當連表很少時用select_related查詢

prefetch_related 被動連表,當連表較多時,用prefetch_related查詢

 返回頂部

未添加:

自定義分頁、驗證碼、頭像預覽、富文本編輯框

相關文章
相關標籤/搜索