Django框架

 

1、Django簡介php

一、MVC模型css

MVC模型是web服務器開發領域著名的模型。html

M:模型,業務對象與數據庫的映射(ORM)java

V:視圖,用於與用戶的交互(頁面)python

C:控制器,接收用戶的輸入調用模型和視圖完成用戶的請求mysql

二、MTV模型jquery

MTV模型是Django的web服務器開發模型,與MVC本質上的原理是同樣的,也是爲了各組件間保持鬆耦合關係。git

M(model):模型,負責業務對象與數據庫的關係映射(ORM)web

T(template):模板,負責將HTML頁面展現給用戶ajax

V(view):視圖,負責業務邏輯,並在適當時候調用Model和Template

除了以上三層以外,還須要一個URL分發器,它的做用是將一個個URL的頁面請求分發給不一樣的View處理,View再調用相應的Model和Template,最後將響應數據返回給用戶。

三、Django的下載

pip3 install django  # 下載最新版本
pip3 install django==1.11  # 指定版本下載

四、建立一個Django項目:

django-admin.py startproject mysite  # 建立一個叫mysite的Django項目

當前目錄下會生成mysite的工程,目錄結構以下:

manage.py ----- Django項目裏面的工具,經過它能夠調用django shell和數據庫等。
settings.py ---- 包含了項目的默認設置,包括數據庫信息,調試標誌以及其餘一些工做的變量。
urls.py ----- 負責把URL模式映射到應用程序。

wsgi.py----遵循wsgi規範的模塊

五、在mysite目錄下建立應用

python3 manage.py startapp blog  # 建立一個叫blog的app

目錄結構:

六、啓動Django項目

python3 manage.py runserver 127.0.0.1:8088

七、基於Django實現一個簡單的示例:顯示當前日期

import time
from django.shortcuts import render


def index(request):
    ctime = time.strftime("%Y-%m-%d %X")
    return render(request, "index.html", {"ctime": ctime})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>顯示當前時間</title>
</head>
<body>
    <div>
        <h3>{{ ctime }}</h3>
    </div>
</body>
</html>
index.html
from django.contrib import admin
from django.urls import path
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),  # 設置路由
]
urls.py

記得在settings.py中設置templates的路徑:

在瀏覽器訪問http://127.0.0.1:8088/index/便可:

2、Django的路由

一、使用正則

經過"圓括號"來捕獲URL 中的值並以位置參數傳遞給視圖。

from django.contrib import admin
from django.urls import path, re_path
from blog import views

urlpatterns = [
    # http://127.0.0.1:8088/articles/2003/ 將調用函數special_case_2003(request)
    re_path(r'^articles/2003/$', views.special_case_2003),

    # http://127.0.0.1:8088/articles/2004/ 將調用函數year_archive(request, year)
    re_path(r'^articles/([0-9]{4})/$', views.year_archive),

    # /articles/2005/03/ 將調用函數month_archive(request, year, month)
    re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),

    # http://127.0.0.1:8088/articles/2003/04/12/ 將調用函數article_detail(request, year, month, day)
    re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
urls.py
from django.shortcuts import render, HttpResponse


def special_case_2003(request):
    return HttpResponse("special_case_2003")


def year_archive(request, year):
    print(year)
    return HttpResponse("year_archive")


def month_archive(request, year, month):
    print(year, month)
    return HttpResponse("month_archive")


def article_detail(request, year, month, day):
    print(year, month, day)
    return HttpResponse("article_detail")
views.py

二、有名分組

給URL中的參數命名,語法:(?P<name>pattern),name是指組的名稱, pattern是指要匹配的模式。

與上面的區別在於,捕獲的值做爲關鍵字參數而不是位置參數傳遞給視圖函數。

from django.contrib import admin
from django.urls import path, re_path
from blog import views

urlpatterns = [
    # http://127.0.0.1:8088/articles/2003/ 將調用函數special_case_2003(request)
    re_path(r'^articles/2003/$', views.special_case_2003),

    # http://127.0.0.1:8088/articles/2004/ 將調用函數year_archive(request, year)
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

    # /articles/2005/03/ 將調用函數month_archive(request, year, month)
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),

    # http://127.0.0.1:8088/articles/2003/04/12/ 將調用函數article_detail(request, year, month, day)
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]+)/$', views.article_detail),
]
urls.py

三、路由分發

有時候在開發一個項目中,能夠會有多個APP,咱們能夠在每一個APP下單獨給這個APP設置urls,使得咱們可以更清晰直觀的讀取URL。

如:

在blog app下新建urls.py:

from django.urls import path
from blog import views

urlpatterns = [
    path('index/', views.index),

]
blog/urls.py

在全局urls.py中經過include來進行路由分發:

from django.contrib import admin
from django.urls import path, re_path, include
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include("blog.urls")),  # http://127.0.0.1:8088/blog/index/ 將訪問blog下的urls.py
]
urls.py
import time
from django.shortcuts import render, HttpResponse


def index(request):
    ctime = time.strftime("%Y-%m-%d %X")
    return render(request, "index.html", {"ctime": ctime})
views.py

四、反向解析

人們強烈但願不要硬編碼這些URL,費力、不可擴展且容易產生錯誤,經過給URL設置name,再借助Django 提供不一樣的工具用於URL 反查

(1)在模板中:使用url 模板標籤 {% %}

(2)在視圖中:使用from django.urls import reverse 函數

from django.contrib import admin
from django.urls import path, re_path, include
from blog import views


urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^book/(?P<pk>\d+)/$', views.book, name="book"),
]
urls.py
from django.shortcuts import render, HttpResponse
from django.urls import reverse


def book(request, pk):
    url = reverse("book", args=(pk,))  # 反向獲取URL,book是在urls.py中給URL設置的名字,args是URL中的參數
    print(url)
    return render(request, "book.html", locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {#    book是給URL設置的名稱,pk是URL中的參數 #}
    <a href="{% url "book" pk %}">book {{ pk }}</a>
</body>
</html>
book.html

五、命名空間

命名空間(Namespace)是表示標識符的可見範圍。

因爲name沒有做用域,Django在反解URL時,會在項目全局順序搜索,當查找到第一個name指定URL時,當即返回。 咱們在開發項目時,會常用name屬性反解出URL,當不當心在不一樣的app的urls中定義相同的name時,可能會致使URL反解錯誤,爲了不這種事情發生,引入了命名空間。

在Django項目mysite下新建兩個APP:

python3 manage.py startapp app01
python3 manage.py startapp app02

mysite/urls.py:

from django.urls import path, re_path, include


urlpatterns = [
    re_path(r'^app01/', include(("app01.urls", "app01"), namespace="app01")),
    re_path(r'^app02/', include(("app02.urls", "app02"), namespace="app02")),
]

 

app01/urls.py:

from django.conf.urls import re_path
from app01.views import index

urlpatterns = [
    re_path(r'^index/$', index, name="index"),
]

app02/urls.py:

from django.conf.urls import re_path
from app02.views import index

urlpatterns = [
    re_path(r'^index/$', index, name="index"),
]

app01/views.py:

from django.shortcuts import HttpResponse


def index(request):
    return HttpResponse("in app01-index")

app02/views.py:

from django.shortcuts import HttpResponse


def index(request):
    return HttpResponse("in app02-index")

經過瀏覽器訪問:

include()源碼:

六、Django2.0版本的path

有以下URL:

urlpatterns = [
re_path('articles/(?P<year>[0-9]{4})/', year_archive),
re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),
]

思考:

(1)默認函數 year_archive 中year參數是字符串類型的,有時候咱們須要將它轉換成整數類型的變量值來使用,固然year=int(year) 不會有諸如如TypeError或者ValueError的異常。那麼有沒有一種方法,在url中,使得這一轉化步驟能夠由Django自動完成?

(2)三個路由中article_id都是一樣的正則表達式,可是你須要寫三遍,當以後article_id規則改變後,須要同時修改三處代碼,那麼有沒有一種方法,只需修改一處便可?

在Django2.0中,可使用 path 解決以上的兩個問題,基本規則:

(1)使用尖括號(<>)從url中捕獲值。

(2)捕獲值中能夠包含一個轉化器類型(converter type),好比使用<int:year>捕獲一個整數變量。若果沒有轉化器,將匹配任何字符串,固然也包括了 / 字符。

from django.urls import path  
from . import views  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<int:year>/', views.year_archive),  
    path('articles/<int:year>/<int:month>/', views.month_archive),  
    path('articles/<int:year>/<int:month>/<slug>/', views.article_detail),  
]

Django默認支持如下5個轉化器:

  • str,匹配除了路徑分隔符(/)以外的非空字符串,這是默認的形式
  • int,匹配正整數,包含0。
  • slug,匹配字母、數字以及橫槓、下劃線組成的字符串。
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
  • path,匹配任何非空字符串,包含了路徑分隔符

 

能夠註冊自定義轉化器

對於一些複雜或者複用的須要,能夠定義本身的轉化器。轉化器是一個類或接口,它的要求有三點:

  • regex 類屬性,字符串類型
  • to_python(self, value) 方法,value是由類屬性 regex 所匹配到的字符串,返回具體的Python變量值,以供Django傳遞到對應的視圖函數中。
  • to_url(self, value) 方法,和 to_python 相反,value是一個具體的Python變量值,返回其字符串,一般用於url反向引用。
class FourDigitYearConverter:  
    regex = '[0-9]{4}'  
    def to_python(self, value):  
        return int(value)  
    def to_url(self, value):  
        return '%04d' % value

使用register_converter 將其註冊到URL配置中:

from django.urls import register_converter, path  
from . import converters, views  
# 註冊類,並給這個轉換器命名爲xx register_converter(converters.FourDigitYearConverter, 'xx') urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<xx:year>/', views.year_archive), # 使用轉換器 ]

 

3、Django的視圖

Django中的視圖有兩類:基於函數的視圖(FBV)和基於類的視圖(CBV),它接收Web請求(HttpRequest對象)而且返回Web響應(HttpResponse對象)。

一、HttpRequest對象

1.HttpRequest.GET
  一個相似於字典的QueryDict對象,包含了 HTTP GET 的全部參數。
2.HttpRequest.POST
  一個QueryDict 對象,若是請求中包含表單數據,則將這些數據封裝成 QueryDict 對象。
  POST 請求能夠帶有空的POST字典。若是經過POST方法發送一個表單,可是表單中沒有任何的數據,QueryDict 對象依然會被建立。
   所以,不該該使用 if request.POST  來檢查使用的是不是POST 方法;應該使用 if request.method == "POST"
  另外:若是使用 POST 上傳文件的話,文件信息將包含在 FILES 屬性中。
     注意:鍵值對的值是多個的時候,好比checkbox類型的input標籤,select標籤,須要用:request.POST.getlist("hobby") 來獲取數據

3.HttpRequest.body
  一個字符串,表明請求報文的主體。在處理非 HTTP 形式的報文時很是有用,例如:二進制圖片、XML,Json等。
  可是,若是要處理表單數據的時候,推薦仍是使用 HttpRequest.POST 。

4.HttpRequest.path
  一個字符串,表示請求的路徑組件(不含域名)。
  例如:"/music/bands/the_beatles/"

5.HttpRequest.method
  一個字符串,表示請求使用的HTTP 方法。必須使用大寫。
  例如:"GET""POST"

6.HttpRequest.encoding
  一個字符串,表示提交的數據的編碼方式(若是爲 None 則表示使用 DEFAULT_CHARSET 的設置,默認爲 'utf-8')。
   這個屬性是可寫的,你能夠修改它來修改訪問表單數據使用的編碼。
   接下來對屬性的任何訪問(例如從 GET 或 POST 中讀取數據)將使用新的 encoding 值。
   若是你知道表單數據的編碼不是 DEFAULT_CHARSET ,則使用它。

7.HttpRequest.META
   一個標準的Python 字典,包含全部的HTTP 首部。具體的頭部信息取決於客戶端和服務器,下面是一些示例:
    CONTENT_LENGTH —— 請求的正文的長度(是一個字符串)。
    CONTENT_TYPE —— 請求的正文的MIME 類型。
    HTTP_ACCEPT —— 響應可接收的Content-Type。
    HTTP_ACCEPT_ENCODING —— 響應可接收的編碼。
    HTTP_ACCEPT_LANGUAGE —— 響應可接收的語言。
    HTTP_HOST —— 客服端發送的HTTP Host 頭部。
    HTTP_REFERER —— Referring 頁面。
    HTTP_USER_AGENT —— 客戶端的user-agent 字符串。
    QUERY_STRING —— 單個字符串形式的查詢字符串(未解析過的形式)。
    REMOTE_ADDR —— 客戶端的IP 地址。
    REMOTE_HOST —— 客戶端的主機名。
    REMOTE_USER —— 服務器認證後的用戶。
    REQUEST_METHOD —— 請求方法,一個字符串,例如"GET""POST"。
    SERVER_NAME —— 服務器的主機名。
    SERVER_PORT —— 服務器的端口(是一個字符串)。
   從上面能夠看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 以外,請求中的任何 HTTP 首部轉換爲 META 的鍵時,
    都會將全部字母大寫並將鏈接符替換爲下劃線最後加上 HTTP_  前綴。
    因此,一個叫作 X-Bender 的頭部將轉換成 META 中的 HTTP_X_BENDER 鍵。

8.HttpRequest.FILES
  一個相似於字典的對象,包含全部的上傳文件信息。
   FILES 中的每一個鍵爲<input type="file" name="" /> 中的name,值則爲對應的數據。
  注意,FILES 只有在請求的方法爲POST 且提交的<form> 帶有enctype="multipart/form-data" 的狀況下才會
   包含數據。不然,FILES 將爲一個空的相似於字典的對象。

9.HttpRequest.COOKIES
  一個標準的Python 字典,包含全部的cookie。鍵和值都爲字符串。

10.HttpRequest.session
   一個既可讀又可寫的相似於字典的對象,表示當前的會話。只有當Django 啓用會話的支持時纔可用。

11.HttpRequest.user(用戶認證組件下使用)
  一個 AUTH_USER_MODEL 類型的對象,表示當前登陸的用戶。
  若是用戶當前沒有登陸,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 中間件時纔可用。
     -------------------------------------------------------------------------------------
    匿名用戶
    class models.AnonymousUser
    django.contrib.auth.models.AnonymousUser 類實現了django.contrib.auth.models.User 接口,但具備下面幾個不一樣點:
    id 永遠爲None。
    username 永遠爲空字符串。
    get_username() 永遠返回空字符串。
    is_staff 和 is_superuser 永遠爲False。
    is_active 永遠爲 False。
    groups 和 user_permissions 永遠爲空。
    is_anonymous() 返回True 而不是False。
    is_authenticated() 返回False 而不是True。
    set_password()、check_password()、save() 和delete() 引起 NotImplementedError。
    New in Django 1.8:
    新增 AnonymousUser.get_username() 以更好地模擬 django.contrib.auth.models.User。
View Code

經常使用方法:

1.HttpRequest.get_full_path()
  返回 path,若是能夠將加上查詢字符串。
  例如:"/music/bands/the_beatles/?print=true"
2.HttpRequest.is_ajax()
  若是請求是經過XMLHttpRequest 發起的,則返回True,方法是檢查 HTTP_X_REQUESTED_WITH 相應的首部是不是字符串'XMLHttpRequest'。
  大部分現代的 JavaScript 庫都會發送這個頭部。若是你編寫本身的 XMLHttpRequest 調用(在瀏覽器端),你必須手工設置這個值來讓 is_ajax() 能夠工做。
  若是一個響應須要根據請求是不是經過AJAX 發起的,而且你正在使用某種形式的緩存例如Django 的 cache middleware,
   你應該使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 裝飾你的視圖以讓響應可以正確地緩存。
View Code

二、HttpResponse對象

1、響應對象主要有三種形式:
HttpResponse()
render()
redirect()

2、HttpResponse()
直接在括號內加上字符串便可返回,如HttpResponse("操做成功!")

3、render()
格式:render(request, template_name[, context])
 結合一個給定的模板和一個給定的上下文字典,將一個模板頁面中的模板語法進行渲染,最終渲染成一個html頁面做爲響應體。
參數:
request: 用於生成響應的請求對象。
template_name:要使用的模板的完整名稱
context:添加到模板上下文的一個字典。默認是一個空字典。若是字典中的某個值是可調用的,視圖將在渲染模板以前調用它。

4、redirect()
傳遞要重定向的一個硬編碼的URL
如:
def my_view(request):
    ...
    return redirect('/books/2/')
View Code

301和302重定向:

(1)301和302的區別。
  301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼後會自動跳轉到一個新的URL地址,這個地址能夠從響應的Location首部中獲取
  (用戶看到的效果就是他輸入的地址A瞬間變成了另外一個地址B)——這是它們的共同點。
  他們的不一樣在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),搜索引擎在抓取新內容的同時也將舊的網址交換爲重定向以後的網址;
  302表示舊地址A的資源還在(仍然能夠訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址。 

(2)重定向緣由:
    1)網站調整(如改變網頁目錄結構);
    2)網頁被移到一個新地址;
    3)網頁擴展名改變(如應用須要把.php改爲.Html或.shtml)。
        這種狀況下,若是不作重定向,則用戶收藏夾或搜索引擎數據庫中舊地址只能讓訪問客戶獲得一個404頁面錯誤信息,訪問流量白白喪失;再者某些註冊了多個域名的網站,也須要經過重定向讓訪問這些域名的用戶自動跳轉到主站點等。
View Code

 

4、Django的模板

集中存放HTML代碼文件的地方

一、Django模板語法——變量

在模板中調用變量:{{var_name}}

import datetime
from django.shortcuts import render


def index(request):
    s = "hello"
    l = [111, 222, 333]  # 列表
    dic = {"name": "amy", "age": 18}  # 字典
    date = datetime.date(1993, 5, 2)  # 日期對象

    class Person(object):
        def __init__(self, name):
            self.name = name

    person_wang = Person("wang")  # 自定義類對象
    person_li = Person("li")
    person_he = Person("he")

    person_list = [person_wang, person_li, person_he]

    return render(request, "index.html", {"s": s, "l": l, "dic": dic, "date": date, "person_list": person_list})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板語法之變量</title>
</head>
<body>
<div>
    <h4>字符串:{{ s }}</h4>
    <h4>列表:{{ l.0 }}</h4>
    <h4>列表:{{ l.2 }}</h4>
    <h4>字典:{{ dic.name }}</h4>
    <h4>日期:{{ date.year }}</h4>
    <h4>類對象列表:{{ person_list.0.name }}</h4>
</div>
</body>
</html>
index.html

二、模板語法——過濾器

語法:{{obj|filter__name:param}}

常見過濾器:
1、default
若是一個變量是false或者爲空,使用給定的默認值。不然,使用變量的值。例如:
{{ value|default:"nothing" }}

2、length
返回值的長度。它對字符串和列表都起做用。例如:
{{ value|length }}
若是 value 是 ['a', 'b', 'c', 'd'],那麼輸出是 43、date
若是 value=datetime.datetime.now(),想要顯示「2019-1-12」的格式,則寫爲:{{ value|date:"Y-m-d" }}

4、slice
經過字符串的index值來截取字符串
若是 value="hello world",{{ value|slice:"2:-1" }}將會顯示:llo worl

5、truncatechars
若是字符串字符多於指定的字符數量,那麼會被截斷。截斷的字符串將以可翻譯的省略號序列(「...」)結尾。
參數:要截斷的字符數
例如:value="hello world",{{ value|truncatechars:6  }}
結果:hel...

6、safe
Django的模板中會對HTML標籤和JS等語法標籤進行自動轉義,緣由顯而易見,這樣是爲了安全。可是有的時候咱們可能不但願這些HTML元素被轉義,好比咱們作一個內容管理系統,後臺添加的文章中是通過修飾的,這些修飾多是經過一個相似於FCKeditor編輯加註了HTML修飾符的文本,若是自動轉義的話顯示的就是保護HTML標籤的源文件。爲了在Django中關閉HTML的自動轉義有兩種方式,若是是一個單獨的變量咱們能夠經過過濾器「|safe」的方式告訴Django這段代碼是安全的沒必要轉義。好比:
value="<a href="">點擊</a>"
{{ value|safe}}
View Code

三、模板語法——標籤

語法:{% tag %}

用於一些在輸出中建立文本,一些經過循環或邏輯來控制流程,一些加載其後的變量將使用到的額外信息到模版中的狀況

(1)for標籤:遍歷每個元素

  • 遍歷字典:
{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}
  • 循環序號:{{forloop}}
forloop.counter       # 計數,index從1開始  ,如1,2,3 
forloop.counter0           # 計數,index從0開始,如 0,1,2
forloop.revcounter         # 計數,從最後一個index開始倒數到1,如3,2,1
forloop.revcounter0        # 計數,從最後一個index開始倒數到0,如2,1,0
forloop.first              # 判斷是否爲第一個元素,返回true或false
forloop.last              # 判斷是否爲最後一個元素,返回true或false

# 例:
{% for item in l %}
       <p>{{ forloop.counter }}</p>
{% endfor %}
  • for ... empty

for 標籤帶有一個可選的{% empty %} 從句,以便在給出的組是空的或者沒有被找到時,能夠有所操做。

{% for person in person_list %}
        {# 若是person_list不爲空就打印這裏: #}
            <p>{{ person.name }}</p>
{% empty %}
        {#  若是person_list爲空就打印這裏: #}
            <p>sorry,no person here</p>
{% endfor %}

(2)if 標籤
{% if %}會對一個變量求值,若是它的值是「True」(存在、不爲空、且不是boolean類型的false值),對應的內容塊會輸出。

{% if num > 100 or num < 0 %}
    <p>無效</p>
{% elif num > 80 and num < 100 %}
    <p>優秀</p>
{% else %}
    <p>湊活吧</p>
{% endif %}

(3)with

使用一個簡單地名字緩存一個複雜的變量,當你須要使用一個「昂貴的」方法(好比訪問數據庫)不少次的時候是很是有用的

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

(4)csrf_token

這個標籤用於跨站請求僞造保護

<form>
    {% csrf_token %}
    <input type="text" name="username"/>
    <input type="submit" value="提交"/>
</form>

使用這個標籤後,在發送form表單數據時就不會出現404 forbidden

(5)能夠利用 {% for obj in list reversed %} 反向完成循環。

四、自定義標籤和過濾器

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


(2)在app中建立templatetags模塊(模塊名只能是templatetags)
(3)建立任意 .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)
my_tags.py

(4)在使用自定義simple_tag和filter的html文件中導入以前建立的 my_tags.py

{% load my_tags %}

(5)使用simple_tag和filter

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

<body>
<div>
    {% load my_tags %}
{#     s是視圖中的變量,s=10 #}
    {{ s|filter_multi:2 }}
    {{ 3|filter_multi:3 }}
    {% simple_tag_multi 2 7 %} {# 參數不限,但不能放在if、for語句中 #}
    {% simple_tag_multi s 5 %}
    {% my_input s "username" %}
</div>
</body>
</html>
index.html

結果:

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

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

 

五、模板繼承

模版繼承可讓您建立一個基本的「骨架」模版,它包含您站點中的所有元素,而且能夠定義可以被子模版覆蓋的 blocks 。

語法:{% block name %} content {% endblock %}

name表示block的名稱,content表示內容

新建一個base.html的文檔做爲HTML基本骨架:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>
        {% block title %}My amazing site{% endblock %}
    </title>
</head>

<body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/index/">Blog</a></li>
        </ul>
        {% endblock %}
    </div>

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>
base.html

在這個例子中, block 標籤訂義了三個能夠被子模版內容填充的block。

導入和使用模板:

{% extends "base.html" %}
{% block title %}this is index{% endblock %}

{% block content %}
    <h4>in content block</h4>
{% endblock %}
index.html

注意:

  • 若是你在模版中使用 {% extends %} 標籤,它必須是模版中的第一個標籤。其餘的任何狀況下,模版繼承都將沒法工做。
  • 在base模版中設置越多的 {% block %} 標籤越好。請記住,子模版沒必要定義所有父模版中的blocks,因此,你能夠在大多數blocks中填充合理的默認內容,而後,只定義你須要的那一個。多一點鉤子總比少一點好。
  • 若是你發現你本身在大量的模版中複製內容,那可能意味着你應該把內容移動到父模版中的一個 {% block %} 中。
  • If you need to get the content of the block from the parent template, the variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.
  • 爲了更好的可讀性,你也能夠給你的{% endblock %} 標籤一個 名字 。例如:
{% block content %}
...
{% endblock content %}

在大型模版中,這個方法幫你清楚的看到哪個  {% block %} 標籤被關閉了。

  • 不能在一個模版中定義多個相同名字的 block 標籤。  

 

5、Django模型層

MVC或者MTV框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不須要依賴於特定的數據庫,經過簡單的配置就能夠輕鬆更換數據庫,這極大的減輕了開發人員的工做量,不須要面對因數據庫變動而致使的無效勞動
ORM是「對象-關係-映射」的簡稱。

一、mysql操做和Python類操做數據庫

MySQL:

# 建立數據庫
CREATE DATABASE db4 default character set utf8 collate utf8_general_ci;

# 建立數據表
CREATE TABLE employee(id INT PRIMARY KEY auto_increment ,
name VARCHAR (20), 
gender BIT default 1, 
birthday DATE , 
department VARCHAR (20), 
salary DECIMAL (8,2) unsigned);

# 插入數據
INSERT employee (name,gender,birthday,salary,department)
 VALUES   ("amy",1,"1999-11-11",8000,"開發部");

# 查詢
SELECT * FROM employee WHERE gender=1;

# 修改
UPDATE employee SET birthday="1993-10-24" WHERE id=1;

# 刪除
DELETE FROM employee WHERE name="amy";

Python類:

新建一個Django工程。

編寫類:

from django.db import models


class Employee(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    gender = models.BooleanField()
    birthday = models.DateField()
    department = models.CharField(max_length=32)
    salary = models.DecimalField(max_digits=8, decimal_places=2)
models.py

在terminal中作數據庫遷移:

python3 manage.py makemigrations

python3 manage.py migrate

編寫視圖:

from django.shortcuts import render, HttpResponse
from app01.models import Employee


def index(request):
    # 添加一條表紀錄(實例化類):
    emp = Employee(name="amy", gender=True, birthday="1999-12-12", department="開發部", salary=8000)
    emp.save()

    # 查詢一條表紀錄:
    Employee.objects.filter(gender=True)

    # 更新一條表紀錄:
    Employee.objects.filter(id=1).update(birthday="1999-10-24")

    # # 刪除一條表紀錄:
    Employee.objects.filter(name="amy").delete()
    return HttpResponse(".....")
views.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'^index/', views.index),
]
urls.py

二、單表操做

(1)建立Django項目,建立名爲app01的app,在app01的models.py中建立模型:

from django.db import models


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    state = models.BooleanField()
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.CharField(max_length=32)

字段:

<1> CharField
       字符串字段, 用於較短的字符串.
       CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所容許的最大字符數.

<2> IntegerField
      #用於保存一個整數.

<3> FloatField
       一個浮點數. 必須 提供兩個參數:

       參數    描述
       max_digits    總位數(不包括小數點和符號)
       decimal_places    小數位數
               舉例來講, 要保存最大值爲 999 (小數點後保存2位),你要這樣定義字段:

               models.FloatField(..., max_digits=5, decimal_places=2)
               要保存最大值一百萬(小數點後保存10位)的話,你要這樣定義:

               models.FloatField(..., max_digits=19, decimal_places=10)
               admin 用一個文本框(<input type="text">)表示該字段保存的數據.

<4> AutoField
       一個 IntegerField, 添加記錄時它會自動增加. 你一般不須要直接使用這個字段;
       自定義一個主鍵:my_id=models.AutoField(primary_key=True)
       若是你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.

<5> BooleanField
       A true/false field. admin 用 checkbox 來表示此類字段.

<6> TextField
       一個容量很大的文本字段.
       admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框).

<7> EmailField
       一個帶有檢查Email合法性的 CharField,不接受 maxlength 參數.

<8> DateField
       一個日期字段. 共有下列額外的可選參數:
       Argument    描述
       auto_now    當對象被保存時,自動將該字段的值設置爲當前時間.一般用於表示 "last-modified" 時間戳.
       auto_now_add    當對象首次被建立時,自動將該字段的值設置爲當前時間.一般用於表示對象建立時間.
       (僅僅在admin中有意義...)

<9> DateTimeField
        一個日期時間字段. 相似 DateField 支持一樣的附加選項.

<10> ImageField
       相似 FileField, 不過要校驗上傳對象是不是一個合法圖片.#它有兩個可選參數:height_field和width_field,
       若是提供這兩個參數,則圖片將按提供的高度和寬度規格保存.    
<11> FileField
    一個文件上傳字段.
    要求一個必須有的參數: upload_to, 一個用於保存上載文件的本地文件系統路徑. 這個路徑必須包含 strftime #formatting,
    該格式將被上載文件的 date/time
    替換(so that uploaded files don't fill up the given directory).
    admin 用一個<input type="file">部件表示該字段保存的數據(一個文件上傳部件) .

    注意:在一個 model 中使用 FileField 或 ImageField 須要如下步驟:
           (1)在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件.
           (出於性能考慮,這些文件並不保存到數據庫.) 定義MEDIA_URL 做爲該目錄的公共 URL. 要確保該目錄對
            WEB服務器用戶賬號是可寫的.
           (2) 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django
            使用 MEDIA_ROOT 的哪一個子目錄保存上傳文件.你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT).
            出於習慣你必定很想使用 Django 提供的 get_<#fieldname>_url 函數.舉例來講,若是你的 ImageField
            叫做 mug_shot, 你就能夠在模板中以 {{ object.#get_mug_shot_url }} 這樣的方式獲得圖像的絕對路徑.

<12> URLField
     用於保存 URL. 若 verify_exists 參數爲 True (默認), 給定的 URL 會預先檢查是否存在( 即URL是否被有效裝入且
     沒有返回404響應).
     admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)

<13> NullBooleanField
      相似 BooleanField, 不過容許 NULL 做爲其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項
      admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes""No" ) 來表示這種字段數據.

<14> SlugField
      "Slug" 是一個報紙術語. slug 是某個東西的小小標記(短籤), 只包含字母,數字,下劃線和連字符.#它們一般用於URLs
      若你使用 Django 開發版本,你能夠指定 maxlength. 若 maxlength 未指定, Django 會使用默認長度: 50.  #
      之前的 Django 版本,沒有任何辦法改變50 這個長度.
      這暗示了 db_index=True.
      它接受一個額外的參數: prepopulate_from, which is a list of fields from which to auto-#populate
      the slug, via JavaScript,in the object's admin form: models.SlugField
      (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.

<13> XMLField
       一個校驗值是否爲合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema #的文件系統路徑.

<14> FilePathField
       可選項目爲某個特定目錄下的文件名. 支持三個特殊的參數, 其中第一個是必須提供的.
       參數    描述
       path    必需參數. 一個目錄的絕對文件系統路徑. FilePathField 據此獲得可選項目.
       Example: "/home/images".
       match    可選參數. 一個正則表達式, 做爲一個字符串, FilePathField 將使用它過濾文件名.
       注意這個正則表達式只會應用到 base filename 而不是
       路徑全名. Example: "foo.*\.txt^", 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif.
       recursive可選參數.要麼 True 要麼 False. 默認值是 False. 是否包括 path 下面的所有子目錄.
       這三個參數能夠同時使用.
       match 僅應用於 base filename, 而不是路徑全名. 那麼,這個例子:
       FilePathField(path="/home/images", match="foo.*", recursive=True)
       ...會匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif

<15> IPAddressField
       一個字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16> CommaSeparatedIntegerField
       用於存放逗號分隔的整數值. 相似 CharField, 必需要有maxlength參數.
View Code

參數:

(1)null

若是爲True,Django 將用NULL 來在數據庫中存儲空值。 默認值是 False.

(1)blank

若是爲True,該字段容許不填。默認爲False。
要注意,這與 null 不一樣。null純粹是數據庫範疇的,而 blank 是數據驗證範疇的。
若是一個字段的blank=True,表單的驗證將容許該字段是空值。若是字段的blank=False,該字段就是必填的。

(2)default

字段的默認值。能夠是一個值或者可調用對象。若是可調用 ,每有新對象被建立它都會被調用。

(3)primary_key

若是爲True,那麼這個字段就是模型的主鍵。若是你沒有指定任何一個字段的primary_key=True,
Django 就會自動添加一個IntegerField字段作爲主鍵,因此除非你想覆蓋默認的主鍵行爲,
不然不必設置任何一個字段的primary_key=True。

(4)unique

若是該值設置爲 True, 這個數據字段的值在整張表中必須是惟一的

(5)choices
由二元組組成的一個可迭代對象(例如,列表或元組),用來給字段提供選擇項。 若是設置了choices ,默認的表單將是一個選擇框而不是標準的文本框,<br>並且這個選擇框的選項就是choices 中的選項。
View Code

(2)settings配置

若想將模型轉爲mysql數據庫中的表,須要在settings中配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db4',  # 要鏈接的數據庫,鏈接前須要建立好
        'USER': 'root',  # 鏈接數據庫的用戶名
        'PASSWORD': '123456789',  # 鏈接數據庫的密碼
        'HOST': '127.0.0.1',  # 鏈接主機,默認本級
        'PORT': 3306  # 端口 默認3306
    }
}

對於Python3 使用PyMySQL來驅動MySQL數據庫,找到項目名文件下的init.py文件,在裏面寫入:

import pymysql
pymysql.install_as_MySQLdb()

在pycharm的終端執行數據庫遷移命令便可在指定的數據庫中建立表 :

python3 manage.py makemigrations
python3 manage.py migrate

注意:

  • 要確保配置文件中的INSTALLED_APPS中寫入咱們建立的app名稱:
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "app01"
]
  • 若是報錯以下:
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

MySQLclient目前只支持到python3.4,所以若是使用的更高版本的python,須要修改以下:

經過查找路徑C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql 這個路徑裏的文件把:

if version < (1, 3, 3):
     raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

註釋掉 就OK了。

  • 若是想打印orm轉換過程當中的sql,須要在settings中進行以下配置:
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

(3)添加表記錄

方法1:

book_obj = Book.objects.create(title="葵花寶典", state=True, price=100, publish="蘋果出版社", pub_date="2012-12-12")

方法2:

book_obj=Book(title="python教程",state=True,price=100,publish="蘋果出版社",pub_date="2012-12-12")
book_obj.save()

(4)查詢記錄

查詢API:

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

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

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

<4> exclude(**kwargs):      它包含了與所給篩選條件不匹配的對象

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

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

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

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

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

<11> exists():              若是QuerySet包含數據,就返回True,不然返回False

<12> values(*field):        返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列
                            model的實例化對象,而是一個可迭代的字典序列
<13> values_list(*field):   它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列

<14> distinct():            從返回結果中剔除重複紀錄
View Code

基於雙下劃線的模糊查詢:

Book.objects.filter(price__in=[100,200,300])  # 價格在列表裏面的記錄
Book.objects.filter(price__gt=100)  # 價格大於100
Book.objects.filter(price__lt=100)  # 價格小於100
Book.objects.filter(price__range=[100,200])  # 區間
Book.objects.filter(title__contains="python")  # 包含,大小寫敏感
Book.objects.filter(title__icontains="python")  # 包含,大小寫不敏感
Book.objects.filter(title__startswith="py")  # 以xx開頭,大小寫敏感
Book.objects.filter(pub_date__year=2012)  # 2012年出版的
View Code

(5)刪除記錄

刪除方法就是 delete(),它運行時當即刪除對象,返回刪除對象:

model_obj.delete()

例:

res = Book.objects.filter(id=1).delete()  # 刪除id=1 的記錄
print(res)  #結果: (1, {'app01.Book': 1})

也能夠一次性刪除多個對象。每一個 QuerySet 都有一個 delete() 方法,它會一次性刪除 QuerySet 中全部的對象。

如:刪除2005年出版的全部書籍

Book.objects.filter(pub_date__year=2005).delete()

要注意的是: delete() 方法是 QuerySet 上的方法,但並不適用於 Manager 自己。這是一種保護機制,是爲了不意外地調用 Book.objects.delete() 方法致使全部的記錄被誤刪除。若是你確認要刪除全部的對象,那麼你必須顯式地調用:

Book.objects.all().delete()

另外,在 Django 刪除對象時,會模仿 SQL 約束 ON DELETE CASCADE 的行爲,即在刪除一個對象時也會刪除與它相關聯的外鍵對象。若是不想級聯刪除,能夠設置on_delete參數:

pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

on_delete的models屬性:

CASCADE:這就是默認的選項,級聯刪除,你無需顯性指定它。
PROTECT: 保護模式,若是採用該選項,刪除的時候,會拋出ProtectedError錯誤。
SET_NULL: 置空模式,刪除的時候,外鍵字段被設置爲空,前提就是blank=True, null=True,定義該字段的時候,容許爲空。
SET_DEFAULT: 置默認值,刪除的時候,外鍵字段設置爲默認值,因此定義外鍵的時候注意加上一個默認值。
SET(): 自定義一個值,該值固然只能是對應的實體了

**set()官方案例**
def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET(get_sentinel_user),
    )

(6)修改記錄

Book.objects.filter(title__startswith="py").update(price=120)

(7)代碼:

from django.shortcuts import render, HttpResponse
from app01.models import Book


def index(request):
    # 一、建立記錄--------------------------------------------------
    # 方法1:
    # create方法的返回值book_obj就是插入book表中的葵花寶典這本書籍對象
    book_obj = Book.objects.create(title="葵花寶典", state=True, price=100, publish="蘋果出版社", pub_date="2012-12-12")

    # 方法2:
    book_obj = Book(title="python教程", state=True, price=100, publish="蘋果出版社", pub_date="2012-12-12")
    book_obj.save()

    # 二、查詢記錄--------------------------------------------------------
    print(Book.objects.all())
    print(Book.objects.filter(title__contains="python"))

    # 三、刪除記錄--------------------------------------------------------
    res = Book.objects.filter(id=1).delete()  # 刪除id=1 的記錄
    print(res)  # (1, {'app01.Book': 1})

    # 四、修改記錄--------------------------------------------------------
    Book.objects.filter(title__startswith="py").update(price=120)

    return HttpResponse(".....")
views.py

三、多表操做

(1)建立模型

實例:
做者模型(Author):一個做者有姓名和年齡。
做者詳細模型(AuthorDetail):把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(one-to-one)
出版商模型(Publish):出版商有名稱,所在城市以及email。

書籍模型(Book): 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);

一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)。

from django.db import models


class Author(models.Model):
    """做者"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    # 與AuthorDetail創建一對一的關係
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    """做者詳細"""
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField()
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=64)


class Publish(models.Model):
    """出版社"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()


class Book(models.Model):
    """書籍"""
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)

    # 與Publish創建一對多的關係,外鍵字段創建在多的一方
    publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.SET_NULL, null=True, blank=True)
    # 與Author表創建多對多的關係,ManyToManyField能夠建在兩個模型中的任意一個,自動建立第三張表
    authors = models.ManyToManyField(to='Author', )
models.py

(2)添加表記錄

一對多或一對一:

方式1:
   publish_obj=Publish.objects.get(nid=1)  # 獲取出版對象
   book_obj=Book.objects.create(title="python",publishDate="2012-12-12",price=100,publish=publish_obj)  # 將對象賦值給publish

方式2:
   book_obj=Book.objects.create(title="python",publishDate="2012-12-12",price=100,publish_id=1)  # 將出版對象對應的id賦值給publish_id

多對多:

    # 一、獲取當前生成的書籍對象
    book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1)
    # 二、要爲書籍綁定的做者對象
    li=Author.objects.filter(name="li").first() # 在Author表中name爲li的紀錄
    wang=Author.objects.filter(name="wang").first() # 在Author表中name爲wang的紀錄

    # 三、綁定多對多關係,即向關係表book_authors中添加紀錄
    book_obj.authors.add(li, wang)    #  將某些特定的 model 對象添加到被關聯對象集合中。      等價於: book_obj.authors.add(*[])

多對多關係其它經常使用API:

book_obj.authors.remove()      # 將某個特定的對象從被關聯對象集合中去除。    等價於:  book_obj.authors.remove(*[])
book_obj.authors.clear()       #清空被關聯對象集合
book_obj.authors.set()         #先清空再設置

補充: class RelatedManager

"關聯管理器"是在一對多或者多對多的關聯上下文中使用的管理器。它存在於下面兩種狀況:
ForeignKey關係的「另外一邊」。像這樣:

from django.db import models

class Reporter(models.Model):
    # ...
    pass

class Article(models.Model):
    reporter = models.ForeignKey(Reporter)

在上面的例子中,管理器reporter.article_set擁有下面的方法。
ManyToManyField關係的兩邊:

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)

這個例子中,topping.pizza_set 和pizza.toppings都擁有下面的方法。
add(obj1[, obj2, ...])

把指定的模型對象添加到關聯對象集中。

例如:

>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.add(e) # Associates Entry e with Blog b.
在上面的例子中,對於ForeignKey關係,e.save()由關聯管理器調用,執行更新操做。然而,在多對多關係中使用add()並不會調用任何 save()方法,而是由QuerySet.bulk_create()建立關係。

延伸:

# 1 *[]的使用
>>> book_obj = Book.objects.get(id=1)
>>> author_list = Author.objects.filter(id__gt=2)
>>> book_obj.authors.add(*author_list)


# 2 直接綁定主鍵
book_obj.authors.add(*[1,3])  # 將id=1和id=3的做者對象添加到這本書的做者集合中
                              # 應用: 添加或者編輯時,提交做者信息時能夠用到.

create(**kwargs)

#建立一個新的對象,保存對象,並將它添加到關聯對象集之中。返回新建立的對象:

>>> b = Blog.objects.get(id=1)
>>> e = b.entry_set.create(
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )

# No need to call e.save() at this point -- it's already been saved.
這徹底等價於(不過更加簡潔於):

>>> b = Blog.objects.get(id=1)
>>> e = Entry(
...     blog=b,
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )
>>> e.save(force_insert=True)
#要注意咱們並不須要指定模型中用於定義關係的關鍵詞參數。在上面的例子中,咱們並無傳入blog參數給create()。Django會明白新的 Entry對象blog 應該添加到b中。

remove(obj1[, obj2, ...])

#從關聯對象集中移除執行的模型對象:

>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.remove(e) # Disassociates Entry e from Blog b.
#對於ForeignKey對象,這個方法僅在null=True時存在。

clear()

#從關聯對象集中移除一切對象。

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.clear()
#注意這樣不會刪除對象 —— 只會刪除他們之間的關聯。

#就像 remove() 方法同樣,clear()只能在 null=True的ForeignKey上被調用。

set()方法
先清空,再設置,編輯書籍時便可用到

注意
對於全部類型的關聯字段,add()、create()、remove()和clear(),set()都會立刻更新數據庫。換句話說,在關聯的任何一端,都不須要再調用save()方法。
直接賦值:
經過賦值一個新的可迭代的對象,關聯對象集能夠被總體替換掉。

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set = new_list

若是外鍵關係知足null=True,關聯管理器會在添加new_list中的內容以前,首先調用clear()方法來解除關聯集中一切已存在對象的關聯。不然, new_list中的對象會在已存在的關聯的基礎上被添加。

(3)基於對象的跨表查詢

  • 一對多查詢(Publish 與 Book)

正向查詢(按字段:publish):

#查詢主鍵爲1的書籍的出版社所在的城市
book_obj = models.Book.objects.filter(nid=1).first()
city = book_obj.publish.city
print(city)

反向查詢(按表名:book_set):

# 查詢與蘋果出版社關聯的全部書籍對象集合
publish_obj = models.Publish.objects.get(name="蘋果出版社")
book_list = publish_obj.book_set.all()
for book_obj in book_list:
    print(book_obj.title)
  • 一對一查詢(Author 與 AuthorDetail)

正向查詢(按字段:authorDetail):

#查詢張三的手機號
tel = models.Author.objects.get(name="張三").authorDetail.telephone
print(tel)

反向查詢(按表名:author):

# 查詢全部住址在上海的做者的姓名
author_detail_list = models.AuthorDetail.objects.filter(addr="上海")
for author_detail_obj in author_detail_list:
    print(author_detail_obj.author.name)
  • 多對多查詢 (Author 與 Book)

正向查詢(按字段:authors):

# 查詢python全部做者的名字以及手機號
authors = models.Book.objects.get(title="python").authors.all()
for author_obj in authors:
    print(author_obj.name, author_obj.authorDetail.telephone)

反向查詢(按表名:book_set):

#查詢jack出過的全部書籍的名字
book_list = models.Author.objects.filter(name="jack").first().book_set.all()
for book in book_list:
    print(book.title)

注意:
你能夠經過在 ForeignKey() 和ManyToManyField()的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,若是 Article model 中作一下更改:

publish = ForeignKey(Book, related_name='bookList')

這樣就能夠直接經過 book_list = models.Author.objects.filter(name="jack").first().bookList.all() 來獲取對象。

(4)基於雙下劃線的跨表查詢

使用兩個下劃線來連接模型(model)間關聯字段的名稱,直到最終連接到你想要的 model 爲止。

關鍵點:正向查詢按字段,反向查詢按表名。

  • 一對多
# 查詢蘋果出版社出版過的全部書籍的名字與價格
# 正向查詢
books1 = models.Book.objects.filter(publish__name="蘋果出版社").values("title", "price")
print(books1)
# 反向查詢
books2 = models.Publish.objects.filter(name="蘋果出版社").values("book__title", "book__price")
print(books2)
  • 多對多
# 查詢jack出過的全部書籍的名字
# 正向查詢
books1 = models.Book.objects.filter(authors__name="jack").values("title")
print(books1)
# 反向查詢
books2 = models.Author.objects.filter(name="jack").values("book__title")
print(books2)
  • 混合查詢
# 查詢橘子出版社出版過的全部書籍的名字以及做者的姓名
# 正向查詢
info_list = models.Book.objects.filter(publish__name="橘子出版社").values("title", "authors__name")
print(info_list)
# 反向查詢
info = models.Publish.objects.filter(name="橘子出版社").values("book__title", "book__authors__name")
print(info)

# 手機號以2開頭的做者出版過的全部書籍名稱以及出版社名稱
# 正向查詢
info1 = models.AuthorDetail.objects.filter(telephone__startswith=2).values("author__book__title",
                                                                           "author__book__publish__name")
print(info1)
# 反向查詢
info2 = models.Book.objects.filter(authors__authorDetail__telephone__startswith=2).values("title", "publish__name")
print(info2)

 

(5)聚合查詢

aggregate(*args, **kwargs)

# 計算全部圖書的平均價格
from django.db.models import Avg
avg_price = models.Book.objects.all().aggregate(Avg('price'))
print(avg_price)  # {'price__avg': 113.333333}

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

from django.db.models import Avg
avg_price = models.Book.objects.all().aggregate(avg_price=Avg('price'))
print(avg_price)  # {'avg_price': 113.333333}

向aggregate()子句中添加參數:

from django.db.models import Avg, Max, Min
res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
print(res)  # {'price__avg': 113.333333, 'price__max': Decimal('120.00'), 'price__min': Decimal('100.00')}

(6)分組查詢 annotate(*args, **kwargs)

在models.py中添加以下模型:

class Employee(models.Model):
    """員工表"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    dep = models.ForeignKey(to="Department", on_delete=models.SET_NULL, null=True, blank=True)


class Department(models.Model):
    """部門表"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)

數據庫遷移,並在表中添加數據:

單表查詢:

# 查詢每個部門以及對應的員工數
"""
SQL查詢:
select dep, Count(*) from employee group by dep;
"""
from django.db.models import Count
info = models.Employee.objects.all().values("dep").annotate(Count("nid"))
print(info)
"""
結果:
<QuerySet [
    {'dep': 1, 'nid__count': 2}, 
    {'dep': 2, 'nid__count': 3}, 
    {'dep': 3, 'nid__count': 2}
]>
"""

多表查詢:

# 查詢每個部門名稱以及對應的員工數
"""
SQL查詢:
select dep.name, Count(*) from employee
left join department on employee.dep_id=department.nid
group by department.name;
"""
from django.db.models import Count
info = models.Department.objects.all().annotate(c=Count("employee")).values("name", "c")
print(info)
"""
結果:
<QuerySet [
    {'name': '財務部', 'c': 2}, 
    {'name': '開發部', 'c': 3}, 
    {'name': '人力資源部', 'c': 2}
]>
"""

 

# 統計每一本書的做者個數
res = models.Book.objects.all().annotate(count=Count("authors")).values("title", "count")
print(res)

# 統計每個出版社的最便宜的書
res = models.Publish.objects.all().annotate(min_price=Min("book__price"))
for obj in res:
    print(obj.name, obj.min_price)

# 統計每一本以Py開頭的書籍的做者個數
res = models.Book.objects.filter(title__startswith="Py").annotate(count=Count("authors")).values("title", "count")
print(res)

# 統計不止一個做者的圖書
res = models.Book.objects.all().annotate(author_num=Count("authors")).\
    filter(author_num__gt=1).values("title", "author_num")
print(res)

# 根據一本圖書做者數量的多少對查詢集QuerySet進行排序
res = models.Book.objects.all().annotate(author_num=Count("authors")).\
    order_by("author_num").values("title", "author_num")
print(res)

# 查詢各個做者出的書的總價格
res = models.Author.objects.all().annotate(sum=Sum("book__price")).values("name", "sum")
print(res)

 

(7)F查詢

  • 比較同一個 model 實例中兩個不一樣字段的值
# 查詢評論數大於收藏數的書籍

from django.db.models import F
Book.objects.filter(commnetNum__gt=F('keepNum'))
  • F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做
# 查詢評論數大於收藏數2倍的書籍
Book.objects.filter(commnetNum__gt=F('keepNum')*2)
#將每一本書的價格提升30元:
Book.objects.all().update(price=F("price")+30)

(8)Q查詢

filter() 等方法中的關鍵字參數查詢都是一塊兒進行「AND」 的。 若是你須要執行更復雜的查詢(例如OR 語句),你可使用Q 對象。

Q 對象可使用 &(and) 和 |(或) 操做符組合起來。當一個操做符在兩個Q 對象上使用時,它產生一個新的Q 對象。

from django.db.models import Q
# 查詢做者爲jack或者wang的書籍
bookList=Book.objects.filter(Q(authors__name="jack")|Q(authors__name="wang"))

Q 對象可使用 ~ 操做符取反,這容許組合正常的查詢和取反(NOT) 查詢:

# 查詢做者叫wang而且出版時間不在2017年的書籍
bookList=Book.objects.filter(Q(authors__name="wang") & ~Q(publishDate__year=2017)).values_list("title")

查詢函數能夠混合使用Q 對象和關鍵字參數。全部提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND」在一塊兒。可是,若是出現Q 對象,它必須位於全部關鍵字參數的前面。

# 2016年或2017年出版的名字包含python的書籍
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")
from django.shortcuts import render, HttpResponse
from django.db.models import Count, Min, Sum
from app01 import models


def index2(request):
    # 一、建立記錄--------------------------------------------------
    detail_obj1 = models.AuthorDetail.objects.create(birthday="1963-12-10", telephone=128938383838, addr="上海")
    detail_obj2 = models.AuthorDetail.objects.create(birthday="1976-3-23", telephone=338383838, addr="南京")
    detail_obj3 = models.AuthorDetail.objects.create(birthday="1977-12-3", telephone=2348383838, addr="廣州")

    publish_obj1 = models.Publish.objects.create(name="蘋果出版社", city="天津", email="cddd@163.com")
    publish_obj2 = models.Publish.objects.create(name="橘子出版社", city="深圳", email="wewe@163.com")
    publish_obj3 = models.Publish.objects.create(name="北京出版社", city="北京", email="sff@163.com")

    author_obj1 = models.Author.objects.create(name="mark", age=43, authorDetail_id=2)  # 經過id添加外鍵,authorDetail_id表示id
    author_obj2 = models.Author.objects.create(name="張三", age=42,
                                               authorDetail=detail_obj3)  # 經過對象添加外鍵,authorDetail表示一個做者詳情對象
    author_obj3 = models.Author.objects.create(name="jack", age=56, authorDetail_id=1)

    book_obj1 = models.Book.objects.create(title="Python", publishDate="2012-12-12", price=100, publish=publish_obj1)
    book_obj2 = models.Book.objects.create(title="java", publishDate="2014-12-12", price=120, publish_id=2)
    book_obj3 = models.Book.objects.create(title="數據庫", publishDate="2014-3-12", price=120, publish_id=3)
    # 多對多新增記錄:
    book_obj1.authors.add(author_obj1, author_obj2)
    book_obj2.authors.add(author_obj2, author_obj3)
    book_obj3.authors.add(author_obj3)

    # 二、基於對象的跨表查詢--------------------------------------------------------
    # 一對多
    # 正向查詢:按字段  查詢主鍵爲1的書籍的出版社所在的城市
    book_obj = models.Book.objects.filter(nid=1).first()
    city = book_obj.publish.city
    print(city)

    # 反向查詢 :按表名:book_set 查詢與蘋果出版社關聯的全部書籍對象集合
    publish_obj = models.Publish.objects.get(name="蘋果出版社")
    book_list = publish_obj.book_set.all()
    for book_obj in book_list:
        print(book_obj.title)

    # 一對一
    # 正向查詢:按字段 查詢張三的手機號
    tel = models.Author.objects.get(name="張三").authorDetail.telephone
    print(tel)

    # 反向查詢(按表名:author):查詢全部住址在上海的做者的姓名
    author_detail_list = models.AuthorDetail.objects.filter(addr="上海")
    for author_detail_obj in author_detail_list:
        print(author_detail_obj.author.name)

    # 多對多
    # 正向查詢(按字段:authors):查詢python全部做者的名字以及手機號
    authors = models.Book.objects.get(title="python").authors.all()
    for author_obj in authors:
        print(author_obj.name, author_obj.authorDetail.telephone)

    # 反向查詢(按表名:book_set):查詢jack出過的全部書籍的名字
    book_list = models.Author.objects.filter(name="jack").first().book_set.all()
    for book in book_list:
        print(book.title)

    # 三、基於雙下劃線的跨表查詢--------------------------------------------------------
    # 一對多
    # 查詢蘋果出版社出版過的全部書籍的名字與價格
    # 正向查詢
    books1 = models.Book.objects.filter(publish__name="蘋果出版社").values("title", "price")
    print(books1)
    # 反向查詢
    books2 = models.Publish.objects.filter(name="蘋果出版社").values("book__title", "book__price")
    print(books2)

    # 多對多
    # 查詢jack出過的全部書籍的名字
    # 正向查詢
    books1 = models.Book.objects.filter(authors__name="jack").values("title")
    print(books1)
    # 反向查詢
    books2 = models.Author.objects.filter(name="jack").values("book__title")
    print(books2)

    # 混合使用
    # 查詢橘子出版社出版過的全部書籍的名字以及做者的姓名
    # 正向查詢
    info_list = models.Book.objects.filter(publish__name="橘子出版社").values("title", "authors__name")
    print(info_list)
    # 反向查詢
    info = models.Publish.objects.filter(name="橘子出版社").values("book__title", "book__authors__name")
    print(info)

    # 手機號以2開頭的做者出版過的全部書籍名稱以及出版社名稱
    # 正向查詢
    info1 = models.AuthorDetail.objects.filter(telephone__startswith=2).values("author__book__title",
                                                                               "author__book__publish__name")
    print(info1)
    # 反向查詢
    info2 = models.Book.objects.filter(authors__authorDetail__telephone__startswith=2).values("title", "publish__name")
    print(info2)

    # 四、聚合查詢--------------------------------------------------------
    # 計算全部圖書的平均價格
    from django.db.models import Avg
    avg_price = models.Book.objects.all().aggregate(avg_price=Avg('price'))
    print(avg_price)  # {'avg_price': 113.333333}

    from django.db.models import Avg, Max, Min
    res = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    print(res)  # {'price__avg': 113.333333, 'price__max': Decimal('120.00'), 'price__min': Decimal('100.00')}

    # 五、分組查詢----------------------------------------------------------
    # 查詢每個部門以及對應的員工數
    """
    SQL查詢:
    select dep, Count(*) from employee group by dep;
    """
    from django.db.models import Count
    info = models.Employee.objects.all().values("dep").annotate(Count("nid"))
    print(info)
    """
    結果:
    <QuerySet [
        {'dep': 1, 'nid__count': 2},
        {'dep': 2, 'nid__count': 3},
        {'dep': 3, 'nid__count': 2}
    ]>
    """

    # 查詢每個部門名稱以及對應的員工數
    """
    SQL查詢:
    select dep.name, Count(*) from employee
    left join department on employee.dep_id=department.nid
    group by department.name;
    """
    from django.db.models import Count
    info = models.Department.objects.all().annotate(c=Count("employee")).values("name", "c")
    print(info)
    """
    結果:
    <QuerySet [
        {'name': '財務部', 'c': 2},
        {'name': '開發部', 'c': 3},
        {'name': '人力資源部', 'c': 2}
    ]>
    """

    # 統計每一本書的做者個數
    res = models.Book.objects.all().annotate(count=Count("authors")).values("title", "count")
    print(res)

    # 統計每個出版社的最便宜的書
    res = models.Publish.objects.all().annotate(min_price=Min("book__price"))
    for obj in res:
        print(obj.name, obj.min_price)

    # 統計每一本以Py開頭的書籍的做者個數
    res = models.Book.objects.filter(title__startswith="Py").annotate(count=Count("authors")).values("title", "count")
    print(res)

    # 統計不止一個做者的圖書
    res = models.Book.objects.all().annotate(author_num=Count("authors")).\
        filter(author_num__gt=1).values("title", "author_num")
    print(res)

    # 根據一本圖書做者數量的多少對查詢集QuerySet進行排序
    res = models.Book.objects.all().annotate(author_num=Count("authors")).\
        order_by("author_num").values("title", "author_num")
    print(res)

    # 查詢各個做者出的書的總價格
    res = models.Author.objects.all().annotate(sum=Sum("book__price")).values("name", "sum")
    print(res)

    return HttpResponse("ok")
views.py

6、Django組件——cookie與session

一、會話跟蹤技術

在JavaWeb中,客戶向某一服務器發出第一個請求開始,會話就開始了,直到客戶關閉了瀏覽器,會話結束。 在一個會話的多個請求中共享數據,這就是會話跟蹤技術。

會話跟蹤技術使用Cookie或session完成。

咱們知道HTTP協議是無狀態協議,也就是說每一個請求都是獨立的!沒法記錄前一次請求的狀態。但HTTP協議中可使用Cookie來完成會話跟蹤!在Web開發中,使用session來完成會話跟蹤,session底層依賴Cookie技術。

二、cookie

(1)什麼是cookie?

Cookie翻譯成中文是小甜點,小餅乾的意思。在HTTP中它表示服務器送給客戶端瀏覽器的小甜點。其實Cookie是key-value結構,相似於一個python中的字典。

當瀏覽器第一次訪問服務器,由服務器建立Cookie,而後經過響應發送給客戶端。客戶端會保存Cookie,並會標註出Cookie的來源(哪一個服務器的Cookie)。當客戶端再次向服務器發出請求時會把全部這個服務器Cookie包含在請求中發送給服務器,這樣服務器就能夠識別客戶端了!

(2)cookie規範

  • Cookie大小上限爲4KB;
  • 一個服務器最多在客戶端瀏覽器上保存20個Cookie;
  • 一個瀏覽器最多保存300個Cookie;
    上面的數據只是HTTP的Cookie規範,但在瀏覽器大戰的今天,一些瀏覽器爲了戰勝對手,爲了展示本身的能力起見,可能對Cookie規範「擴展」了一些,例如每一個Cookie的大小爲8KB,最多可保存500個Cookie等!但也不會出現把你硬盤佔滿的可能!
  • 不一樣瀏覽器之間是不共享Cookie的。也就是說在你使用IE訪問服務器時,服務器會把Cookie發給IE,而後由IE保存起來,當你在使用FireFox訪問服務器時,不可能把IE保存的Cookie發送給服務器。

(3)Cookie與HTTP頭
Cookie是經過HTTP請求頭和響應頭在客戶端和服務器端傳遞的:

  • Cookie:請求頭,客戶端發送給服務器端;
  • 格式:Cookie: a=A; b=B; c=C。即多個Cookie用分號分隔,
  • Set-Cookie:響應頭,服務器端發送給客戶端;
  • 一個Cookie對象一個Set-Cookie: Set-Cookie: a=A Set-Cookie: b=B Set-Cookie: c=C
  • Cookie的覆蓋

若是服務器端發送重複的Cookie那麼會覆蓋原有的Cookie,例如客戶端的第一個請求服務器端發送的Cookie是:Set-Cookie: a=A;第二請求服務器端發送的是:Set-Cookie: a=AA,那麼客戶端只留下一個Cookie,即:a=AA。

(4)django中的cookie語法

  • 設置cookie:
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect()  
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密鹽',...)

源碼:

class HttpResponseBase:
        def set_cookie(self, key,                 鍵
                     value='',            值
                     max_age=None,        超長時間
                              cookie須要延續的時間(以秒爲單位)
                              若是參數是\ None`` ,這個cookie會延續到瀏覽器關閉爲止。
                     expires=None,        超長時間
                                 expires默認None ,cookie失效的實際日期/時間。
                                
                     path='/',           Cookie生效的路徑,
                                                 瀏覽器只會把cookie回傳給帶有該路徑的頁面,這樣能夠避免將
                                                 cookie傳給站點中的其餘的應用。
                                                 / 表示根路徑,特殊的:根路徑的cookie能夠被任何url的頁面訪問
                    
                             domain=None,         Cookie生效的域名

                                                  你可用這個參數來構造一個跨站cookie。
                                                  如, domain=".example.com"
                                                  所構造的cookie對下面這些站點都是可讀的:
                                                  www.example.com 、 www2.example.com
                                 和an.other.sub.domain.example.com 。
                                                  若是該參數設置爲 None ,cookie只能由設置它的站點讀取。
                     secure=False,        若是設置爲 True ,瀏覽器將經過HTTPS來回傳cookie。
                     httponly=False       只能http協議傳輸,沒法被JavaScript獲取
                                                 (不是絕對,底層抓包能夠獲取到也能夠被覆蓋)
                  ): pass
  • 獲取cookie:
request.COOKIES
  • 刪除cookie:
response.delete_cookie("cookie_key",path="/",domain=name)

 

(5)案例1:顯示上次訪問時間。 
(6)案例2:顯示上次瀏覽過的商品。

三、session

(1)session介紹

Session是服務器端技術,利用這個技術,服務器在運行時能夠爲每個用戶的瀏覽器建立一個其獨享的session對象,因爲 session爲用戶瀏覽器獨享,因此用戶在訪問服務器的web資源時 ,能夠把各自的數據放在各自的session中,當用戶再去訪問該服務器中的其它web資源時,其它web資源再從用戶各自的session中 取出數據爲用戶服務。

(2)django中session語法

#一、設置Sessions值
          request.session['session_name'] ="admin"
#二、獲取Sessions值
          session_name = request.session["session_name"]
#三、刪除Sessions值
          del request.session["session_name"]
#四、刪除當前的會話數據並刪除會話的Cookie。這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問
          flush()
#五、獲取session值
        get(key, default=None)
        fav_color = request.session.get('fav_color', 'red')  
#六、刪除
        pop(key)
        fav_color = request.session.pop('fav_color')  
#七、查看全部key
        keys()
#八、查詢全部鍵值對
        items()  
#九、setdefault()  
#十、用戶session的隨機字符串
        request.session.session_key

# 十一、將全部Session失效日期小於當前日期的數據刪除
        request.session.clear_expired()

#十二、 檢查用戶session的隨機字符串在數據庫中是否存在
        request.session.exists("session_key")

# 1三、刪除當前用戶的全部Session數據
        request.session.delete("session_key")

#1四、設置超時時間
        request.session.set_expiry(value)
            * 若是value是個整數,session會在xx秒後失效。
            * 若是value是個datatime或timedelta,session就會在這個時間後失效。
            * 若是value是0,用戶關閉瀏覽器session就會失效。
            * 若是value是None,session會依賴全局session失效策略。            

(3)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,默認修改以後才保存(默認)

(4)登陸案例

#login.html:登陸頁面,提供登陸表單;
#index1.html:主頁,顯示當前用戶名稱,若是沒有登陸,顯示您還沒登陸;
#index2.html:主頁,顯示當前用戶名稱,若是沒有登陸,顯示您還沒登陸;

思考:若是第二我的再次再同一個瀏覽器上登陸,django-session表會怎樣?

7、Django組件——forms

一、forms組件的校驗字段功能

如:用戶註冊

模型models.py:

class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    email = models.EmailField()
    tel = models.CharField(max_length=32)

模板 register.html:

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

</head>
<body>

<form action="" method="post">
    {% csrf_token %}
    <div>
        <label for="user">用戶名</label>
        <p><input type="text" name="name" id="name"></p>
    </div>
    <div>
        <label for="pwd">密碼</label>
        <p><input type="password" name="pwd" id="pwd"></p>
    </div>
    <div>
        <label for="r_pwd">確認密碼</label>
        <p><input type="password" name="r_pwd" id="r_pwd"></p>
    </div>
    <div>
        <label for="email">郵箱</label>
        <p><input type="text" name="email" id="email"></p>
    </div>
    <input type="submit">
</form>

</body>
</html>
register.html

視圖views.py:

from django import forms
from django.forms import widgets

from django.shortcuts import render, HttpResponse
from app01 import models

wid_01 = widgets.TextInput(attrs={"class": "form-control"})
wid_02 = widgets.PasswordInput(attrs={"class": "form-control"})


class UserForm(forms.Form):
    name = forms.CharField(max_length=32, widget=wid_01)
    pwd = forms.CharField(max_length=32, widget=wid_02)
    r_pwd = forms.CharField(max_length=32, widget=wid_02)
    email = forms.EmailField(widget=wid_01)
    tel = forms.CharField(max_length=32, widget=wid_01)


def register(request):
    if request.method == "POST":
        form = UserForm(request.POST)
        if form.is_valid():  # 字段合法
            print(1, form.cleaned_data)  # 全部乾淨的字段以及對應的值
            return HttpResponse("OK")
        else:
            print(2, form.cleaned_data)  # 合法字段
            print(3, form.errors, type(form.errors))  # ErrorDict : {"校驗錯誤的字段":["錯誤信息",]}
            print(4, form.errors.get("email"))  # 當輸入錯誤格式的email時就會捕獲到這個錯誤信息:ErrorList ["錯誤信息",]
            return HttpResponse(str(form.errors))

    form = UserForm()
    return render(request, "register.html", locals())
views.py

二、forms組件的渲染標籤功能

views.py:

方式1:

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>註冊頁面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-lg-offset-3">

            <form action="" method="post">
                {% csrf_token %}
                <div>
                    <label for="">用戶名</label>
                    {{ form.name }}
                </div>
                <div>
                    <label for="">密碼</label>
                    {{ form.pwd }}
                </div>
                <div>
                    <label for="">確認密碼</label>
                    {{ form.r_pwd }}
                </div>
                <div>
                    <label for=""> 郵箱</label>
                    {{ form.email }}
                </div>
                <div>
                    <label for=""> 電話</label>
                    {{ form.tel }}
                </div>

                <input type="submit" class="btn btn-default pull-right">
            </form>
        </div>
    </div>
</div>
</body>
</html>
register.html

效果:

方式2:

<form action="" method="post">
    {% csrf_token %}

    {% for field in form %}
        <div>
            <label for="">{{ field.label }}</label>
            {{ field }}
        </div>
    {% endfor %}
    <input type="submit" class="btn btn-default pull-right">
</form>

效果:

若是想要顯示中文,就須要到UserForm類中設置label屬性:

方式3:

<form action="" method="post">
    {% csrf_token %}

    {{ form.as_p }}
    <input type="submit" class="btn btn-default pull-right">

</form>

三、在頁面顯示錯誤提示信息

from django import forms
from django.forms import widgets

from django.shortcuts import render, HttpResponse
from app01 import models

wid_01 = widgets.TextInput(attrs={"class": "form-control"})
wid_02 = widgets.PasswordInput(attrs={"class": "form-control"})


class UserForm(forms.Form):
    name = forms.CharField(max_length=32, widget=wid_01, label="用戶名")
    pwd = forms.CharField(max_length=32, widget=wid_02)
    r_pwd = forms.CharField(max_length=32, widget=wid_02)
    email = forms.EmailField(widget=wid_01)
    tel = forms.CharField(max_length=32, widget=wid_01)


def register(request):
    if request.method == "POST":
        form = UserForm(request.POST)
        if form.is_valid():  # 字段合法
            print(1, form.cleaned_data)  # 全部乾淨的字段以及對應的值
            return HttpResponse("OK")
        else:
            print(2, form.cleaned_data)  # 合法字段
            print(3, form.errors, type(form.errors))  # ErrorDict : {"校驗錯誤的字段":["錯誤信息",]}
            print(4, form.errors.get("email"))  # 當輸入錯誤格式的email時就會捕獲到這個錯誤信息:ErrorList ["錯誤信息",]
            return render(request, "register.html", locals())

    form = UserForm()  # 實例化
    return render(request, "register.html", locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>註冊頁面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-lg-offset-3">

            <form action="" method="post">
                {% csrf_token %}

                {% for field in form %}
                    <div>
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="pull-right" style="color: red">{{ field.errors.0 }}</span>
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-default">

            </form>

        </div>
    </div>
</div>
</body>
</html>
register.html

四、局部鉤子與全局鉤子

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

from django.shortcuts import render, HttpResponse
from app01 import models

wid_01 = widgets.TextInput(attrs={"class": "form-control"})
wid_02 = widgets.PasswordInput(attrs={"class": "form-control"})


class UserForm(forms.Form):
    name = forms.CharField(max_length=32, widget=wid_01, label="用戶名")
    pwd = forms.CharField(max_length=32, widget=wid_02, label="密碼")
    r_pwd = forms.CharField(max_length=32, widget=wid_02, label="確認密碼")
    email = forms.EmailField(widget=wid_01, label="郵箱")
    tel = forms.CharField(max_length=32, widget=wid_01, label="電話")

    # 局部鉤子
    def clean_name(self):  # 函數名規則:clean_字段名
        """規定用戶名不能爲純數字"""
        val = self.cleaned_data.get("name")
        if not val.isdigit():
            return val
        else:
            raise ValidationError("用戶名不能是純數字!")

    # 全局鉤子
    def clean(self):
        """校驗兩次密碼必須一致"""
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")

        if pwd == r_pwd:
            return self.cleaned_data
        else:
            raise ValidationError('兩次密碼不一致!')


def register(request):
    if request.method == "POST":
        form = UserForm(request.POST)
        if form.is_valid():  # 字段合法
            print(1, form.cleaned_data)  # 全部乾淨的字段以及對應的值
            return HttpResponse("OK")
        else:
            clean_error = form.errors.get("__all__")
            print(clean_error)
            return render(request, "register.html", locals())

    form = UserForm()  # 實例化
    return render(request, "register.html", locals())
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>註冊頁面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-lg-offset-3">

            <form action="" method="post" novalidate>
                {% csrf_token %}

                {% for field in form %}
                    <div>
                        <label for="">{{ field.label }}</label>
                        {{ field }}
                        <span class="pull-right" style="color: red">
                            {% if field.label == '確認密碼' %}
                                <span>{{ clean_error.0 }}</span>
                            {% endif %}
                            {{ field.errors.0 }}
                    </span>
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-default">
            </form>

        </div>
    </div>
</div>
</body>
</html>
register.html

 

8、Django組件——用戶認證

一、auth模塊

from django.contrib import auth

 (1)authenticate()
提供了用戶認證,即驗證用戶名以及密碼是否正確,通常須要username和password兩個關鍵字參數。
若是認證信息有效,會返回一個 User 對象。當咱們試圖登錄一個從數據庫中直接取出來不通過authenticate()的User對象就會報錯。

user=authenticate(username='amy',password='123456')

(2)login(HttpRequest, user)  
該函數接受一個HttpRequest對象,以及一個認證了的User對象,
此函數使用django的session框架給某個已認證的用戶附加上session id等信息。

from django.contrib.auth import authenticate, login

def login(request):
  username = request.POST.get('username')
  password = request.POST.get('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.
    ...

(3)logout(request) 註銷用戶

當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。

from django.contrib.auth import logout

def logout_view(request):
  logout(request)
  # Redirect to a success page.

二、user對象

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

(1) is_authenticated()
 用於檢查用戶是否已經經過了認證。返回值爲 True或False 。
經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。

在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是True則能夠向前臺展現request.user.name
要求:
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_requierd()

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
  ...

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

(2)create_user() 建立用戶

from django.contrib.auth.models import User
user = User.objects.create_user(username='xxx',password='xxx',email='xxx'

(3)check_password(password) 校驗密碼
如,當用戶須要修改密碼的時候,首先要讓他輸入原來的密碼 ,若是給定的字符串經過了密碼檢查,返回 True

(4)set_password()  修改密碼

user = User.objects.get(username='amy')
user.set_password(password='111')
user.save

(5)示例

註冊:

from django.contrib.auth.models import User

from django.shortcuts import render, HttpResponse, redirect
from app01 import models


def sign_up(request):
    state = None
    if request.method == 'POST':
        username = request.POST.get('username', '')
        password = request.POST.get('password', '')
        repeat_password = request.POST.get('repeat_password', '')
        email = request.POST.get('email', '')

        if not password == repeat_password:
            state = "兩次密碼不一致"
        else:
            # 檢查用戶名是否已經存在
            if User.objects.filter(username=username):
                state = '用戶名已經存在!'
            else:
                new_user = User.objects.create_user(username=username, password=password, email=email)
                new_user.save()

                return redirect('/index/')

    content = {'state': state, 'user': None}
    return render(request, 'sign_up.html', {"content": content})

sign_up.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {% csrf_token %}
    用戶名:<input type="text" name="username"/>
    密碼:<input type="password" name="password"/>
    確認密碼:<input type="password" name="repeat_password"/>
    郵箱:<input type="email" name="email"/>
    <input type="submit" value="註冊"/>
    {% if content.state %}
        <span style="color: red">{{ content.state }}</span>
    {% endif %}
</form>
</body>
</html>

修改密碼:

from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout

from django.shortcuts import render, HttpResponse, redirect
from app01 import models


@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 = '不能爲空'
            elif new_password != repeat_password:
                state = '兩次密碼不一致'
            else:
                user.set_password(new_password)
                user.save()
                return redirect("/accounts/login/")
        else:
            state = '密碼錯誤'
    content = {
        'user': user,
        'state': state,
    }
    return render(request, 'set_password.html', {"content": content})


def login_view(request):
    """登陸"""
    next_url = request.GET.get("next")

    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")

        user = authenticate(username=username, password=password)
        if user is not None:
            login(request, user)
            if next_url:
                return redirect(next_url)
            else:
                return HttpResponse("ok")
    return render(request, "login.html")
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {% csrf_token %}
    <p>用戶名:<input type="text" name="username"/></p>
    <p>密碼:<input type="password" name="password"/></p>
    <p><input type="submit" value="登陸"/></p>
</form>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    {% csrf_token %}
    <p>舊密碼:<input type="password" name="old_password"/></p>
    <p>新密碼:<input type="password" name="new_password"/></p>
    <p>確認密碼:<input type="password" name="repeat_password"/></p>
    <p><input type="submit" value="提交"/></p>
</form>
</body>
</html>
set_password.html

 

9、Django組件——中間件

一、中間件概念

中間件,是介於request與response處理之間的一道處理過程,相對比較輕量級,在全局上改變django的輸入與輸出,便可以對request和response做批量操做。

當請求進來時會穿過一層層的中間件到達視圖,視圖做相應處理後返回響應數據,響應數據再經過層層中間件返回給用戶:

二、自定義中間件

(1)中間件中有五種方法

process_request()
process_view()
process_response()
process_exception()

(2)自定義中間件,即編寫一個類,繼承MiddlewareMixin

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):
    def process_request(self, request):
        print("Md1請求")

    def process_response(self, request, response):
        print("Md1返回")
        return response


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2請求")
        # return HttpResponse("Md2中斷")  # 加上這句,請求不會到達視圖函數裏面

    def process_response(self, request, response):
        print("Md2返回")
        return response
my_middlewares.py

settings.py:

from django.shortcuts import render, HttpResponse


def index(request):
    print("in view...")
    return HttpResponse("OK")
views.py

結果:

 

修改middleware以下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):
    def process_request(self, request):
        print("Md1請求")
        # return HttpResponse("Md1中斷")

    def process_response(self, request, response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md1 view")


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2請求")
        # return HttpResponse("Md2中斷")

    def process_response(self, request, response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md2 view")
my_middlewares.py

結果:

結果:

流程:

在process_view中能夠調用視圖函數:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):
    def process_request(self, request):
        print("Md1請求")
        # return HttpResponse("Md1中斷")

    def process_response(self, request, response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        # return HttpResponse("hello")
        response = callback(request, *callback_args, **callback_kwargs)
        return response


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2請求")
        # return HttpResponse("Md2中斷")

    def process_response(self, request, response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md2 view")
my_middlewares.py

結果:

當process_view有返回值時,會越過其餘的process_view以及視圖函數,可是全部的process_response都還會執行。

修改middleware:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):

    def process_request(self, request):
        print("Md1請求")

    def process_response(self, request, response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("md1 process_view...")

    def process_exception(self, request, exception):
        print("md1 process_exception...")
        return HttpResponse("error")


class Md2(MiddlewareMixin):
    def process_request(self, request):
        print("Md2請求")

    def process_response(self, request, response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("md2 process_view...")

    def process_exception(self, request, exception):
        print("md2 process_exception...")
        return HttpResponse("error")
my_middlewares.py

結果:

當視圖函數出現異常時,process_exception就會被觸發:

三、自帶的中間件:

'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',

 

10、Django組件——分頁器

一、數據量不大的狀況

import random
from django.shortcuts import render, HttpResponse
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from app01.models import *


def index(request):
    # 批量導入數據:
    # book_list = []
    # for i in range(100):
    #     book_list.append(Book(title="book" + str(i), price=30.00 + i * 2, publishDate="2012-10-23",
    #                           publish_id=random.randint(1, 3)))
    # Book.objects.bulk_create(book_list)  # 批量建立記錄

    book_list = Book.objects.all()

    paginator = Paginator(book_list, 10)  # 每頁10條

    print("count:", paginator.count)  # 數據總數
    print("num_pages", paginator.num_pages)  # 總頁數
    print("page_range", paginator.page_range)  # 頁碼的列表

    page1 = paginator.page(1)  # 第1頁的page對象
    for i in page1:  # 遍歷第1頁的全部數據對象
        print(i)

    print(page1.object_list)  # 第1頁的全部數據

    page2 = paginator.page(2)

    print(page2.has_next())  # 是否有下一頁
    print(page2.next_page_number())  # 下一頁的頁碼
    print(page2.has_previous())  # 是否有上一頁
    print(page2.previous_page_number())  # 上一頁的頁碼

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

    try:
        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", {"book_list": book_list, "paginator": paginator, "currentPage": current_page})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</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="/app02/index/?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="/app02/index/?page={{ num }}">{{ num }}</a></li>
            {% else %}
                <li class="item"><a href="/app02/index/?page={{ num }}">{{ num }}</a></li>

            {% endif %}
        {% endfor %}

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

    </ul>
</div>

</body>
</html>
index.html

二、數據量大的狀況

def index(request):
    book_list = Book.objects.all()
    paginator = Paginator(book_list, 5)
    page = request.GET.get('page', 1)
    currentPage = int(page)

    #  若是頁數特別多時,限制顯示的index
    if paginator.num_pages > 11:  # 只顯示10頁的index
        if currentPage - 5 < 1:  # 當前頁碼小於6
            pageRange = range(1, 11)
        elif currentPage + 5 > paginator.num_pages:  # 當前頁碼+5大於了總頁碼
            pageRange = range(currentPage - 5, paginator.num_pages + 1)
        else:
            pageRange = range(currentPage - 5, currentPage + 5)
    else:
        pageRange = paginator.page_range
    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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</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="/app02/index/?page={{ book_list.previous_page_number }}">上一頁</a></li>
        {% else %}
            <li class="previous disabled"><a href="#">上一頁</a></li>
        {% endif %}

        {% for num in pageRange %}

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

            {% endif %}
        {% endfor %}

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

    </ul>
</div>

</body>
</html>
index.html

能夠將分頁的代碼寫到一個文件中,要使用分頁時直接調用既能夠。

11、Django與Ajax

一、Ajax介紹

AJAX(Asynchronous Javascript And XML)翻譯成中文就是「異步Javascript和XML」。即便用Javascript語言與服務器進行異步交互,傳輸的數據爲XML(固然,傳輸的數據不僅是XML,如今更多使用json數據)。

  • 同步交互:客戶端發出一個請求後,須要等待服務器響應結束後,才能發出第二個請求;
  • 異步交互:客戶端發出一個請求後,無需等待服務器響應結束,就能夠發出第二個請求。

Ajax特色:

  • 使用Javascript技術向服務器發送異步請求;
  • 瀏覽器頁面局部刷新。

二、基於jQuery的Ajax實現

<button class="send_Ajax">send_Ajax</button>
<script>
       $(".send_Ajax").click(function(){
           $.ajax({
               url:"/handle_Ajax/",  # URL
               type:"POST",  # 請求方式
               data:{username:"xxx",password:123},  # 請求數據
               # 回調函數:成功執行,發生錯誤等狀況發生時作些什麼操做
               success:function(data){
                   console.log(data)
               },
               
               error: function (jqXHR, textStatus, err) {
                        console.log(arguments);
                    },
               complete: function (jqXHR, textStatus) {
                        console.log(textStatus);
                },
               statusCode: {
                    '403': function (jqXHR, textStatus, err) {
                          console.log(arguments);
                     },
                    '400': function (jqXHR, textStatus, err) {
                        console.log(arguments);
                    }
                }
           })
       })
</script>

三、應用場景

(1) 用戶名是否已被註冊
在註冊表單中,當用戶填寫了用戶名後,把光標移開後,會自動向服務器發送異步請求。

服務器返回true或false,返回true表示這個用戶名已經被註冊過,返回false表示沒有註冊過。

客戶端獲得服務器返回的結果後,肯定是否在用戶名文本框後顯示「用戶名已被註冊」的錯誤信息!
(2) 基於Ajax進行登陸驗證
用戶在表單輸入用戶名與密碼,經過Ajax提交給服務器,服務器驗證後返回響應信息,客戶端經過響應信息肯定是否登陸成功,成功,則跳轉到首頁,不然,在頁面上顯示相應的錯誤信息。

四、請求頭ContentType

ContentType指的是請求體的編碼類型,常見的類型共有3種:

  • application/x-www-form-urlencoded

最多見的 POST 提交數據的方式,瀏覽器的原生表單,若是不設置 enctype 屬性,那麼最終就會以 application/x-www-form-urlencoded 方式提交數據。

請求體:

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
...

user=amy&age=22
  • multipart/form-data

使用表單上傳文件時,必須讓表單的 enctype 等於 multipart/form-data

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="user"

yuan
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

首先生成了一個 boundary 用於分割不一樣的字段,爲了不與正文內容重複,boundary 很長很複雜。而後 Content-Type 裏指明瞭數據是以 multipart/form-data 來編碼,本次請求的 boundary 是什麼內容。消息主體裏按照字段個數又分爲多個結構相似的部分,每部分都是以 --boundary 開始,緊接着是內容描述信息,而後是回車,最後是字段具體內容(文本或二進制)。若是傳輸的是文件,還要包含文件名和文件類型信息。消息主體最後以 --boundary-- 標示結束。

  • application/json

表示消息主體是序列化後的 JSON 字符串。

五、文件上傳

(1)基於form表單的文件上傳

def avatar(request):
    # print("body:", request.body)  # 原始的請求體數據
    print("get:", request.GET)  # GET請求數據
    print("post:", request.POST)  # POST請求數據
    print("files:", request.FILES)  # 上傳的文件數據
    return render(request, "avatar.html")
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    用戶名 <input type="text" name="user">
    頭像 <input type="file" name="avatar">
    <input type="submit">
</form>
</body>
</html>
avatar.html

結果:

get: <QueryDict: {}>
post: <QueryDict: {'csrfmiddlewaretoken': ['vVPsFNXCJpl3P0aUIaS1ABYkczxtZmrIa1RJEsoqGIQ9zrJ6CITbm7LkulaLwp71'], 'user': ['qw']}>
files: <MultiValueDict: {'avatar': [<InMemoryUploadedFile: 照片.jpg (image/jpeg)>]}>

(2)基於Ajax的文件上傳

def avatar(request):
    if request.is_ajax():
        print(request.GET)  # GET請求數據
        print(request.POST)  # POST請求數據
        print(request.FILES)  # 上傳的文件數據
        return HttpResponse("ok")
    return render(request, "avatar.html")
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form>
    {% csrf_token %}
    用戶名 <input type="text" id="user">
    頭像 <input type="file" id="avatar">
    <input type="button" id="ajax-submit" value="ajax-submit">
</form>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script>
<script>

    $("#ajax-submit").click(function () {
        var formdata = new FormData();
        formdata.append("user", $("#user").val());
        formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
        formdata.append("avatar_img", $("#avatar")[0].files[0]);
        $.ajax({

            url: "",  //默認當前URL
            type: "post",
            data: formdata,
            processData: false,    // 不處理數據
            contentType: false,    // 不設置內容類型

            success: function (data) {
                console.log(data);
            }
        })
    })
</script>
</body>
</html>
avatar.html
相關文章
相關標籤/搜索