Django基礎--Django基本命令、路由配置系統(URLconf)、編寫視圖、Template、數據庫與ORM

web框架

框架,即framework,特指爲解決一個開放性問題而設計的具備必定約束性的支撐結構。php

使用框架能夠幫你快速開發特定的系統。html

簡單地說,就是你用別人搭建好的舞臺來作表演。前端

嘗試搭建一個簡單的web框架:python

由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。mysql

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

#---------------------myweb.py------------------------


from wsgiref.simple_server import  make_server

def foo1(request):
    f=open("alex.html","rb")
    data=f.read()
    f.close()

    return [data]

def foo2(request):
    f=open("egon.html","rb")
    data=f.read()
    f.close()

    return [data]

def reg(request):
    f=open("register.html","rb")
    data=f.read()
    f.close()

    return [data]

def login(request):
    f=open("login.html","rb")
    data=f.read()
    f.close()

    return [data]



def auth(request):
    print("++++++",request)
    user_union,pwd_union=request.get("QUERY_STRING").split("&")
    _,user=user_union.split("=")
    _,pwd=pwd_union.split("=")

    if user=="yuan" and pwd=="123":
        return [b"login successful"]
    else:
        return [b"user or password exists errors"]

def routers():
    URLpattern=(
        ("/login",login),
        ("/auth",auth),
        ("/alex",foo1),
        ("/egon",foo2),
        ("reg",reg)
    )
    return URLpattern

def application(environ,start_response):
    print("environ",environ)
    path=environ.get("PATH_INFO")
    print("path",path)
    start_response("200 ok",[("content-type","text/html")])

    urlpattern=routers()
    func=None
    for item in urlpattern:
        if path==item[0]:
            func=item[1]
            break

    if func:
        return func(environ)
    else:
        return [b"404"]

t=make_server("",8880,application)
t.serve_forever()  # 開始監聽t請求:

 

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

<body>
<h1>登陸頁面</h1>
<form action="http://127.0.0.1:8880/auth">
    <p>姓名<input type="text" name="user"></p>
    <p>密碼<input type="password" name="pwd"></p>
    <input type="submit" value="提交">
</form>
</body>

 

#---------------------egon.html--------------------------------


<body>
    <h1>welcome to egon's home</h1>
</body>



#----------------------alex.html----------------------------------


<body>
    <h1>welcome to alex's home</h1>
</body>

 注意:git

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

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

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

在application()函數中,調用:

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

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

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

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

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

 

MVC和MTV模式

MVC

  • 大部分開發語言中都有MVC框架
  • MVC框架的核心思想是:解耦
  • 下降各功能模塊之間的耦合性,方便變動,更容易重構代碼,最大程度上實現代碼的重用
  • m表示model,主要用於對數據庫層的封裝
  • v表示view,用於向用戶展現結果
  • c表示controller,是核心,用於處理請求、獲取數據、返回結果

MTV

  • Django是一款python的web開發框架
  • 與MVC有所不一樣,屬於MTV框架
  • m表示model,負責與數據庫(ORM)交互
  • v表示view,是核心,負責接收請求、獲取數據、返回結果
  • t表示template,負責呈現內容到瀏覽器

此外,Django還有一個url分發器,它的做用是將一個個URL的頁面請求分發給不一樣的view處理,view再調用相應的Model和Template程序員

 

下面進入正式部分。web

主要知識點框架羅列:ajax

  • 環境搭建
  • 定義模型
  • 使用後臺管理
  • 路由配置系統(URLconf)

  • 編寫視圖
  • 定義模板

一 Django基本命令

 安裝Django

pip install django

 

(雙等號能夠指定安裝版本,好比我的安裝1.11.4版本,就可使用:pip install django==1.11.4)

說明:使用pip install django命令進行安裝時,會自動刪除舊版本,再安裝新版本

查看Django版本

進入python shell,運行以下代碼:

import django
django.get_version()

 

建立項目

django-admin startproject project_name

 

項目結構:

例如建立一個名爲test1的項目,結構以下:

說明:

  • manage.py:一個命令行工具,可使你用多種方式對Django項目進行交互
  • 內層的目錄:項目的真正的Python包
  • _init _.py:一個空文件,它告訴Python這個目錄應該被看作一個Python包
  • settings.py:項目的配置
  • urls.py:項目的URL聲明
  • wsgi.py:項目與WSGI兼容的Web服務器入口

在項目下建立應用,好比blog:

項目與應用關係:
        一個項目有多個應用
        一個應用能夠被多個項目擁有

命令:

python manage.py startapp blog

 

 

啓動django項目

python manage.py runserver IP PORT
  • 能夠不寫ip,默認端口爲8000

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

 

同步更改數據庫表或字段

 

注意:Django 1.7.1 及以上的版本須要用如下命令
    python manage.py makemigrations
    python manage.py migrate

 清空數據庫

 

python manage.py flush

 此命令會詢問是 yes 仍是 no, 選擇 yes 會把數據所有清空掉,只留下空表。

 

 Django 項目環境終端

python manage.py shell

 

python manage.py dbshell

 更多命令

 

python manage.py

 查看全部的命令,忘記子名稱的時候特別有用。

 

一些經常使用的配置:

數據庫配置

在settings.py文件中,經過DATABASES項進行數據庫設置。

Django默認使用SQLite數據庫,同時支持MySQL等主流數據庫。

<1> sqlite

            django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動 , 引擎名稱:django.db.backends.sqlite3

在settings裏有以下設置:

 


<2> mysql 引擎名稱:django.db.backends.mysql

 

mysql驅動程序

       MySQLdb(mysql python)
       mysqlclient
       MySQL
       PyMySQL(純python的mysql驅動程序)

 

 若是咱們想要更改成MySQL數據庫,須要修改以下:

 

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql', 

        'NAME': 'books',    #你的數據庫名稱

        'USER': 'root',   #你的數據庫用戶名

        'PASSWORD': '', #你的數據庫密碼

        'HOST': '', #你的數據庫主機,留空默認爲localhost

        'PORT': '3306', #你的數據庫端口

    }

}

 注意:

NAME即數據庫的名字,在mysql鏈接前該數據庫必須已經建立,而上面的sqlite數據庫下的db.sqlite3則是項目自動建立

USER和PASSWORD分別是數據庫的用戶名和密碼。

設置完後,再啓動咱們的Django項目前,咱們須要激活咱們的mysql。

而後,啓動項目,會報錯:no module named MySQLdb

這是由於django默認你導入的驅動是MySQLdb,但是MySQLdb對於py3有很大問題,因此咱們須要的驅動是PyMySQL

因此,咱們只須要找到項目名文件下的__init__,在裏面寫入:

import pymysql
pymysql.install_as_MySQLdb()

問題解決!

 

static配置

settings文件中static的配置以下:

STATIC文件還能夠配置STATICFILES_DIRS,指定額外的靜態文件存儲位置。

 

#注意1:
        #爲了後端的更改不會影響前端的引入,避免形成前端大量修改

        STATIC_URL = '/static/'               #引用名
        STATICFILES_DIRS = (
            os.path.join(BASE_DIR,"statics")  #實際名 ,即實際文件夾的名字
        )

        #django對引用名和實際名進行映射,引用時,只能按照引用名來,不能按實際名去找

例如:咱們寫一個模板文件,通常會須要引入JS文件
咱們常寫的格式爲:
#<script src="/statics/jquery-3.2.1.js"></script>
#--------------錯誤--------------------------

正確引用方式:

必須用STATIC_URL = '/static/':
#<script src="/static/jquery-3.2.1.js"></script>

 

 #注意2(statics文件夾寫在不一樣的app下,靜態文件的調用):

        STATIC_URL = '/static/'

        STATICFILES_DIRS=(
            ('hello',os.path.join(BASE_DIR,"app01","statics")) ,
        )

        #<script src="/static/hello/jquery-3.2.1.js"></script>

 

#注意3:
        STATIC_URL = '/static/'
        {% load staticfiles %}
       # <script src={% static "jquery-3.2.1.js" %}></script>

  日誌記錄部分:

  應用場景:對於每次建立一個對象,想顯示對應的raw 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',
        },
    }
}

 

 二 路由配置系統(URLconf)

 

  • 在settings.py文件中經過ROOT_URLCONF指定根級url的配    
  • urlpatterns是一個url()實例的列表

  例:

 

ROOT_URLCONF = 'BlogSM.urls'

(建立的項目名稱爲:BlogSM)

 

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^add/',views.add),
  url(r'^$',views.add),  #(此條通常用做增長用戶體驗,好比首頁展現特定內容)

]

 

  • 一個url()對象包括:
    • 正則表達式
    • 視圖函數
    • 名稱name
  • 編寫URLconf的注意:
    • 若要從url中捕獲一個值,須要在它周圍設置一對圓括號
    • 不須要添加一個前導的反斜槓,如應該寫做'test/',而不該該寫做'/test/'
    • 每一個正則表達式前面的r表示字符串不轉義
  • 請求的url被看作是一個普通的python字符串,進行匹配時不包括get或post請求的參數及域名
http://www.itcast.cn/python/1/?i=1&p=new,只匹配「/python/1/」部分

示例:

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

 

一些請求的例子:

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

 

 

無名分組(named group)

正則表達式非命名組(經過圓括號),經過位置參數傳遞給視圖

 

有名分組(named group)

正則表達式命名組,經過關鍵字參數傳遞給視圖

語法:(?P<name>pattern),其中name 是組的名稱,pattern 是要匹配的模式。

例如:

urlpatterns = [ url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
  url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
 ]
   /articles/2005/03/    
    請求將調用views.month_archive(request, year='2005', month='03')函數
    /articles/2003/03/03/ 
    請求將調用函數views.article_detail(request, year='2003', month='03', day='03')。
 

 URLconf 在什麼上查找

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

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

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

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

 

 捕獲的參數永遠是字符串

每一個捕獲的參數都做爲一個普通的Python 字符串傳遞給視圖,不管正則表達式使用的是什麼匹配方式。例如,下面這行URLconf 中:
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

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

 

指定視圖參數的默認值

有一個方便的小技巧是指定視圖參數的默認值。 下面是一個URLconf 和視圖的示例:
# URLconf
from django.conf.urls import url

from . import views

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

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

    ...
 

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

包含其它的URLconfs

 
from django.conf.urls import include, url

urlpatterns = [
   url(r'^admin/', admin.site.urls),
   url(r'^blog/', include('blog.urls')),
]
 

匹配過程:先與主URLconf匹配,成功後再用剩餘的部分與應用中的URLconf匹配 

 
請求http://www.itcast.cn/booktest/1/
在sesstings.py中的配置:
url(r'^booktest/', include('booktest.urls', namespace='booktest')),
在booktest應用urls.py中的配置
url(r'^([0-9]+)/$', views.detail, name='detail'),
匹配部分是:/booktest/1/
匹配過程:在settings.py中與「booktest/」成功,再用「1/」與booktest應用的urls匹配
 
  • 使用include能夠去除urlconf的冗餘
  • 參數:視圖會收到來自父URLconf、當前URLconf捕獲的全部參數
  • 在include中經過namespace定義命名空間,用於反解析

 

name參數

'''

urlpatterns = [
    url(r'^index',views.index,name='INDEX'),

]
###################

def index(req):
    if req.method=='POST':
        username=req.POST.get('username')
        password=req.POST.get('password')
        if username=='alex' and password=='123':
            return HttpResponse("登錄成功")

    return render(req,'index.html')

#####################

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#     <form action="/index/" method="post">#}
     <form action="{% url 'INDEX' %}" method="post">
         用戶名:<input type="text" name="username">
         密碼:<input type="password" name="password">
         <input type="submit" value="submit">
     </form>
</body>
</html>


#######################

'''

 

錯誤視圖

 

Django原生自帶幾個默認視圖用於處理HTTP錯誤

404 (page not found) 視圖

  • defaults.page_not_found(request, template_name='404.html')
  • 默認的404視圖將傳遞一個變量給模板:request_path,它是致使錯誤的URL
  • 若是Django在檢測URLconf中的每一個正則表達式後沒有找到匹配的內容也將調用404視圖
  • 若是在settings中DEBUG設置爲True,那麼將永遠不會調用404視圖,而是顯示URLconf 並帶有一些調試信息
  • 在templates中建立404.html
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
找不到了
<hr/>
{{request_path}}
</body>
</html>

 在settings.py中修改調試

DEBUG = False
ALLOWED_HOSTS = ['*', ]

 請求一個不存在的地址

http://127.0.0.1:8000/test/

 500 (server error) 視圖

  • defaults.server_error(request, template_name='500.html')
  • 在視圖代碼中出現運行時錯誤
  • 默認的500視圖不會傳遞變量給500.html模板
  • 若是在settings中DEBUG設置爲True,那麼將永遠不會調用505視圖,而是顯示URLconf 並帶有一些調試信息

400 (bad request) 視圖

    defaults.bad_request(request, template_name='400.html')
    錯誤來自客戶端的操做
    當用戶進行的操做在安全方面可疑的時候,例如篡改會話cookie

 

編寫視圖

'''
http請求-響應過程當中有兩個核心對象:

        http請求對象:HttpRequest

        http響應響應:HttpResponse

所在位置:django.http

'''

 一個視圖函數,或者簡短來講叫作視圖,是一個簡單的Python函數,它接受web請求,而且返回web響應。

響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片. . . 是任何東西均可以。

不管視圖自己包含什麼邏輯,都要返回響應。

一個簡單的視圖

返回當前日期和時間:

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

 每一個視圖函數都應接收HttpRequest對象做爲第一個參數,通常叫作request。

 

render函數

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

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

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

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

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

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

     status:響應的狀態碼。默認爲200。

 

redirect函數

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

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

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

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

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


def yuan_back(req):

    name="苑昊"

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

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

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

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

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

視圖之其餘:

 HttpReqeust對象

  • 服務器接收到http協議的請求後,會根據報文建立HttpRequest對象
  • 視圖函數的第一個參數是HttpRequest對象
  • 在django.http模塊中定義了HttpRequest對象的API

屬性

  • 下面除非特別說明,屬性都是隻讀的
  • path:一個字符串,表示請求的頁面的完整路徑,不包含域名
  • method:一個字符串,表示請求使用的HTTP方法,經常使用值包括:'GET'、'POST'
  • encoding:一個字符串,表示提交的數據的編碼方式
    • 若是爲None則表示使用瀏覽器的默認設置,通常爲utf-8
    • 這個屬性是可寫的,能夠經過修改它來修改訪問表單數據使用的編碼,接下來對屬性的任何訪問將使用新的encoding值
  • GET:一個相似於字典的對象,包含get請求方式的全部參數
  • POST:一個相似於字典的對象,包含post請求方式的全部參數
  • FILES:一個相似於字典的對象,包含全部的上傳文件
  • COOKIES:一個標準的Python字典,包含全部的cookie,鍵和值都爲字符串
  • session:一個既可讀又可寫的相似於字典的對象,表示當前的會話,只有當Django 啓用會話的支持時纔可用,詳細內容見「狀態保持」

方法

  • is_ajax():若是請求是經過XMLHttpRequest發起的,則返回True

 

QueryDict對象

  • request對象的屬性GET、POST都是QueryDict類型的對象
  • 與python字典不一樣,QueryDict類型的對象用來處理同一個鍵帶有多個值的狀況
  • 方法get():根據鍵獲取值
    • 只能獲取鍵的一個值
    • 若是一個鍵同時擁有多個值,獲取最後一個值
    • dict.get('鍵',default)
      或簡寫爲
      dict['鍵']
      

       

  • 方法getlist():根據鍵獲取值
    • 將鍵的值以列表返回,能夠獲取一個鍵的多個值
    • dict.getlist('鍵',default)
      GET屬性

 GET屬性

  • QueryDict類型的對象
  • 包含get請求方式的全部參數
  • 與url請求地址中的參數對應,位於?後面
  • 參數的格式是鍵值對,如key1=value1
  • 多個參數之間,使用&鏈接,如key1=value1&key2=value2
  • 鍵是開發人員定下來的,值是可變的
  • 示例以下
  • 建立視圖getTest1用於定義連接,getTest2用於接收一鍵一值,getTest3用於接收一鍵多值
def getTest1(request):
    return render(request,'booktest/getTest1.html')
def getTest2(request):
    return render(request,'booktest/getTest2.html')
def getTest3(request):
    return render(request,'booktest/getTest3.html')
  • 配置url
url(r'^getTest1/$', views.getTest1),
url(r'^getTest2/$', views.getTest2),
url(r'^getTest3/$', views.getTest3),
  • 建立getTest1.html,定義連接
<html>
<head>
    <title>Title</title>
</head>
<body>
連接1:一個鍵傳遞一個值
<a href="/getTest2/?a=1&b=2">gettest2</a><br>
連接2:一個鍵傳遞多個值
<a href="/getTest3/?a=1&a=2&b=3">gettest3</a>
</body>
</html>
  •  完善視圖getTest2的代碼
def getTest2(request):
    a=request.GET['a']
    b=request.GET['b']
    context={'a':a,'b':b}
    return render(request,'booktest/getTest2.html',context)
  • 建立getTest2.html,顯示接收結果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{{ a }}<br>
b:{{ b }}
</body>
</html>

 

  • 完善視圖getTest3的代碼
def getTest3(request):
    a=request.GET.getlist('a')
    b=request.GET['b']
    context={'a':a,'b':b}
    return render(request,'booktest/getTest3.html',context)
  • 建立getTest3.html,顯示接收結果
<html>
<head>
    <title>Title</title>
</head>
<body>
a:{% for item in a %}
{{ item }}
{% endfor %}
<br>
b:{{ b }}
</body>
</html>

 

POST屬性

  • QueryDict類型的對象
  • 包含post請求方式的全部參數
  • 與form表單中的控件對應
  • 問:表單中哪些控件會被提交?
  • 答:控件要有name屬性,則name屬性的值爲鍵,value屬性的值爲鍵,構成鍵值對提交
    • 對於checkbox控件,name屬性同樣爲一組,當控件被選中後會被提交,存在一鍵多值的狀況
  • 鍵是開發人員定下來的,值是可變的
  • 示例以下
  • 定義視圖postTest1
def postTest1(request):
    return render(request,'booktest/postTest1.html')
  •  配置url
url(r'^postTest1$',views.postTest1)

 建立模板postTest1.html

<html>
<head>
    <title>Title</title>
</head>
<body>
<form method="post" action="/postTest2/">
    姓名:<input type="text" name="uname"/><br>
    密碼:<input type="password" name="upwd"/><br>
    性別:<input type="radio" name="ugender" value="1"/>男
    <input type="radio" name="ugender" value="0"/>女<br>
    愛好:<input type="checkbox" name="uhobby" value="胸口碎大石"/>胸口碎大石
    <input type="checkbox" name="uhobby" value="跳樓"/>跳樓
    <input type="checkbox" name="uhobby" value="喝酒"/>喝酒
    <input type="checkbox" name="uhobby" value="登山"/>登山<br>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
  • 建立視圖postTest2接收請求的數據
def postTest2(request):
    uname=request.POST['uname']
    upwd=request.POST['upwd']
    ugender=request.POST['ugender']
    uhobby=request.POST.getlist('uhobby')
    context={'uname':uname,'upwd':upwd,'ugender':ugender,'uhobby':uhobby}
    return render(request,'booktest/postTest2.html',context)
  • 配置url
url(r'^postTest2$',views.postTest2)

 

  • 建立模板postTest2.html
<html>
<head>
    <title>Title</title>
</head>
<body>
{{ uname }}<br>
{{ upwd }}<br>
{{ ugender }}<br>
{{ uhobby }}
</body>
</html>

 

注意:使用表單提交,註釋掉settings.py中的中間件crsf

 

HttpResponse對象

 

  • 在django.http模塊中定義了HttpResponse對象的API
  • HttpRequest對象由Django自動建立,HttpResponse對象由程序員建立
  • 不調用模板,直接返回數據
from django.http import HttpResponse

def index(request):
    return HttpResponse('你好')

 

屬性

  • content:表示返回的內容,字符串類型
  • charset:表示response採用的編碼字符集,字符串類型
  • status_code:響應的HTTP響應狀態碼
  • content-type:指定輸出的MIME類型

方法

  • init :使用頁內容實例化HttpResponse對象
  • write(content):以文件的方式寫
  • flush():以文件的方式輸出緩存區
  • set_cookie(key, value='', max_age=None, expires=None):設置Cookie
    • key、value都是字符串類型
    • max_age是一個整數,表示在指定秒數後過時
    • expires是一個datetime或timedelta對象,會話將在這個指定的日期/時間過時,注意datetime和timedelta值只有在使用PickleSerializer時纔可序列化
    • max_age與expires二選一
    • 若是不指定過時時間,則兩個星期後過時
  • delete_cookie(key):刪除指定的key的Cookie,若是key不存在則什麼也不發生

 

子類JsonResponse

  • 返回json數據,通常用於異步請求
  • _init _(data)
  • 幫助用戶建立JSON編碼的響應
  • 參數data是字典對象
  • JsonResponse的默認Content-Type爲application/json
from django.http import JsonResponse

def index2(requeset):
    return JsonResponse({'list': 'abc'})

 

狀態保持

  • http協議是無狀態的:每次請求都是一次新的請求,不會記得以前通訊的狀態
  • 客戶端與服務器端的一次通訊,就是一次會話
  • 實現狀態保持的方式:在客戶端或服務器端存儲與會話有關的數據
  • 存儲方式包括cookie、session,會話通常指session對象
  • 使用cookie,全部數據存儲在客戶端,注意不要存儲敏感信息
  • 推薦使用sesison方式,全部數據存儲在服務器端,在客戶端cookie中存儲session_id
  • 狀態保持的目的是在一段時間內跟蹤請求者的狀態,能夠實現跨頁面訪問當前請求者的數據
  • 注意:不一樣的請求者之間不會共享這個數據,與請求者一一對應

 

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

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

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

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

認證應用

場景:

一個登錄頁面,在驗證了用戶名和密碼的正確性後跳轉到後臺的頁面。

可是測試發現,若是繞過登錄頁面,直接輸入後臺的url地址也能夠直接訪問的。這個顯然是不合理的。

咱們缺失的就是cookie和session配合的驗證。

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

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

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

{'123abc':dasdasdasd1231231da1231231}

COOKIE

---------------------views.py

def login(request):
    if request.method == "POST":
        user=request.POST.get("user")
        pwd=request.POST.get("pwd")

        if user == "kaylee" and pwd == "123":
            print(request.COOKIES)  #第一次:{}
            print(request.session)  #第一次:<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003D4D0F0>
            obj=redirect("/index/") #這一步不會走index視圖,注意 return redirect纔會重定向
            obj.set_cookie("Yuan123",11111111,max_age= 10)  #三個參數:key,value,期限
            return obj
    return render(request,"login.html")



def index(request):
    print("+++++++++++++",request.COOKIES)  #第一次{}  第二次:{'Yuan123': '11111111'} 第三次:{'Yuan123': '11111111'}
    print("-------------",request.session)  #<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003A9A828>
    is_login=request.COOKIES.get("Yuan123",None)
    if is_login:
        return render(request,"index.html")
    else:
        return redirect("/login/")

 

SESSION

  • 先在templates目錄下建立兩個html,login.html負責登陸頁面。backend頁面表明後臺頁面
  • 第二步 編輯app01應用下的views.py文件,編寫代碼邏輯部分
--------------------------------views.py

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

 

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

 

--------------------------------------urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^backend/', views.backend),
    url(r'^logout/', views.logout),
]
  •  最後打開瀏覽器直接訪問/backend/頁面的時候直接就被重定向到了/login/
  • 只有在輸入了正確的用戶名和密碼以後才進入到了/backend/頁面
--------------------backend.html-----------------內容截取

<div class="container">
    <h2>cookie 內容是 {{ cookie_content }}</h2>
    <h2>session 內容是 {{ session_content }}</h2>
    <h2>登陸用戶名 :{{ username }}</h2>
    <a href="http://830909.blog.51cto.com/logout/">註銷</a>
</div>

 頁面顯示結果:

 

 

從上圖中咱們看到有一下幾點:

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

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

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

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

從火狐瀏覽器裏查看cookie

 

 

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

 

 

 

 cookie、session總結:

 

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



# 二、設置Cookie:
# rep = HttpResponse(...) 或 rep = render(request, ...)
#
# rep.set_cookie(key,value,...)
# rep.set_signed_cookie(key,value,salt='加密鹽',...)
#     參數:
#         key,              鍵
#         value='',         值
#         max_age=None,     超時時間
#         expires=None,     超時時間(IE requires expires, so set it if hasn't been already.)
#         path='/',         Cookie生效的路徑,/ 表示根路徑,特殊的:跟路徑的cookie能夠被任何url的頁面訪問
#         domain=None,      Cookie生效的域名
#         secure=False,     https傳輸
#         httponly=False    只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)


# 因爲cookie保存在客戶端的電腦上,因此,JavaScript和jquery也能夠操做cookie。

# <script src='/static/js/jquery.cookie.js'></script>
# $.cookie("list_pager_num", 30,{ path: '/' });

 

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

  • 數據庫(默認)
  • 緩存
  • 文件
  • 緩存+數據庫
  • 加密cookie

 

 一、數據庫Session

jango默認支持Session,而且默認是將Session數據存儲在數據庫中,即:django_session 表中。
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默認)
     
    SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認)
    SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路徑(默認)
    SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默認)
    SESSION_COOKIE_SECURE = False                            # 是否Https傳輸cookie(默認)
    SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http傳輸(默認)
    SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默認)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否關閉瀏覽器使得Session過時(默認)
    SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次請求都保存Session,默認修改以後才保存(默認)
 
 
 
b. 使用
 
    def index(request):
        # 獲取、設置、刪除Session中數據
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在則不設置
        del request.session['k1']
 
        # 全部 鍵、值、鍵值對
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()
 
 
        # 用戶session的隨機字符串
        request.session.session_key
 
        # 將全部Session失效日期小於當前日期的數據刪除
        request.session.clear_expired()
 
        # 檢查 用戶session的隨機字符串 在數據庫中是否
        request.session.exists("session_key")
 
        # 刪除當前用戶的全部Session數據
        request.session.delete("session_key")
 
        ...

 

 二、緩存Session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置
 
 
    SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串
    SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路徑
    SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                             # 是否Https傳輸cookie
    SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http傳輸
    SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否關閉瀏覽器使得Session過時
    SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次請求都保存Session,默認修改以後才保存
 
 
 
b. 使用
 
    同上

 

 三、文件Session

. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
 
 
    SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串
    SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路徑
    SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                               # 是否Https傳輸cookie
    SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http傳輸
    SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否關閉瀏覽器使得Session過時
    SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次請求都保存Session,默認修改以後才保存
 
b. 使用
 
    同上

 

 四、緩存+數據庫Session

 

 

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

 

五、加密cookie Session

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

 

擴展:Session用戶驗證

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

 

 四 Template

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

模板支持的語法

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

語法格式: {{var_name}}

def current_time(req):

    now=datetime.datetime.now()

    return render(req, 'current_datetime.html', {'current_date':now})

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

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

首先,句點可用於訪問列表索引
-----------views.py
fruit=['apples', 'bananas', 'carrots']

----------------templates
<h2>{{ fruit.0 }}</h2>

 訪問字典:

-----------views.py
dic={"name":"kaylee","age":18}
----------------templates
{% for i,v in dic.items %} 
{{ i }} {{ v }}
{% endfor %}

{{ dic.name }}
{{ dic.age }}

 

#一樣,也能夠經過句點來訪問對象的屬性
比方說, Python 的 datetime.date 對象有 #year 、 month 和 day 幾個屬性,你一樣能夠在模板中使用句點來訪問這些屬性:
-----------views.py
d = datetime.date(1993, 5, 2) ----------------templates {{ d.year }} {{ d.month }} {{ d.day }}

 

使用了一個自定義的類,經過實例變量加一點(dots)來訪問它的屬性,這個方法適 # 用於任意的對象。
-----------views.py
>>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name ----------------templates {{ person.first_name }} {{ person.last_name }}

 

# 點語法也能夠用來引用對象的方法。 例如,每一個 Python 字符串都有 upper() 和 isdigit() # 方法,你在模板中可使用一樣的句點語法來調用它們:
-----------views.py
Context={'var': '123'}
----------------templates
{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}

 

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

  # 1  add          :   給變量加上相應的值
   #
   # 2  addslashes   :    給變量中的引號前加上斜線
   #
   # 3  capfirst     :    首字母大寫
   #
   # 4  cut          :   從字符串中移除指定的字符
   #
   # 5  date         :   格式化日期字符串
   #
   # 6  default      :   若是值是False,就替換成設置的默認值,不然就是用原本的值
   #
   # 7  default_if_none:  若是值是None,就替換成設置的默認值,不然就使用原本的值

 

#實例:

#value1="aBcDe"
{{ value1|upper }}<br>

#value2=5
{{ value2|add:3 }}<br>

#value3='he  llo wo r ld'
{{ value3|cut:' ' }}<br>

#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}<br>

#value5=[]
{{ value5|default:'空的' }}<br>

#value6='<a href="#">跳轉</a>'

{{ value6 }}

{% autoescape off %}
  {{ value6 }}
{% endautoescape %}

{{ value6|safe }}<br>

{{ value6|striptags }}

#value7='1234'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>

#value8='http://www.baidu.com/?a=1&b=3'
{{ value8|urlencode }}<br>
    value9='hello I am yuan'

 

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

語法格式:    {% tags %}

{% if %} 的使用

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

{% if num >= 100 and 8 %}

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

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

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

{% endif %}

 

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

{% if obj1 and obj2 or obj3 %} 

 

{% for %}的使用

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

 

-----------views.py
class Person(object): def __init__(self,name): self.name=name p1=Person("egon") p2=Person("阿毛") p3=Person("ago") querySet=[p1,p2,p3] ----------------templates {% for person in querySet %} <p>{{ person.name }}</p> {% endfor %}

 

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

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

 

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

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

 

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

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

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

    
    {% for object in objects %}   
         {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}   
         {{ object }}   
        </li>  
    {% endfor %}  



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

 

#{% empty %}

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

#         [11, 22, 33, 44, 55]
#            0----11
#            1----22
#            2----33
#            3----44
#            4----55

 

csrf_token標籤

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

 

{% url %}

引用路由配置的地址

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

 

{% with %}

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



{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}

 

{% verbatim %}

禁止render

{% verbatim %}
         {{ hello }}
{% endverbatim %}

 

加載標籤庫:自定義filter和simple_tag

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

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

 

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

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


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


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


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

 

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

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

 

-------------------------------.html
{% load 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 %}

 

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

注意:

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

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

 

 extend模板繼承

解決問題:減小共用頁面區域(好比站點導航)所引發的重複和冗餘代碼

本質上來講,模板繼承就是先構造一個基礎框架模板,然後在其子模板中對它所包含站點公用部分和定義塊進行重載。

你能夠對那些不一樣 的代碼段進行定義,而不是 共同 代碼段。

第一步是定義 基礎模板,該框架以後將由子模板所繼承。

示例:

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

 

子模板的做用就是重載、添加或保留那些塊的內容。

咱們使用模板標籤: {% block %} 。 全部的 {% block %} 標籤告訴模板引擎,子模板能夠重載這些部分。 每一個{% block %}標籤所要作的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。

子模版一:

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

 子模版二:

 

{% extends "base.html" %}
 
{% block title %}Future time{% endblock %}
 
{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}

 注意因爲子模板並無定義 footer 塊,模板系統將使用在父模板中定義的值。

父模板 {% block %} 標籤中的內容老是被看成一條退路。繼承並不會影響到模板的上下文。

換句話說,任何處在繼承樹上的模板均可以訪問到你傳到模板中的每個模板變量。

你能夠根據須要使用任意多的繼承次數。

如下是使用模板繼承的一些訣竅:

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

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

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

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

 

五 數據庫與ORM

 

ORM表模型

設計模型之定義模型類

有一個數據表,就有一個模型類與之對應;

打開models.py文件,定義模型類;

模型類繼承自models.Model類;

 

說明:不須要定義主鍵列,在生成時會自動添加,而且值爲自動增加

當輸出對象時,會調用對象的str方法

例如咱們設計兩張表:圖書表和英雄表,二者關係爲一對多

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20)
    bpub_date = models.DateTimeField()
    def _ _str_ _(self):
        return "%d" % self.pk

class HeroInfo(models.Model):
    hname = models.CharField(max_length=20)
    hgender = models.BooleanField()
    hcontent = models.CharField(max_length=100)
    hBook = models.ForeignKey('BookInfo')
    def _ _str_ _(self):
        return "%d" % self.pk

 設計模型之生成數據表

數據庫與ORM(對象關係映射)

目的: 經過python代碼實現對數據庫的增刪改查

在ORM中,
             表名-----------類名
             字段-----------類屬性
             表中的一條記錄-------------類實例對象

 

  • 激活模型:編輯settings.py文件,將應用名稱加入到installed_apps中

注意:因爲Django版本不一樣,因此實現上述的方式也不一樣,例如本人用的Django版本爲1.11.4版本,系統會自動添加,無需本身再添加

  • 生成遷移文件:根據模型類生成sql語句

 

python manage.py makemigrations
  •  遷移文件被生成到應用的migrations目錄

 

  • 執行遷移:執行sql語句生成數據表

 

python manage.py migrate

 

關係的類型包括:

  • ForeignKey:一對多,將字段定義在多的端中
  • ManyToManyField:多對多,將字段定義在兩端中
  • OneToOneField:一對一,將字段定義在任意一端中
用一訪問多:對象.模型類小寫_set

用一訪問一:對象.模型類小寫

訪問id:對象.屬性_id

 

以建立四張表爲例,模擬建立模型類的過程:

四張表:書表、做者表、做者詳細信息表、出版社表

其中書表與出版社表是一對多(one-to-many)關係,也被稱做外鍵。

書表與做者表是多對多(many-to-many)關係。

此外,做者表、做者詳細信息表二者是一對一關係,咱們知道:

一對一:實質就是在主外鍵(author_id就是foreign key)的關係基礎上,給外鍵加了一個UNIQUE=True的屬性;

 一對多:就是主外鍵關係;(foreign key)

多對多:(ManyToManyField) 自動建立第三張表;(固然咱們也能夠本身建立第三張表:兩個foreign key)

注意在models文件中定義模型類時的前後順序:

 

from django.db import models
class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.name class AuthorDetail(models.Model): sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),)) email = models.EmailField() address = models.CharField(max_length=50) birthday = models.DateField() author = models.OneToOneField(Author) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2,default=10) def __str__(self): return self.title

 

 

 注意:

    • 每一個數據模型都是django.db.models.Model的子類,它的父類Model包含了全部必要的和數據庫交互的方法。並提供了一個簡介漂亮的定義數據庫字段的語法。
    • 每一個模型至關於單個數據庫表(多對多關係例外,會多生成一張關係表),每一個屬性也是這個表中的字段。屬性名就是字段名,它的類型(例如CharField)至關於數據庫的字段類型(例如varchar)。
    • 使用方式
      1. 導入from django.db import models
      2. 經過models.Field建立字段類型的對象,賦值給屬性
    • 上述咱們用到的屬性名的類型:CharField、.URLField、BooleanField、EmailField、DateField、DecimalField,還有其餘等等,見下表。
    • 屬性命名限制
      • 不能是python的保留關鍵字
      • 因爲django的查詢方式,不容許使用連續的下劃線
    • 定義屬性:對於重要數據都作邏輯刪除,不作物理刪除,實現方法是定義isDelete屬性,類型爲BooleanField,默認值爲False

 

字段類型

    AutoField:一個根據實際ID自動增加的IntegerField,一般不指定
        若是不指定,一個主鍵字段將自動添加到模型中
    BooleanField:true/false 字段,此字段的默認表單控制是CheckboxInput
    NullBooleanField:支持null、true、false三種值
    CharField(max_length=字符長度):字符串,默認的表單樣式是 TextInput
    TextField:大文本字段,通常超過4000使用,默認的表單控件是Textarea
    IntegerField:整數
    DecimalField(max_digits=None, decimal_places=None):使用python的Decimal實例表示的十進制浮點數
        DecimalField.max_digits:位數總數
        DecimalField.decimal_places:小數點後的數字位數
    FloatField:用Python的float實例來表示的浮點數
    DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date實例表示的日期
        參數DateField.auto_now:每次保存對象時,自動設置該字段爲當前時間,用於"最後一次修改"的時間戳,它老是使用當前日期,默認爲false
        參數DateField.auto_now_add:當對象第一次被建立時自動設置當前時間,用於建立的時間戳,它老是使用當前日期,默認爲false
        該字段默認對應的表單控件是一個TextInput. 在管理員站點添加了一個JavaScript寫的日曆控件,和一個「Today"的快捷按鈕,包含了一個額外的invalid_date錯誤消息鍵
        auto_now_add, auto_now, and default 這些設置是相互排斥的,他們之間的任何組合將會發生錯誤的結果
    TimeField:使用Python的datetime.time實例表示的時間,參數同DateField
    DateTimeField:使用Python的datetime.datetime實例表示的日期和時間,參數同DateField
    FileField:一個上傳文件的字段
    ImageField:繼承了FileField的全部屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image

 

字段選項

    經過字段選項,能夠實現對字段的約束
    在字段對象時經過關鍵字參數指定
    null:若是爲True,Django 將空值以NULL 存儲到數據庫中,默認值是 False
    blank:若是爲True,則該字段容許爲空白,默認值是 False
    對比:null是數據庫範疇的概念,blank是表單驗證證範疇的
    db_column:字段的名稱,若是未指定,則使用屬性的名稱
    db_index:若值爲 True, 則在表中會爲此字段建立索引
    default:默認值
    primary_key:若爲 True, 則該字段會成爲模型的主鍵字段
    unique:若是爲 True, 這個字段在表中必須有惟一值

 建立對象

  • 當建立對象時,django不會對數據庫進行讀寫操做
  • 調用save()方法才與數據庫交互,將對象保存到數據庫中
  • 說明: _init _方法已經在基類models.Model中使用,在自定義模型中沒法使用

 

tips:

批量導入數據:

例如類名爲:Book

Book.objects.bulk_create(book_list)

 

實例的屬性

  • DoesNotExist:在進行單個查詢時,模型的對象不存在時會引起此異常,結合try/except使用

實例的方法

  • str (self):重寫object方法,此方法在將對象轉換成字符串時會被調用
  • save():將模型對象保存到數據表中
  • delete():將模型對象從數據表中刪除

 

Book.objects.filter(id=1).delete()

 

 

 ORM之增(create,save)


ORM之增(create,save): 
from app01.models import *

#create方式一: Author.objects.create(name='Alvin') #create方式二: Author.objects.create(**{"name":"alex"}) #save方式一: author=Author(name="alvin") author.save() #save方式二: author=Author() author.name="alvin" author.save()

 重點:如何處理外鍵關係的字段如一對多的publisher和多對多的authors?

 

#一對多(ForeignKey):

    #方式一: 因爲綁定一對多的字段,好比publish,存到數據庫中的字段名叫publish_id,因此咱們能夠直接給這個
    #       字段設定對應值:
           Book.objects.create(title='php',
                               publisher_id=2,   #這裏的2是指爲該book對象綁定了Publisher表中id=2的行對象
                               publication_date='2017-7-7',
                               price=99)


    #方式二:
    #       <1> 先獲取要綁定的Publisher對象:
        pub_obj=Publisher(name='河大出版社',address='保定',city='保定',
                state_province='河北',country='China',website='http://www.hbu.com')
    OR  pub_obj=Publisher.objects.get(id=1)

    #       <2>將 publisher_id=2 改成  publisher=pub_obj

#多對多(ManyToManyField()):
  #多對多關係第三張表經過ManyToManyField()自動建立方式,綁定關係僅此一種 author1=Author.objects.get(id=1) author2=Author.objects.filter(name='alvin')[0] book=Book.objects.get(id=1) book.authors.add(author1,author2) #等同於: book.authors.add(*[author1,author2]) book.authors.remove(*[author1,author2]) #------------------- book=models.Book.objects.filter(id__gt=1) authors=models.Author.objects.filter(id=1)[0] authors.book_set.add(*book) authors.book_set.remove(*book) #------------------- book.authors.add(1) book.authors.remove(1) authors.book_set.add(1) authors.book_set.remove(1)   #多對多關係第三張表手動建立方式: class Book2Author(models.Model): author=models.ForeignKey("Author") Book= models.ForeignKey("Book")   # 那麼就還有一種方式: author_obj=models.Author.objects.filter(id=2)[0] book_obj =models.Book.objects.filter(id=3)[0] s=models.Book2Author.objects.create(author_id=1,Book_id=2) s.save() s=models.Book2Author(author=author_obj,Book_id=1) s.save()

 ORM之刪(delete)

>>> Book.objects.filter(id=1).delete()

有時咱們刪除一條信息,實際卻會刪除了多條,好比某本書由兩名做者共同創做完成,那麼刪除該本書,會連同對多關係表中和該本書相關的做者那行記錄刪除。

這種刪除方式就是django默認的級聯刪除。

ORM之改(update和save)

例如,如下幾張表:

書表:

-------------------------------------------------------------------------------------------------

做者表:

-------------------------------------------------------------------------------------------------

出版社表:

-------------------------------------------------------------------------------------------------

書_做者表:

bookW=Book.objects.get(id=5)
    bookW.bookName ="我是一本書"
    bookW.save()
############################

Publisher.objects.filter(id=3).update(name="亞太出版社")
#  注意:不能用get(id=3),由於update是QuerySet對象的方法,filter返回的就是一個QuerySet對象,而get返回的是一個model對象
(filter裏面的條件可能有多個條件符合)

 注意:模型的save()方法,這個方法會更新一行裏的全部列。 而某些狀況下,咱們只須要更新行裏的某幾列。

 

如上:
#--------------------save方法工做機制----------------------------------
###(0.003) SELECT `app01_book`.`id`, `app01_book`.`bookName`, `app01_book`.`price`, 
`app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`id` = 5; args=(5,)

###(0.059) UPDATE `app01_book` SET `bookName` = '我是一本書', `price` = '35.78', 
`publisher_id` = 1 WHERE `app01_book`.`id` = 5; args=('我是一本書', '35.78', 1, 5)
即save方法會將全部屬性從新設定一遍,效率低

#-------------------update方法工做機制---------------------------------------

(0.042) UPDATE `app01_publisher` SET `name` = '亞太出版社' WHERE `app01_publisher`.`id` = 3; args=('亞太出版社', 3)

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

 

 ORM之查(filter,value)

# 查詢相關API:

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

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

#  <3>get(**kwargs):         返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
                  若是未找到會引起"模型類.DoesNotExist"異常;
                  若是多條被返回,會引起"模型類.MultipleObjectsReturned"異常。
            
#-----------下面的方法都是對查詢的結果再進行處理:好比 objects.filter.values()--------

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

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

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

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

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

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

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

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

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

 

---------------了不得的雙下劃線(__)之單表條件查詢----------------

#    models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值
#
#    models.Tb1.objects.filter(id__in=[11, 22, 33])   # 獲取id等於十一、2二、33的數據
#    models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
#
#    models.Tb1.objects.filter(name__contains="ven") # 獲取name字段包含「ven」的值
#    models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
#
#    models.Tb1.objects.filter(id__range=[1, 2])   # 範圍bettwen and
#
#    startswith,istartswith, 
#   endswith, iendswith,

 

QuerySet與惰性機制

所謂惰性機制:Publisher.objects.all()或者.filter()等都只是返回了一個QuerySet(查詢結果集對象),它並不會立刻執行sql,而是當調用QuerySet的時候才執行。

QuerySet特色:

       <1>  可迭代的

       <2>  可切片

#objs=models.Book.objects.all()#[obj1,obj2,ob3...]

    #QuerySet:   可迭代

    # for obj in objs:#每一obj就是一個行對象
    #     print("obj:",obj)
    # QuerySet:  可切片

    # print(objs[1])
    # print(objs[1:4])
    # print(objs[::-1])

 

QuerySet的高效使用:

<1>Django的queryset是惰性的

     Django的queryset對應於數據庫的若干記錄(row),經過可選的查詢來過濾。例如,下面的代碼會得
     到數據庫中名字爲‘Dave’的全部的人:person_set = Person.objects.filter(first_name="Dave")
     上面的代碼並無運行任何的數據庫查詢。你可使用person_set,給它加上一些過濾條件,或者將它傳給某個函數,
     這些操做都不會發送給數據庫。這是對的,由於數據庫查詢是顯著影響web應用性能的因素之一。

<2>要真正從數據庫得到數據,你能夠遍歷queryset或者使用if queryset,總之你用到數據時就會執行sql.
   爲了驗證這些,須要在settings里加入 LOGGING(驗證方式)
        obj=models.Book.objects.filter(id=3)
        # for i in obj:
        #     print(i)

        # if obj:
        #     print("ok")

<3>queryset是具備cache的
     當你遍歷queryset時,全部匹配的記錄會從數據庫獲取,而後轉換成Django的model。這被稱爲執行
    (evaluation).這些model會保存在queryset內置的cache中,這樣若是你再次遍歷這個queryset,
     你不須要重複運行通用的查詢。
        obj=models.Book.objects.filter(id=3)

        # for i in obj:
        #     print(i)
                          ## models.Book.objects.filter(id=3).update(title="GO")
                          ## obj_new=models.Book.objects.filter(id=3)
        # for i in obj:
        #     print(i)   #LOGGING只會打印一次

<4>
     簡單的使用if語句進行判斷也會徹底執行整個queryset而且把數據放入cache,雖然你並不須要這些
     數據!爲了不這個,能夠用exists()方法來檢查是否有數據:

            obj = Book.objects.filter(id=4)
            #  exists()的檢查能夠避免數據放入queryset的cache。
            if obj.exists():
                print("hello world!")

<5>當queryset很是巨大時,cache會成爲問題

     處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統
     進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache,可使用iterator()方法
     來獲取數據,處理完數據就將其丟棄。
        objs = Book.objects.all().iterator()
        # iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存
        for obj in objs:
            print(obj.name)
        #BUT,再次遍歷沒有打印,由於迭代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了
        for obj in objs:
            print(obj.name)

     #固然,使用iterator()方法來防止生成cache,意味着遍歷同一個queryset時會重複執行查詢。因此使
     #用iterator()的時候要小心,確保你的代碼在操做一個大的queryset時沒有重複執行查詢

總結:
    queryset的cache是用於減小程序對數據庫的查詢,在一般的使用下會保證只有在須要的時候纔會查詢數據庫。
使用exists()和iterator()方法能夠優化程序對內存的使用。不過,因爲它們並不會生成queryset cache,可能
會形成額外的數據庫查詢。

 

對象查詢,單表條件查詢,多表條件關聯查詢

 

# --------------對象查詢----------------------------------
 # 正向查找
   book1=Book.objects.first()    
    print(book1.bookName)    #python
    print(book1.price)    #100.25
    print(book1.publisher)    #人民出版社 南京(由於一對多的關係因此(book1.publisher是一個對象,而不是一個queryset集合) print(book1.publisher.name) #人民出版社

# 反向查找
pubL=Publisher.objects.last()  
print(pubL.name)  #亞太出版社
print(pubL.addr)  #上海
#如何拿到與某一個出版社綁定的Book對象呢?
pub1=Publisher.objects.first()
print(pub1.book_set.all())  # <QuerySet [<Book: go>, <Book: 我是一本書>, <Book: 從你的全世界路過>]>


 注意:條件查詢與對象查詢對應,是指在filter,values等方法中的經過__來明確查詢條件。

#----------------了不得的雙下劃線(__)之多表條件關聯查詢---------------

# 正向查找(條件)

#     ret3=models.Book.objects.filter(title='Python').values('id')
#     print(ret3)#[{'id': 1}]

      #正向查找(條件)之一對多

      ret4=models.Book.objects.filter(title='Python').values('publisher__city')
      print(ret4)  #[{'publisher__city': '北京'}]

      #正向查找(條件)之多對多
      ret5=models.Book.objects.filter(title='Python').values('author__name')
      print(ret5)
      ret6=models.Book.objects.filter(author__name="alex").values('title')
      print(ret6)

      #注意
      #正向查找的publisher__city或者author__name中的publisher,author是book表中綁定的字段
      #一對多和多對多在這裏用法沒區別

# 反向查找(條件)

    #反向查找之一對多:
    ret8=models.Publisher.objects.filter(book__title='Python').values('name')
    print(ret8)#[{'name': '人大出版社'}]  注意,book__title中的book就是Publisher的關聯表名

    ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors')
    print(ret9)#[{'book__authors': 1}, {'book__authors': 2}]

    #反向查找之多對多:
    ret10=models.Author.objects.filter(book__title='Python').values('name')
    print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}]

    #注意
    #正向查找的book__title中的book是表名Book
    #一對多和多對多在這裏用法沒區別

 

模型之其餘一

 

字段查詢
實現where子名,做爲方法filter()、exclude()、get()的參數 語法:屬性名稱__比較運算符=值 表示兩個下劃線,左側是屬性名稱,右側是比較類型 對於外鍵,使用「屬性名_id」表示外鍵的原始值

 

比較運算符
exact:表示判等,大小寫敏感;若是沒有寫「 比較運算符」,表示判等
contains:是否包含,大小寫敏感
startswith、endswith:以value開頭或結尾,大小寫敏感 isnull、isnotnull:是否爲null
在前面加個i表示不區分大小寫,如iexact、icontains、istarswith、iendswith
in:是否包含在範圍內
例如:

gt、gte、lt、lte:大於、大於等於、小於、小於等於 year、month、day、week_day、hour、minute、second:對日期間類型的屬性進行運算: filter(bpub_date__year=1980) filter(bpub_date__gt=date(1980, 12, 31)) 查詢的快捷方式:pk,pk表示primary key,默認的主鍵是id filter(pk__lt=6) filter(pk__in=[1, 2, 3, 4, 5])

聚合與分組:aggregate、annotate

  • 使用aggregate()函數返回聚合函數的值
  • 函數:Avg,Count,Max,Min,Sum

示例:

求全部書籍的平均價格:

from django.db.models import Avg,Max,Min,
Book.objects.all().aggregate(Avgprice=Avg("price"))

鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。若是你想要爲聚合值指定
一個名稱,能夠向聚合子句提供它:
>>> Book.objects.all().aggregate(average_price=Avg('price'))

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

求全部書籍的最高價格

Book.objects.all().aggregate(Maxprice=Max("price"))

 求老男孩出版過得書中的最高價

Book.objeacts.filter(authorName="老男孩").aggregate(Maxprice=Max("price"))

 求每個做者出版過得書中的最低價

Book.objects.values("authorName").annotate(Minprice=Min("price"))

查詢alex出的書總價格      

 

Book.objeacts.filter(authorName="alex").aggregate(Sumprice=Sum("price"))

 

 count的通常用法:

count = list.count()

 F對象

  • django支持對F()對象使用算數運算

示例:將全部書籍的價格增加20元


Book.objects.all().update(price=F("price")+20) from django.db.models import F

 Q對象

  • 過濾器的方法中關鍵字參數查詢,會合併爲And進行
  • 須要進行or查詢,使用Q()對象
  • Q對象(django.db.models.Q)用於封裝一組關鍵字參數,這些關鍵字參數與「比較運算符」中的相同
  • Q對象可使用&(and)、|(or)操做符組合起來
  • 當操做符應用在兩個Q對象時,會產生一個新的Q對象
  •  使用~(not)操做符在Q對象前表示取反

示例:

查詢書籍名字或者以老開頭,或者價格大於200的書籍

from django.db.models import Q
寫法一:Book.objects.filter(bookName__startswith="老",price__gt=200) 寫法二:Book.objects.filter(Q(bookName__startswith="老") | Q(price__gt=200))

 查詢書籍名字或者以老開頭,或者價格大於200,或者編號爲5的書籍

Book.objects.filter(Q(price__gt=100) | Q(id=5),bookName__startswith="老")

查詢編號不小於6的書籍

Book.objects.filter(~Q(id__lt=6))

 

模型之其餘二:

  • 查詢集表示從數據庫中獲取的對象集合
  • 查詢集能夠含有零個、一個或多個過濾器
  • 過濾器基於所給的參數限制查詢的結果
  • 從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句

查詢集

  • 在管理器上調用過濾器方法會返回查詢集
  • 查詢集通過過濾器篩選後返回新的查詢集,所以能夠寫成鏈式過濾
  • 惰性執行:建立查詢集不會帶來任何數據庫的訪問,直到調用數據時,纔會訪問數據庫
  • 什麼時候對查詢集求值:迭代,序列化,與if合用
  • 返回查詢集的方法,稱爲過濾器
    • all()
    • filter()
    • exclude()
    • order_by()
    • values():一個對象構成一個字典,而後構成一個列表返回
  • 寫法:
filter(鍵1=值1,鍵2=值2)
等價於
filter(鍵1=值1).filter(鍵2=值2)
  • 返回單個值的方法
  • get():返回單個知足條件的對象
    • 若是未找到會引起"模型類.DoesNotExist"異常
    • 若是多條被返回,會引起"模型類.MultipleObjectsReturned"異常
  • count():返回當前查詢的總條數
  • first():返回第一個對象
  • last():返回最後一個對象
  • exists():判斷查詢集中是否有數據,若是有則返回True

限制查詢集

  • 查詢集返回列表,可使用下標的方式進行限制,等同於sql中的limit和offset子句
  • 注意:不支持負數索引
  • 使用下標後返回一個新的查詢集,不會當即執行查詢
  • 若是獲取一個對象,直接使用[0],等同於[0:1].get(),可是若是沒有數據,[0]引起IndexError異常,[0:1].get()引起DoesNotExist異常

查詢集的緩存

  • 每一個查詢集都包含一個緩存來最小化對數據庫的訪問
  • 在新建的查詢集中,緩存爲空,首次對查詢集求值時,會發生數據庫查詢,django會將查詢的結果存在查詢集的緩存中,並返回請求的結果,接下來對查詢集求值將重用緩存的結果
  • 狀況一:這構成了兩個查詢集,沒法重用緩存,每次查詢都會與數據庫進行一次交互,增長了數據庫的負載
  • 狀況二:兩次循環使用同一個查詢集,第二次使用緩存中的數據
  • 什麼時候查詢集不會被緩存:當只對查詢集的部分進行求值時會檢查緩存,可是若是這部分不在緩存中,那麼接下來查詢返回的記錄將不會被緩存,這意味着使用索引來限制查詢集將不會填充緩存,若是這部分數據已經被緩存,則直接使用緩存中的數據

 

 

管理操做

admin是django強大功能之一,它能共從數據庫中讀取數據,呈如今頁面中,進行管理。

默認狀況下,它的功能已經很是強大,若是你不須要複雜的功能,它已經夠用,可是有時候,一些特殊的功能還須要定製,好比搜索功能,下面就逐步深刻介紹如何定製適合本身的admin應用。

Django會根據定義的模型類徹底自動地生成管理模塊

使用django的管理

  • 建立一個管理員用戶
python manage.py createsuperuser,按提示輸入用戶名、郵箱、密碼
  • 進入管理站點,默承認以對groups、users進行管理

管理界面本地化

  • 編輯settings.py文件,設置編碼、時區
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'

 

向admin註冊booktest的模型

方式一:使用register的方法

  • 打開booktest/admin.py文件,註冊模型

from django.contrib import admin
from models import BookInfo
admin.site.register(BookInfo)

 方式二:使用register的裝飾器

@admin.register(Book)

 

  • 刷新管理頁面,能夠對BookInfo的數據進行增刪改查操做
  • 問題:若是在str方法中返回中文,在修改和添加時會報ascii的錯誤
  • 解決:在str()方法中,將字符串末尾添加「.encode('utf-8')」

自定義管理頁面

  • Django提供了admin.ModelAdmin類
  • 經過定義ModelAdmin的子類,來定義模型在Admin界面的顯示方式
class QuestionAdmin(admin.ModelAdmin):
    ...
admin.site.register(Question, QuestionAdmin)

 

列表頁屬性

  • list_display:顯示字段,能夠點擊列頭進行排序
list_display = ['pk', 'btitle', 'bpub_date']
  •  list_filter:過濾字段,過濾框會出如今右側
list_filter = ['btitle']
  •  search_fields:搜索字段,搜索框會出如今上側
search_fields = ['btitle']
  •  list_per_page:分頁,分頁框會出如今下側
list_per_page = 10
  • ordering:       指定排序字段 

 

from django.contrib import admin
from app01.models import *
# Register your models here.

# @admin.register(Book)#----->單給某個表加一個定製
class MyAdmin(admin.ModelAdmin):
    list_display = ("title","price","publisher")
    search_fields = ("title","publisher")
    list_filter = ("publisher",)
    ordering = ("price",)
    fieldsets =[
        (None,               {'fields': ['title']}),
        ('price information', {'fields': ['price',"publisher"], 'classes': ['collapse']}),
    ]

admin.site.register(Book,MyAdmin)
admin.site.register(Publish)
admin.site.register(Author)


 

添加、修改頁屬性

  • fields:屬性的前後順序
fields = ['bpub_date', 'btitle']
  •  fieldsets:屬性分組
fieldsets = [
    ('basic',{'fields': ['btitle']}),
    ('more', {'fields': ['bpub_date']}),
]

 

布爾值的顯示

  • 發佈性別的顯示不是一個直觀的結果,可使用方法進行封裝
def gender(self):
    if self.hgender:
        return '男'
    else:
        return '女'
gender.short_description = '性別'

 

  • 在admin註冊中使用gender代替hgender
class HeroInfoAdmin(admin.ModelAdmin):
    list_display = ['id', 'hname', 'gender', 'hcontent']
  • 元信息db_table:定義數據表名稱,推薦使用小寫字母,數據表的默認名稱
  •  ordering:對象的默認排序字段,獲取對象的列表時使用,接收屬性構成的列表
  • 字符串前加-表示倒序,不加-表示正序
  • 排序會增長數據庫的開銷
相關文章
相關標籤/搜索