django基礎(轉載)

web應用

Web應用程序是一種能夠經過Web訪問的應用程序,程序的最大好處是用戶很容易訪問應用程序,用戶只須要有瀏覽器便可,不須要再安裝其餘軟件。應用程序有兩種模式C/S、B/S。C/S是客戶端/服務器端程序,也就是說這類程序通常獨立運行。而B/S就是瀏覽器端/服務器端應用程序,這類應用程序通常藉助谷歌,火狐等瀏覽器來運行。WEB應用程序通常是B/S模式。Web應用程序首先是「應用程序」,和用標準的程序語言,如java,python等編寫出來的程序沒有什麼本質上的不一樣。在網絡編程的意義下,瀏覽器是一個socket客戶端,服務器是一個socket服務端。php

複製代碼
import socket

def handle_request(client):

    request_data = client.recv(1024)
    print("request_data: ",request_data)

    client.send("HTTP/1.1 200 OK\r\nstatus: 200\r\nContent-Type:text/html\r\n\r\n".encode("utf8"))
    client.send("<h1>Hello, luffycity!</h1><img src=''>".encode("utf8"))

def main():

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost',8812))
    sock.listen(5)

    while True:
        print("the server is waiting for client-connection....")
        connection, address = sock.accept()
        handle_request(connection)
        connection.close()

if __name__ == '__main__':

    main()
複製代碼

 

http協議簡介

HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於萬維網(WWW:World Wide Web )服務器與本地瀏覽器之間傳輸超文本的傳送協議。css

HTTP是一個屬於應用層的面向對象的協議,因爲其簡捷、快速的方式,適用於分佈式超媒體信息系統。它於1990年提出,通過幾年的使用與發展,獲得不斷地完善和擴展。HTTP協議工做於客戶端-服務端架構爲上。瀏覽器做爲HTTP客戶端經過URL向HTTP服務端即WEB服務器發送全部請求。Web服務器根據接收到的請求後,向客戶端發送響應信息。html

http協議特性

 

(1) 基於TCP/IP

http協議是基於TCP/IP協議之上的應用層協議。java

(2) 基於請求-響應模式

HTTP協議規定,請求從客戶端發出,最後服務器端響應該請求並 返回。換句話說,確定是先從客戶端開始創建通訊的,服務器端在沒有 接收到請求以前不會發送響應python

(3) 無狀態保存

HTTP是一種不保存狀態,即無狀態(stateless)協議。HTTP協議 自身不對請求和響應之間的通訊狀態進行保存。也就是說在HTTP這個 級別,協議對於發送過的請求或響應都不作持久化處理。mysql

使用HTTP協議,每當有新的請求發送時,就會有對應的新響應產 生。協議自己並不保留以前一切的請求或響應報文的信息。這是爲了更快地處理大量事務,確保協議的可伸縮性,而特地把HTTP協議設計成 如此簡單的。但是,隨着Web的不斷髮展,因無狀態而致使業務處理變得棘手 的狀況增多了。好比,用戶登陸到一家購物網站,即便他跳轉到該站的 其餘頁面後,也須要能繼續保持登陸狀態。針對這個實例,網站爲了能 夠掌握是誰送出的請求,須要保存用戶的狀態。HTTP/1.1雖然是無狀態協議,但爲了實現指望的保持狀態功能, 因而引入了Cookie技術。有了Cookie再用HTTP協議通訊,就能夠管 理狀態了。有關Cookie的詳細內容稍後講解。git

無鏈接

無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。程序員

http請求協議與響應協議

http協議包含由瀏覽器發送數據到服務器須要遵循的請求協議與服務器發送數據到瀏覽器須要遵循的請求協議。用於HTTP協議交互的信被爲HTTP報文。請求端(客戶端)的HTTP報文 作請求報文,響應端(服務器端)的 作響應報文。HTTP報文自己是由多行數據構成的字 文本。 web

請求協議

請求格式

請求方式: get與post請求

  • GET提交的數據會放在URL以後,以?分割URL和傳輸數據,參數之間以&相連,如EditBook?name=test1&id=123456. POST方法是把提交的數據放在HTTP包的請求體中.
  • GET提交的數據大小有限制(由於瀏覽器對URL的長度有限制),而POST方法提交的數據沒有限制.
  • GET與POST請求在服務端獲取請求數據方式不一樣。

響應協議

響應格式

響應狀態碼

狀態碼的職 是當客戶端向服務器端發送請求時, 返回的請求 結果。藉助狀態碼,用戶能夠知道服務器端是正常 理了請求,仍是出 現了 。狀態碼如200 OK,以3位數字和緣由 成。數字中的 一位指定了響應 別,後兩位無分 。響應 別有以5種。ajax

 

演示示例:

複製代碼
import socket


sock=socket.socket()
sock.bind(("127.0.0.1",8808))
sock.listen(5)

while 1:
    print("server waiting.....")
    conn,addr=sock.accept()
    data=conn.recv(1024)
    print("data",data)

    # 讀取html文件
    with open("login.html","rb") as f:
        data=f.read()

    conn.send((b"HTTP/1.1 200 OK\r\n\r\n%s"%data))
    conn.close()
複製代碼

login.html

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>



<form action="" method="post">
    用戶名 <input type="text" name="user">
    密碼 <input type="password" name="pwd">
    <input type="submit">
</form>

</body>
</html>
複製代碼

 

web框架

Web框架(Web framework)是一種開發框架,用來支持動態網站、網絡應用和網絡服務的開發。這大多數的web框架提供了一套開發和部署網站的方式,也爲web行爲提供了一套通用的方法。web框架已經實現了不少功能,開發人員使用框架提供的方法而且完成本身的業務邏輯,就能快速開發web應用了。瀏覽器和服務器的是基於HTTP協議進行通訊的。也能夠說web框架就是在以上十幾行代碼基礎張擴展出來的,有不少簡單方便使用的方法,大大提升了開發的效率。

wsgiref模塊

最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。

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

正確的作法是底層代碼由專門的服務器軟件實現,咱們用Python專一於生成HTML文檔。由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口協議來實現這樣的服務器軟件,讓咱們專心用Python編寫Web業務。這個接口就是WSGI:Web Server Gateway Interface。而wsgiref模塊就是python基於wsgi協議開發的服務模塊。

複製代碼
from wsgiref.simple_server import make_server


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


httpd = make_server('', 8080, application)

print('Serving HTTP on port 8000...')
# 開始監聽HTTP請求:
httpd.serve_forever()
複製代碼

DIY一個web框架

 

models.py

複製代碼
import pymysql
#鏈接數據庫
conn = pymysql.connect(host='127.0.0.1',port= 3306,user = 'root',passwd='',db='web') #db:庫名
#建立遊標
cur = conn.cursor()

sql='''
create table userinfo(
        id INT PRIMARY KEY ,
        name VARCHAR(32) ,
        password VARCHAR(32)
)

'''

cur.execute(sql)

#提交
conn.commit()
#關閉指針對象
cur.close()
#關閉鏈接對象
conn.close()
複製代碼

啓動文件manage.py

複製代碼
from wsgiref.simple_server import make_server

from app01.views import *
import urls


def routers():

    URLpattern=urls.URLpattern
    return URLpattern


def applications(environ,start_response):

    path=environ.get("PATH_INFO")
    start_response('200 OK', [('Content-Type', 'text/html'),('Charset', 'utf8')])
    urlpattern=routers()
    func=None
    for item in urlpattern:
        if path==item[0]:
            func=item[1]
            break
    if func:
        return [func(environ)]
    else:
        return [b"<h1>404!<h1>"]

if __name__ == '__main__':

    server=make_server("",8889,applications)
    print("server is working...")
    server.serve_forever()
複製代碼

urls.py

from app01.views import *


URLpattern = (
    ("/login/", login),
)

views

複製代碼
import pymysql

from urllib.parse import parse_qs


def login(request):

    if request.get("REQUEST_METHOD")=="POST":

        try:
            request_body_size = int(request.get('CONTENT_LENGTH', 0))
        except (ValueError):
            request_body_size = 0

        request_body = request['wsgi.input'].read(request_body_size)
        data = parse_qs(request_body)


        user=data.get(b"user")[0].decode("utf8")
        pwd=data.get(b"pwd")[0].decode("utf8")

        #鏈接數據庫
        conn = pymysql.connect(host='127.0.0.1',port= 3306,user = 'root',passwd='',db='web') # db:庫名
        #建立遊標
        cur = conn.cursor()
        SQL="select * from userinfo WHERE NAME ='%s' AND PASSWORD ='%s'"%(user,pwd)
        cur.execute(SQL)

        if cur.fetchone():

            f=open("templates/backend.html","rb")
            data=f.read()
            data=data.decode("utf8")
            return data.encode("utf8")

        else:
             print("OK456")
             return b"user or pwd is wrong"

    else:
        f = open("templates/login.html", "rb")
        data = f.read()
        f.close()
        return data
複製代碼

login.html

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h4>登陸頁面</h4>
<form action="" method="post">
     用戶名 <input type="text" name="user">
     密碼 <input type="text" name="pwd">
    <input type="submit">
</form>

</body>
</html>
複製代碼

backend.html

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h4>welcome to oldboy!</h4>
</body>
</html>
複製代碼

 yuan這個package就是一個web框架,下載這個web框架就能夠快速實現一些簡單的web功能,好比查看時間。

 

4.Django簡介

1)MVC和MTV模型

2)Django的下載和安裝

3)基於Django實現一個簡單的實例

MVC與MTV模型

MVC

Web服務器開發領域裏著名的MVC模式,所謂MVC就是把Web應用分爲模型(M),控制器(C)和視圖(V)三層,他們之間以一種插件式的、鬆耦合的方式鏈接在一塊兒,模型負責業務對象與數據庫的映射(ORM),視圖負責與用戶的交互(頁面),控制器接受用戶的輸入調用模型和視圖完成用戶的請求,其示意圖以下所示:

MTV

Django的MTV模式本質上和MVC是同樣的,也是爲了各組件間保持鬆耦合關係,只是定義上有些許不一樣,Django的MTV分別是值:

  • M 表明模型(Model): 負責業務對象和數據庫的關係映射(ORM)。
  • T 表明模板 (Template):負責如何把頁面展現給用戶(html)。
  • V 表明視圖(View):   負責業務邏輯,並在適當時候調用Model和Template。

除了以上三層以外,還須要一個URL分發器,它的做用是將一個個URL的頁面請求分發給不一樣的View處理,View再調用相應的Model和Template,MTV的響應模式以下所示:

通常是用戶經過瀏覽器向咱們的服務器發起一個請求(request),這個請求回去訪問視圖函數,(若是不涉及到數據調用,那麼這個時候視圖函數返回一個模板也就是一個網頁給用戶),視圖函數調用模型,模型去數據庫查找數據,而後逐級返回,視圖函數把返回的數據填充到模板中空格中,最後返回網頁給用戶。

Django的下載與基本命令

一、下載Django:

1
pip3 install django

二、建立一個django project

1
django - admin.py startproject mysite

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

        

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

三、在mysite目錄下建立應用

1
python manage.py startapp blog

        

四、啓動django項目

1
python manage.py runserver  8080

       這樣咱們的django就啓動起來了!當咱們訪問:http://127.0.0.1:8080/時就能夠看到:

       

基於Django實現的一個簡單示例

 url控制器

複製代碼
from django.contrib import admin
from django.urls import path


from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/',views.index),
]
複製代碼

視圖

複製代碼
from django.shortcuts import render

# Create your views here.



def index(request):

    import datetime
    now=datetime.datetime.now()
    ctime=now.strftime("%Y-%m-%d %X")

    return render(request,"index.html",{"ctime":ctime})
複製代碼

模板

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h4>當前時間:{{ ctime }}</h4>

</body>
</html>
複製代碼

執行效果以下:

 

 

 

 

 

Django-2 路由層(URLconf)

URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL與要爲該URL調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對於客戶端發來的某個URL調用哪一段邏輯代碼對應執行。

 

簡單的路由配置

複製代碼
from django.urls import path,re_path

from app01 import views

urlpatterns = [
    re_path(r'^articles/2003/$', views.special_case_2003),
    re_path(r'^articles/([0-9]{4})/$', views.year_archive),
    re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
複製代碼

注意:

  • 若要從URL 中捕獲一個值,只須要在它周圍放置一對圓括號。
  • 不須要添加一個前導的反斜槓,由於每一個URL 都有。例如,應該是^articles 而不是 ^/articles
  • 每一個正則表達式前面的'r' 是可選的可是建議加上。它告訴Python 這個字符串是「原始的」 —— 字符串中任何字符都不該該轉義

示例:

複製代碼
    
    '''
 一些請求的例子:

/articles/2005/03/ 請求將匹配列表中的第三個模式。Django 將調用函數views.month_archive(request, '2005', '03')。
/articles/2005/3/ 不匹配任何URL 模式,由於列表中的第三個模式要求月份應該是兩個數字。
/articles/2003/ 將匹配列表中的第一個模式不是第二個,由於模式按順序匹配,第一個會首先測試是否匹配。請像這樣自由插入一些特殊的狀況來探測匹配的次序。
/articles/2003 不匹配任何一個模式,由於每一個模式要求URL 以一個反斜線結尾。
/articles/2003/03/03/ 將匹配最後一個模式。Django 將調用函數views.article_detail(request, '2003', '03', '03')。
   
    '''
複製代碼

有名分組

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

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

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

複製代碼
from django.urls import path,re_path

from app01 import views

urlpatterns = [
    re_path(r'^articles/2003/$', views.special_case_2003),
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
複製代碼

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

複製代碼
    '''
    /articles/2005/03/ 請求將調用views.month_archive(request, year='2005', month='03')函數,而不是views.month_archive(request, '2005', '03')。
    /articles/2003/03/03/ 請求將調用函數views.article_detail(request, year='2003', month='03', day='03')。

    '''
複製代碼

在實際應用中,這意味你的URLconf 會更加明晰且不容易產生參數順序問題的錯誤 —— 你能夠在你的視圖函數定義中從新安排參數的順序。固然,這些好處是以簡潔爲代價;

分發

複製代碼
'''
At any point, your urlpatterns can 「include」 other URLconf modules. This
essentially 「roots」 a set of URLs below other ones.

'''

from django.urls import path,re_path,include
from app01 import views

urlpatterns = [
   re_path(r'^admin/', admin.site.urls),
   re_path(r'^blog/', include('blog.urls')),
]
複製代碼

反向解析

在使用Django 項目時,一個常見的需求是得到URL 的最終形式,以用於嵌入到生成的內容中(視圖中和顯示給用戶的URL等)或者用於處理服務器端的導航(重定向等)。人們強烈但願不要硬編碼這些URL(費力、不可擴展且容易產生錯誤)或者設計一種與URLconf 絕不相關的專門的URL 生成機制,由於這樣容易致使必定程度上產生過時的URL。

在須要URL 的地方,對於不一樣層級,Django 提供不一樣的工具用於URL 反查:

  • 在模板中:使用url 模板標籤。
  • 在Python 代碼中:使用from django.urls import reverse()函數
     

urls.py:

複製代碼
from django.conf.urls import url

from . import views

urlpatterns = [
    #...
    re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
    #...
]
複製代碼

在模板中:

複製代碼
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>

<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>
複製代碼

在python中:

複製代碼
from django.urls import reverse
from django.http import HttpResponseRedirect

def redirect_to_year(request):
    # ...
    year = 2006
    # ...
    return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))   # 同redirect("/path/")
複製代碼

當命名你的URL 模式時,請確保使用的名稱不會與其它應用中名稱衝突。若是你的URL 模式叫作comment,而另一個應用中也有一個一樣的名稱,當你在模板中使用這個名稱的時候不能保證將插入哪一個URL。在URL 名稱中加上一個前綴,好比應用的名稱,將減小衝突的可能。咱們建議使用myapp-comment 而不是comment

名稱空間

命名空間(英語:Namespace)是表示標識符的可見範圍。一個標識符可在多個命名空間中定義,它在不一樣命名空間中的含義是互不相干的。這樣,在一個新的命名空間中可定義任何標識符,它們不會與任何已有的標識符發生衝突,由於已有的定義都處於其它命名空間中。

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

project的urls.py:

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

app01.urls:

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

app02.urls:

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

app01.views 

from django.core.urlresolvers import reverse

def index(request):

    return  HttpResponse(reverse("app01:index"))

app02.views

from django.core.urlresolvers import reverse

def index(request):

    return  HttpResponse(reverse("app02:index"))

django2.0版的path

思考狀況以下:

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

考慮下這樣的兩個問題:

第一個問題,函數 year_archive 中year參數是字符串類型的,所以須要先轉化爲整數類型的變量值,固然year=int(year) 不會有諸如如TypeError或者ValueError的異常。那麼有沒有一種方法,在url中,使得這一轉化步驟能夠由Django自動完成?

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

在Django2.0中,可使用 path 解決以上的兩個問題。

基本示例

這是一個簡單的例子:

複製代碼
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),  
]  
複製代碼

基本規則:

  • 使用尖括號(<>)從url中捕獲值。
  • 捕獲值中能夠包含一個轉化器類型(converter type),好比使用 <int:name> 捕獲一個整數變量。若果沒有轉化器,將匹配任何字符串,固然也包括了 / 字符。
  • 無需添加前導斜槓。

如下是根據 2.0官方文檔 而整理的示例分析表:

path轉化器

文檔原文是Path converters,暫且翻譯爲轉化器。

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  
register_converter(converters.FourDigitYearConverter, 'yyyy')  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<yyyy:year>/', views.year_archive),  
    ...  
]  
複製代碼

 

Django的視圖層

視圖函數

一個視圖函數,簡稱視圖,是一個簡單的Python 函數,它接受Web請求而且返回Web響應。響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片. . . 是任何東西均可以。不管視圖自己包含什麼邏輯,都要返回響應。代碼寫在哪裏也無所謂,只要它在你的Python目錄下面。除此以外沒有更多的要求了——能夠說「沒有什麼神奇的地方」。爲了將代碼放在某處,約定是將視圖放置在項目或應用程序目錄中的名爲views.py的文件中。

下面是一個返回當前日期和時間做爲HTML文檔的視圖:

複製代碼
from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)
複製代碼

讓咱們逐行閱讀上面的代碼:

  • 首先,咱們從 django.shortcuts模塊導入了HttpResponse類,以及Python的datetime庫。

  • 接着,咱們定義了current_datetime函數。它就是視圖函數。每一個視圖函數都使用HttpRequest對象做爲第一個參數,而且一般稱之爲request

    注意,視圖函數的名稱並不重要;不須要用一個統一的命名方式來命名,以便讓Django識別它。咱們將其命名爲current_datetime,是由於這個名稱可以精確地反映出它的功能。

  • 這個視圖會返回一個HttpResponse對象,其中包含生成的響應。每一個視圖函數都負責返回一個HttpResponse對象。

視圖層,熟練掌握兩個對象便可:請求對象(request)和響應對象(HttpResponse)

HttpRequest對象

request屬性   

django將請求報文中的請求行、首部信息、內容主體封裝成 HttpRequest 類中的屬性。 除了特殊說明的以外,其餘均爲只讀的。

複製代碼
/*

1.HttpRequest.GET

  一個相似於字典的對象,包含 HTTP GET 的全部參數。詳情請參考 QueryDict 對象。

2.HttpRequest.POST

  一個相似於字典的對象,若是請求中包含表單數據,則將這些數據封裝成 QueryDict 對象。

  POST 請求能夠帶有空的 POST 字典 —— 若是經過 HTTP 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。

*/
複製代碼

request經常使用方法

複製代碼
/*

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') 裝飾你的視圖以讓響應可以正確地緩存。

*/
複製代碼

HttpResponse對象

響應對象主要有三種形式:

  • HttpResponse()
  • render()
  • redirect()

HttpResponse()括號內直接跟一個具體的字符串做爲響應體,比較直接很簡單,因此這裏主要介紹後面兩種形式。

render()

1
2
3
render(request, template_name[, context])
 
結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 對象。
參數:
     request: 用於生成響應的請求對象。

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

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

render方法就是將一個模板頁面中的模板語法進行渲染,最終渲染成一個html頁面做爲響應體。

redirect()

傳遞要重定向的一個硬編碼的URL

1
2
3
def  my_view(request):
     ...
     return  redirect( '/some/url/' )

也能夠是一個完整的URL:

1
2
3
def  my_view(request):
     ...
     return  redirect( 'http://example.com/' ) 

key:兩次請求

1)301和302的區別。

  301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼後會自動跳轉到一個新的URL地址,這個地址能夠從響應的Location首部中獲取
  (用戶看到的效果就是他輸入的地址A瞬間變成了另外一個地址B)——這是它們的共同點。

  他們的不一樣在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),搜索引擎在抓取新內容的同時也將舊的網址交換爲重定向以後的網址;

  302表示舊地址A的資源還在(仍然能夠訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址。 SEO302好於301

 

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

關於301與302

用redirect能夠解釋APPEND_SLASH的用法!

 

Django的模板層

你可能已經注意到咱們在例子視圖中返回文本的方式有點特別。 也就是說,HTML被直接硬編碼在 Python代碼之中

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

儘管這種技術便於解釋視圖是如何工做的,但直接將HTML硬編碼到你的視圖裏卻並非一個好主意。 讓咱們來看一下爲何:

  • 對頁面設計進行的任何改變都必須對 Python 代碼進行相應的修改。 站點設計的修改每每比底層 Python 代碼的修改要頻繁得多,所以若是能夠在不進行 Python 代碼修改的狀況下變動設計,那將會方便得多。

  • Python 代碼編寫和 HTML 設計是兩項不一樣的工做,大多數專業的網站開發環境都將他們分配給不一樣的人員(甚至不一樣部門)來完成。 設計者和HTML/CSS的編碼人員不該該被要求去編輯Python的代碼來完成他們的工做。

  • 程序員編寫 Python代碼和設計人員製做模板兩項工做同時進行的效率是最高的,遠勝於讓一我的等待另外一我的完成對某個既包含 Python又包含 HTML 的文件的編輯工做。

基於這些緣由,將頁面的設計和Python的代碼分離開會更乾淨簡潔更容易維護。 咱們可使用 Django的 模板系統 (Template System)來實現這種模式,這就是本章要具體討論的問題。

1
python的模板:HTML代碼+模板語法
複製代碼
def current_time(req):
    # ================================原始的視圖函數
    # import datetime
    # now=datetime.datetime.now()
    # html="<html><body>如今時刻:<h1>%s.</h1></body></html>" %now


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


    #另外一種寫法(推薦)
    import datetime
    now=datetime.datetime.now()
    return render(req, 'current_datetime.html', {'current_date':str(now)[:19]})
複製代碼

1 模板語法之變量

在 Django 模板中遍歷複雜數據結構的關鍵是句點字符, 語法:  

1
{{var_name}}

views.py:

複製代碼
def index(request):
    import datetime
    s="hello"
    l=[111,222,333]    # 列表
    dic={"name":"yuan","age":18}  # 字典
    date = datetime.date(1993, 5, 2)   # 日期對象
 
    class Person(object):
        def __init__(self,name):
            self.name=name
 
    person_yuan=Person("yuan")  # 自定義類對象
    person_egon=Person("egon")
    person_alex=Person("alex")
 
    person_list=[person_yuan,person_egon,person_alex]
 
 
    return render(request,"index.html",{"l":l,"dic":dic,"date":date,"person_list":person_list}) 
複製代碼

template: 

1
2
3
4
5
6
<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>

注意:句點符也能夠用來引用對象的方法(無參數方法):

1
<h4>字典:{{ dic.name.upper }}< / h4>

2 模板之過濾器

語法:

1
{{obj|filter__name:param}}

default

若是一個變量是false或者爲空,使用給定的默認值。不然,使用變量的值。例如:

1
{{ value|default: "nothing"  }}

length

返回值的長度。它對字符串和列表都起做用。例如:

1
{{ value|length }}

若是 value 是 ['a', 'b', 'c', 'd'],那麼輸出是 4。

filesizeformat

將值格式化爲一個 「人類可讀的」 文件尺寸 (例如 '13 KB''4.1 MB''102 bytes', 等等)。例如:

1
{{ value|filesizeformat }}

若是 value 是 123456789,輸出將會是 117.7 MB。  

date

若是 value=datetime.datetime.now()

1
{{ value|date: "Y-m-d"  }}  

slice

若是 value="hello world"

1
{{ value| slice : "2:-1"  }}

truncatechars

若是字符串字符多於指定的字符數量,那麼會被截斷。截斷的字符串將以可翻譯的省略號序列(「...」)結尾。

參數:要截斷的字符數

例如:

1
{{ value|truncatechars: 9  }}

safe

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

1
value = "<a href=" ">點擊</a>"
1
{{ value|safe}}

這裏簡單介紹一些經常使用的模板的過濾器,更多詳見

3 模板之標籤 

標籤看起來像是這樣的: {% tag %}。標籤比變量更加複雜:一些在輸出中建立文本,一些經過循環或邏輯來控制流程,一些加載其後的變量將使用到的額外信息到模版中。一些標籤須要開始和結束標籤 (例如{% tag %} ...標籤 內容 ... {% endtag %})。

for標籤

遍歷每個元素:

{% for person in person_list %}
    <p>{{ person.name }}</p>
{% endfor %}

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

遍歷一個字典:

{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

注:循環序號能夠經過{{forloop}}顯示  

forloop.counter            The current iteration of the loop (1-indexed)
forloop.counter0           The current iteration of the loop (0-indexed)
forloop.revcounter         The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0        The number of iterations from the end of the loop (0-indexed)
forloop.first              True if this is the first time through the loop
forloop.last               True if this is the last time through the loop

for ... empty

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

{% for person in person_list %}
    <p>{{ person.name }}</p>

{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

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 %}
複製代碼

with

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

例如:

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

csrf_token

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

4 自定義標籤和過濾器

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

二、在app中建立templatetags模塊(模塊名只能是templatetags)

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

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

1
{ %  load my_tags  % } 

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

1
2
3
4
5
6
7
8
9
10
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .html
{ %  load xxx  % }  
      
# 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  % }

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

5 模板繼承 (extend)

Django模版引擎中最強大也是最複雜的部分就是模版繼承了。模版繼承可讓您建立一個基本的「骨架」模版,它包含您站點中的所有元素,而且能夠定義可以被子模版覆蓋的 blocks 。

經過從下面這個例子開始,能夠容易的理解模版繼承:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My amazing site{%/span> endblock %}</title>
</head>

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

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>
複製代碼

這個模版,咱們把它叫做 base.html, 它定義了一個能夠用於兩列排版頁面的簡單HTML骨架。「子模版」的工做是用它們的內容填充空的blocks。

在這個例子中, block 標籤訂義了三個能夠被子模版內容填充的block。 block 告訴模版引擎: 子模版可能會覆蓋掉模版中的這些位置。

子模版可能看起來是這樣的:

extends 標籤是這裏的關鍵。它告訴模版引擎,這個模版「繼承」了另外一個模版。當模版系統處理這個模版時,首先,它將定位父模版——在此例中,就是「base.html」。

那時,模版引擎將注意到 base.html 中的三個 block 標籤,並用子模版中的內容來替換這些block。根據 blog_entries 的值,輸出可能看起來是這樣的:

請注意,子模版並無定義 sidebar block,因此係統使用了父模版中的值。父模版的 {% block %} 標籤中的內容老是被用做備選內容(fallback)。

這種方式使代碼獲得最大程度的複用,而且使得添加內容到共享的內容區域更加簡單,例如,部分範圍內的導航。

這裏是使用繼承的一些提示:

  • 若是你在模版中使用 {% extends %} 標籤,它必須是模版中的第一個標籤。其餘的任何狀況下,模版繼承都將沒法工做。

  • 在base模版中設置越多的 {% block %} 標籤越好。請記住,子模版沒必要定義所有父模版中的blocks,因此,你能夠在大多數blocks中填充合理的默認內容,而後,只定義你須要的那一個。多一點鉤子總比少一點好。

  • 若是你發現你本身在大量的模版中複製內容,那可能意味着你應該把內容移動到父模版中的一個 {% block %} 中。

  • If you need to get the content of the block from the parent template, the {{ block.super }} 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 {{ block.super }} will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.

  • 爲了更好的可讀性,你也能夠給你的 {% endblock %} 標籤一個 名字 。例如:

    1
    2
    3
    { %  block content  % }
    ...
    { %  endblock content  % }  

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

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

 

Django 模型層(1)

 ORM簡介

ORM簡介

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

  View Code

單表操做

建立表

1  建立模型

建立名爲book的app,在book下的models.py中建立模型:

複製代碼
from django.db import models

# Create your models here.


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)
複製代碼

2 更多字段和參數

每一個字段有一些特有的參數,例如,CharField須要max_length參數來指定VARCHAR數據庫字段的大小。還有一些適用於全部字段的通用參數。 這些參數在文檔中有詳細定義,這裏咱們只簡單介紹一些最經常使用的:

更多字段:

<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參數.
 
 
更多參數:

(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 中的選項。

 

3 settings配置

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

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

注意1:NAME即數據庫的名字,在mysql鏈接前該數據庫必須已經建立,而上面的sqlite數據庫下的db.sqlite3則是項目自動建立 USER和PASSWORD分別是數據庫的用戶名和密碼。設置完後,再啓動咱們的Django項目前,咱們須要激活咱們的mysql。而後,啓動項目,會報錯:no module named MySQLdb 。這是由於django默認你導入的驅動是MySQLdb,但是MySQLdb 對於py3有很大問題,因此咱們須要的驅動是PyMySQL 因此,咱們只須要找到項目名文件下的__init__,在裏面寫入:

1
2
import  pymysql
pymysql.install_as_MySQLdb()

最後經過兩條數據庫遷移命令便可在指定的數據庫中建立表 :

1
2
python manage.py makemigrations
python manage.py migrate

注意2:確保配置文件中的INSTALLED_APPS中寫入咱們建立的app名稱

複製代碼
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "book"
]
複製代碼

注意3:若是報錯以下:

1
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
這個路徑裏的文件把

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

註釋掉 就OK了。

注意4: 若是想打印orm轉換過程當中的sql,須要在settings中進行以下配置:

添加表紀錄

方式1 

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

方式2

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

查詢表紀錄

查詢API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
< 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():            從返回結果中剔除重複紀錄

基於雙下劃線的模糊查詢  

1
2
3
4
5
6
7
8
Book.objects. filter (price__in = [ 100 , 200 , 300 ])
Book.objects. filter (price__gt = 100 )
Book.objects. filter (price__lt = 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" )
Book.objects. filter (pub_date__year = 2012 )

刪除表紀錄

刪除方法就是 delete()。它運行時當即刪除對象而不返回任何值。例如:

1
model_obj.delete()

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

例如,下面的代碼將刪除 pub_date 是2005年的 Entry 對象:

1
Entry.objects. filter (pub_date__year = 2005 ).delete()

在 Django 刪除對象時,會模仿 SQL 約束 ON DELETE CASCADE 的行爲,換句話說,刪除一個對象時也會刪除與它相關聯的外鍵對象。例如:

1
2
3
=  Blog.objects.get(pk = 1 )
# This will delete the Blog and all of its Entry objects.
b.delete()

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

1
Entry.objects. all ().delete()  

若是不想級聯刪除,能夠設置爲:

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

修改表紀錄

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

此外,update()方法對於任何結果集(QuerySet)均有效,這意味着你能夠同時更新多條記錄update()方法會返回一個整型數值,表示受影響的記錄條數。  

章節做業

1 圖書管理系統

實現功能:book單表的增刪改查

2 查詢操做練習

1
2
3
4
5
6
7
8
9
1  查詢老男孩出版社出版過的價格大於 200 的書籍
 
2  查詢 2017 8 月出版的全部以py開頭的書籍名稱
 
3  查詢價格爲 50 , 100 或者 150 的全部書籍名稱及其出版社名稱
 
4  查詢價格在 100 200 之間的全部書籍名稱及其價格
 
5  查詢全部人民出版社出版的書籍的價格(從高到低排序,去重)

 

9 Django 模型層(2)

多表操做

建立模型

實例:咱們來假定下面這些概念,字段和關係

做者模型:一個做者有姓名和年齡。

做者詳細模型:把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(one-to-one)

出版商模型:出版商有名稱,所在城市以及email。

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

模型創建以下:

複製代碼
from django.db import models

# Create your models here.


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.CASCADE) # 與Author表創建多對多的關係,ManyToManyField能夠建在兩個模型中的任意一個,自動建立第三張表 authors=models.ManyToManyField(to='Author',)
複製代碼

 生成表以下:

 

注意事項:

  •  表的名稱myapp_modelName,是根據 模型中的元數據自動生成的,也能夠覆寫爲別的名稱  
  •  id 字段是自動添加的
  •  對於外鍵字段,Django 會在字段名上添加"_id" 來建立數據庫中的列名
  •  這個例子中的CREATE TABLE SQL 語句使用PostgreSQL 語法格式,要注意的是Django 會根據settings 中指定的數據庫類型來使用相應的SQL 語句。
  •  定義好模型以後,你須要告訴Django _使用_這些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中設置,在其中添加models.py所在應用的名稱。
  • 外鍵字段 ForeignKey 有一個 null=True 的設置(它容許外鍵接受空值 NULL),你能夠賦給它空值 None 。

添加表紀錄 

操做前先簡單的錄入一些數據:

publish表:

author表:

authordetail表:

一對多

1
2
3
4
5
6
方式 1 :
    publish_obj = Publish.objects.get(nid = 1 )
    book_obj = Book.objects.create(title = "金瓶眉" ,publishDate = "2012-12-12" ,price = 100 ,publish = publish_obj)
  
方式 2 :
    book_obj = Book.objects.create(title = "金瓶眉" ,publishDate = "2012-12-12" ,price = 100 ,publish_id = 1 )  

核心:book_obj.publish與book_obj.publish_id是什麼? 

多對多

複製代碼
    # 當前生成的書籍對象
    book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1)
    # 爲書籍綁定的作做者對象
    yuan=Author.objects.filter(name="yuan").first() # 在Author表中主鍵爲2的紀錄
    egon=Author.objects.filter(name="alex").first() # 在Author表中主鍵爲1的紀錄

    # 綁定多對多關係,即向關係表book_authors中添加紀錄
    book_obj.authors.add(yuan,egon)    #  將某些特定的 model 對象添加到被關聯對象集合中。   =======    book_obj.authors.add(*[])
複製代碼

數據庫表紀錄生成以下:

book表 

book_authors表

核心:book_obj.authors.all()是什麼?

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

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

more

基於對象的跨表查詢

一對多查詢(Publish 與 Book)

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

1
2
3
4
# 查詢主鍵爲1的書籍的出版社所在的城市
book_obj = Book.objects. filter (pk = 1 ).first()
# book_obj.publish 是主鍵爲1的書籍對象關聯的出版社對象
print (book_obj.publish.city)  

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

1
2
3
4
5
publish = Publish.objects.get(name = "蘋果出版社" )
#publish.book_set.all() : 與蘋果出版社關聯的全部書籍對象集合
book_list = publish.book_set. all ()    
for  book_obj  in  book_list:
        print (book_obj.title)

一對一查詢(Author 與 AuthorDetail)

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

1
2
egon = Author.objects. filter (name = "egon" ).first()
print (egon.authorDetail.telephone)

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

1
2
3
4
5
# 查詢全部住址在北京的做者的姓名
 
authorDetail_list = AuthorDetail.objects. filter (addr = "beijing" )
for  obj  in  authorDetail_list:
      print (obj.author.name)

多對多查詢 (Author 與 Book)

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

1
2
3
4
5
6
# 金瓶眉全部做者的名字以及手機號
 
book_obj = Book.objects. filter (title = "金瓶眉" ).first()
authors = book_obj.authors. all ()
for  author_obj  in  authors:
      print (author_obj.name,author_obj.authorDetail.telephone)

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

1
2
3
4
5
6
# 查詢egon出過的全部書籍的名字
 
     author_obj = Author.objects.get(name = "egon" )
     book_list = author_obj.book_set. all ()         #與egon做者相關的全部書籍
     for  book_obj  in  book_list:
         print (book_obj.title)

注意:

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

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

那麼接下來就會如咱們看到這般:

1
2
3
4
# 查詢 人民出版社出版過的全部書籍
 
publish = Publish.objects.get(name = "人民出版社" )
book_list = publish.bookList. all ()   # 與人民出版社關聯的全部書籍對象集合

基於雙下劃線的跨表查詢 

Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯繫。要作跨關係查詢,就使用兩個下劃線來連接模型(model)間關聯字段的名稱,直到最終連接到你想要的model 爲止。

 

'''
    正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表
'''

一對多查詢

複製代碼
# 練習:  查詢蘋果出版社出版過的全部書籍的名字與價格(一對多)

    # 正向查詢 按字段:publish

    queryResult=Book.objects
            .filter(publish__name="蘋果出版社")
            .values_list("title","price")

    # 反向查詢 按表名:book

    queryResult=Publish.objects
              .filter(name="蘋果出版社")
              .values_list("book__title","book__price")
複製代碼

多對多查詢  

複製代碼
# 練習: 查詢alex出過的全部書籍的名字(多對多)

    # 正向查詢 按字段:authors:
    queryResult=Book.objects
            .filter(authors__name="yuan")
            .values_list("title")

    # 反向查詢 按表名:book
    queryResult=Author.objects
              .filter(name="yuan")
              .values_list("book__title","book__price")
複製代碼

一對一查詢

複製代碼
    # 查詢alex的手機號
    
    # 正向查詢
    ret=Author.objects.filter(name="alex").values("authordetail__telephone")

    # 反向查詢
    ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")
複製代碼

進階練習(連續跨表)

複製代碼
# 練習: 查詢人民出版社出版過的全部書籍的名字以及做者的姓名


    # 正向查詢
    queryResult=Book.objects
            .filter(publish__name="人民出版社")
            .values_list("title","authors__name")
    # 反向查詢
    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("book__title","book__authors__age","book__authors__name")


# 練習: 手機號以151開頭的做者出版過的全部書籍名稱以及出版社名稱

# 方式1: queryResult=Book.objects             .filter(authors__authorDetail__telephone__regex="151")             .values_list("title","publish__name") # 方式2: ret=Author.objects .filter(authordetail__telephone__startswith="151") .values("book__title","book__publish__name")
複製代碼

related_name

反向查詢時,若是定義了related_name ,則用related_name替換表名,例如:

1
publish  =  ForeignKey(Blog, related_name = 'bookList' )
複製代碼
# 練習: 查詢人民出版社出版過的全部書籍的名字與價格(一對多)

# 反向查詢 再也不按表名:book,而是related_name:bookList

queryResult=Publish.objects               .filter(name="人民出版社")               .values_list("bookList__title","bookList__price")
複製代碼

聚合查詢與分組查詢

聚合

aggregate(*args, **kwargs)

1
2
3
4
# 計算全部圖書的平均價格
     >>>  from  django.db.models  import  Avg
     >>> Book.objects. all ().aggregate(Avg( 'price' ))
     { 'price__avg' 34.35 }

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

1
2
>>> Book.objects.aggregate(average_price = Avg( 'price' ))
{ 'average_price' 34.35 }

若是你但願生成不止一個聚合,你能夠向aggregate()子句中添加另外一個參數。因此,若是你也想知道全部圖書價格的最大值和最小值,能夠這樣查詢:

1
2
3
>>>  from  django.db.models  import  Avg,  Max Min
>>> Book.objects.aggregate(Avg( 'price' ),  Max ( 'price' ),  Min ( 'price' ))
{ 'price__avg' 34.35 'price__max' : Decimal( '81.20' ),  'price__min' : Decimal( '12.99' )}

分組

複製代碼
###################################--單表分組查詢--#######################################################

查詢每個部門名稱以及對應的員工數

emp:

id  name age   salary    dep
1   alex  12   2000     銷售部
2   egon  22   3000     人事部
3   wen   22   5000     人事部


sql語句:
select dep,Count(*) from emp group by dep;

ORM:
emp.objects.values("dep").annotate(c=Count("id")

###################################--多表分組查詢--###########################


多表分組查詢:

查詢每個部門名稱以及對應的員工數


emp:

id  name age   salary   dep_id
1   alex  12   2000       1
2   egon  22   3000       2
3   wen   22   5000       2


dep

id   name 
1    銷售部
2    人事部



emp-dep:

id  name age   salary   dep_id   id   name 
1   alex  12   2000       1      1    銷售部
2   egon  22   3000       2      2    人事部
3   wen   22   5000       2      2    人事部


sql語句:
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id

ORM:
dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")
複製代碼
  View Code

annotate()爲調用的QuerySet中每個對象都生成一個獨立的統計值(統計方法用聚合函數)。

總結 :跨表分組查詢本質就是將關聯表join成一張表,再按單表的思路進行分組查詢。 

查詢練習

(1) 練習:統計每個出版社的最便宜的書

1
2
3
publishList = Publish.objects.annotate(MinPrice = Min ( "book__price" ))
for  publish_obj  in  publishList:
     print (publish_obj.name,publish_obj.MinPrice)

annotate的返回值是querySet,若是不想遍歷對象,能夠用上valuelist:

queryResult= Publish.objects
            .annotate(MinPrice=Min("book__price"))
            .values_list("name","MinPrice")
print(queryResult)

'''


SELECT "app01_publish"."name", MIN("app01_book"."price") AS "MinPrice" FROM "app01_publish"
LEFT JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id")
GROUP BY "app01_publish"."nid", "app01_publish"."name", "app01_publish"."city", "app01_publish"."email"

'''

 

(2) 練習:統計每一本書的做者個數

ret=Book.objects.annotate(authorsNum=Count('authors__name'))

(3) 統計每一本以py開頭的書籍的做者個數:

 queryResult=Book.objects
           .filter(title__startswith="Py")
           .annotate(num_authors=Count('authors'))

(4) 統計不止一個做者的圖書:

queryResult=Book.objects
          .annotate(num_authors=Count('authors'))
          .filter(num_authors__gt=1)

(5) 根據一本圖書做者數量的多少對查詢集 QuerySet進行排序:

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

(6) 查詢各個做者出的書的總價格:

#   按author表的全部字段 group by
    queryResult=Author.objects
              .annotate(SumPrice=Sum("book__price"))
              .values_list("name","SumPrice") print(queryResult)

F查詢與Q查詢

F查詢

在上面全部的例子中,咱們構造的過濾器都只是將字段值與某個常量作比較。若是咱們要對兩個字段的值作比較,那該怎麼作呢?

Django 提供 F() 來作這樣的比較。F() 的實例能夠在查詢中引用字段,來比較同一個 model 實例中兩個不一樣字段的值。

1
2
3
4
# 查詢評論數大於收藏數的書籍
 
    from  django.db.models  import  F
    Book.objects. filter (commnetNum__lt = F( 'keepNum' ))

Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做。

1
2
# 查詢評論數大於收藏數2倍的書籍
     Book.objects. filter (commnetNum__lt = F( 'keepNum' ) * 2 )

修改操做也可使用F函數,好比將每一本書的價格提升30元:

1
Book.objects. all ().update(price = F( "price" ) + 30 ) 

Q查詢

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

1
2
from  django.db.models  import  Q
Q(title__startswith = 'Py' )

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

1
bookList = Book.objects. filter (Q(authors__name = "yuan" )|Q(authors__name = "egon" ))

等同於下面的SQL WHERE 子句:

1
WHERE name  = "yuan"  OR name  = "egon"

你能夠組合& 和|  操做符以及使用括號進行分組來編寫任意複雜的Q 對象。同時,Q 對象可使用~ 操做符取反,這容許組合正常的查詢和取反(NOT) 查詢:

1
bookList = Book.objects. filter (Q(authors__name = "yuan" ) & ~Q(publishDate__year = 2017 )).values_list( "title" )

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

1
2
3
bookList = Book.objects. filter (Q(publishDate__year = 2016 ) | Q(publishDate__year = 2017 ),
                               title__icontains = "python"
                              )
相關文章
相關標籤/搜索