框架,即framework,特指爲解決一個開放性問題而設計的具備必定約束性的支撐結構,使用框架能夠幫你快速開發特定的系統,簡單地說,就是你用別人搭建好的舞臺來作表演。php
Web應用的流程:html
//瀏覽器發送一個HTTP請求; //服務器收到請求,生成一個HTML文檔; //服務器把HTML文檔做爲HTTP響應的Body發送給瀏覽器; //瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。
對於全部的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。前端
import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8")) client.send("<h1 style='color:red'>Hello, world</h1>".encode("utf8")) def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8001)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。
若是要動態生成HTML,就須要把上述步驟本身來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,若是咱們本身來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。python
正確的作法是底層代碼由專門的服務器軟件實現,咱們用Python專一於生成HTML文檔。由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。mysql
這個接口就是WSGI:Web Server Gateway Interface。jquery
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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()
|
整個application()函數自己沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不須要咱們本身編寫, 咱們只負責在更高層次上考慮如何響應請求就能夠了。 application()函數必須由WSGI服務器來調用。有不少符合WSGI規範的服務器,咱們能夠挑選一個來用。 Python內置了一個WSGI服務器,這個模塊叫wsgiref application()函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數: //environ:一個包含全部HTTP請求信息的dict對象; //start_response:一個發送HTTP響應的函數。 在application()函數中,調用: start_response('200 OK', [('Content-Type', 'text/html')]) 就發送了HTTP響應的Header,注意Header只能發送一次,也就是隻能調用一次start_response()函數。 start_response()函數接收兩個參數,一個是HTTP響應碼,一個是一組list表示的HTTP Header,每一個Header用一個包含兩個str的tuple表示。 一般狀況下,都應該把Content-Type頭髮送給瀏覽器。其餘不少經常使用的HTTP Header也應該發送。 而後,函數的返回值b'<h1>Hello, web!</h1>'將做爲HTTP響應的Body發送給瀏覽器。 有了WSGI,咱們關心的就是如何從environ這個dict對象拿到HTTP請求信息,而後構造HTML, 經過start_response()發送Header,最後返回Body。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
print
(environ[
'PATH_INFO'
])
path
=
environ[
'PATH_INFO'
]
start_response(
'200 OK'
, [(
'Content-Type'
,
'text/html'
)])
f1
=
open
(
"index1.html"
,
"rb"
)
data1
=
f1.read()
f2
=
open
(
"index2.html"
,
"rb"
)
data2
=
f2.read()
if
path
=
=
"/yuan"
:
return
[data1]
elif
path
=
=
"/alex"
:
return
[data2]
else
:
return
[
"<h1>404</h1>"
.encode(
'utf8'
)]
|
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
27
28
29
30
31
32
33
34
35
|
from
wsgiref.simple_server
import
make_server
def
f1():
f1
=
open
(
"index1.html"
,
"rb"
)
data1
=
f1.read()
return
[data1]
def
f2():
f2
=
open
(
"index2.html"
,
"rb"
)
data2
=
f2.read()
return
[data2]
def
application(environ, start_response):
print
(environ[
'PATH_INFO'
])
path
=
environ[
'PATH_INFO'
]
start_response(
'200 OK'
, [(
'Content-Type'
,
'text/html'
)])
if
path
=
=
"/yuan"
:
return
f1()
elif
path
=
=
"/alex"
:
return
f2()
else
:
return
[
"<h1>404</h1>"
.encode(
"utf8"
)]
httpd
=
make_server('',
8502
, application)
print
(
'Serving HTTP on port 8084...'
)
# 開始監聽HTTP請求:
httpd.serve_forever()
|
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
from
wsgiref.simple_server
import
make_server
def
f1(req):
print
(req)
print
(req[
"QUERY_STRING"
])
f1
=
open
(
"index1.html"
,
"rb"
)
data1
=
f1.read()
return
[data1]
def
f2(req):
f2
=
open
(
"index2.html"
,
"rb"
)
data2
=
f2.read()
return
[data2]
import
time
def
f3(req):
#模版以及數據庫
f3
=
open
(
"index3.html"
,
"rb"
)
data3
=
f3.read()
times
=
time.strftime(
"%Y-%m-%d %X"
, time.localtime())
data3
=
str
(data3,
"utf8"
).replace(
"!time!"
,
str
(times))
return
[data3.encode(
"utf8"
)]
def
routers():
urlpatterns
=
(
(
'/yuan'
,f1),
(
'/alex'
,f2),
(
"/cur_time"
,f3)
)
return
urlpatterns
def
application(environ, start_response):
print
(environ[
'PATH_INFO'
])
path
=
environ[
'PATH_INFO'
]
start_response(
'200 OK'
, [(
'Content-Type'
,
'text/html'
)])
urlpatterns
=
routers()
func
=
None
for
item
in
urlpatterns:
if
item[
0
]
=
=
path:
func
=
item[
1
]
break
if
func:
return
func(environ)
else
:
return
[
"<h1>404</h1>"
.encode(
"utf8"
)]
httpd
=
make_server('',
8518
, application)
print
(
'Serving HTTP on port 8084...'
)
# 開始監聽HTTP請求:
httpd.serve_forever()
|
著名的MVC模式:所謂MVC就是把web應用分爲模型(M),控制器(C),視圖(V)三層;他們之間以一種插件似的,鬆耦合的方式鏈接在一塊兒。
模型負責業務對象與數據庫的對象(ORM),視圖負責與用戶的交互(頁面),控制器(C)接受用戶的輸入調用模型和視圖完成用戶的請求。git
Django的MTV模式本質上與MVC模式沒有什麼差異,也是各組件之間爲了保持鬆耦合關係,只是定義上有些許不一樣,Django的MTV分別表明:程序員
此外,Django還有一個url分發器,它的做用是將一個個URL的頁面請求分發給不一樣的view處理,view再調用相應的Model和Template
web
django #安裝: pip3 install django 添加環境變量 #1 建立project django-admin startproject mysite ---mysite ---settings.py ---url.py ---wsgi.py ---- manage.py(啓動文件) #2 建立APP python mannage.py startapp app01 #3 settings配置 TEMPLATES STATICFILES_DIRS=( os.path.join(BASE_DIR,"statics"), ) STATIC_URL = '/static/' # 咱們只能用 STATIC_URL,但STATIC_URL會按着你的STATICFILES_DIRS去找 <script src="/statics/jquery-3.1.1.js"></script> ------error-----不能直接用,必須用STATIC_URL = '/static/' <script src="/static/jquery-3.1.1.js"></script> 採用第二種方式 ,後端的更改不會影響前端的引入,避免形成前端大量修改 #4 根據需求設計代碼 url.py view.py #5 使用模版 render(req,"index.html") #6 啓動項目 python manage.py runserver 127.0.0.1:8090 #7 鏈接數據庫,操做數據 model.py
django-admin.py 是Django的一個用於管理任務的命令行工具,manage.py是對django-admin.py的簡單包裝,每個Django Project裏都會有一個mannage.py。正則表達式
<1> 建立一個django工程 : django-admin.py startproject mysite
當前目錄下會生成mysite的工程,目錄結構以下:
. └── mysite ├── manage.py └── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py
<2>在mysite目錄下建立blog應用: python manage.py startapp blog
. ├── blog │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py └── mysite ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-35.pyc │ └── settings.cpython-35.pyc ├── settings.py ├── urls.py └── wsgi.py
<3>啓動django項目:python manage.py runserver 8080
這樣咱們的django就啓動起來了!當咱們訪問:http://127.0.0.1:8080/ 時就能夠看到」It worked!」
<4>生成數據庫
生成同步數據庫的腳本:python manage.py makemigrations
同步數據庫:python manage.py migrate
注意:在開發過程當中,數據庫同步誤操做以後,不免會遇到後面不能同步成功的狀況,解決這個問題的一個簡單粗暴方法是把migrations目錄下的腳本(除init.py以外)所有刪掉,再把數據庫刪掉以後建立一個新的數據庫,數據庫同步操做再從新作一遍。
<5>admin後臺
當咱們訪問http://127.0.0.1:8080/admin/ 時,會出現admin後臺的登陸頁面
因此咱們須要爲進入這個項目的後臺建立超級管理員:python manage.py createsuperuser,設置好用戶名和密碼後即可登陸啦!
<6>清空數據庫: python manage.py flush
<7>查詢某個命令的詳細信息: django-admin.py help startapp
admin 是Django 自帶的一個後臺數據庫管理系統。
<8>啓動交互界面 :python manage.py shell
這個命令和 直接運行 python 進入 shell 的區別是:你能夠在這個 shell 裏面調用當前項目的 models.py 中的 API,對於操做數據,還有一些小測試很是方便。
<9> 終端上輸入 python manage.py 能夠看到詳細的列表,在忘記子名稱的時候特別有用。
demo1提交數據並展現
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
<!
DOCTYPE
html>
<
html
lang="en">
<
head
>
<
meta
charset="UTF-8">
<
title
>Title</
title
>
</
head
>
<
body
>
<
h1
>建立我的信息</
h1
>
<
form
action="/userInfor/" 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
>
<
hr
>
<
h1
>信息展現</
h1
>
<
table
border="1">
<
tr
>
<
td
>姓名</
td
>
<
td
>性別</
td
>
<
td
>郵箱</
td
>
</
tr
>
{% for i in info_list %}
<
tr
>
<
td
>{{ i.username }}</
td
>
<
td
>{{ i.sex }}</
td
>
<
td
>{{ i.email }}</
td
>
</
tr
>
{% endfor %}
</
table
>
</
body
>
</
html
>
|
-----------------------url.py---------------------------------------
url(r'^userInfor/', views.userInfor) -----------------------views.py-------------------------------------- info_list=[] def userInfor(req): if req.method=="POST": username=req.POST.get("username",None) sex=req.POST.get("sex",None) email=req.POST.get("email",None) info={"username":username,"sex":sex,"email":email} info_list.append(info) return render(req,"userInfor.html",{"info_list":info_list})
demo2提交數據並展現(數據庫)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
<!
DOCTYPE
html>
<
html
lang="en">
<
head
>
<
meta
charset="UTF-8">
<
title
>Title</
title
>
</
head
>
<
body
>
<
h1
>建立我的信息</
h1
>
<
form
action="/userInfor/" 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
>
<
hr
>
<
h1
>信息展現</
h1
>
<
table
border="1">
<
tr
>
<
td
>姓名</
td
>
<
td
>性別</
td
>
<
td
>郵箱</
td
>
</
tr
>
{% for i in info_list %}
<
tr
>
<
td
>{{ i.username }}</
td
>
<
td
>{{ i.sex }}</
td
>
<
td
>{{ i.email }}</
td
>
</
tr
>
{% endfor %}
</
table
>
</
body
>
</
html
>
|
----------------------------------------------models.py
from django.db import models # Create your models here. class UserInfor(models.Model): username=models.CharField(max_length=64) sex=models.CharField(max_length=64) email=models.CharField(max_length=64) ----------------------------------------------views.py from django.shortcuts import render from app01 import models # Create your views here. def userInfor(req): if req.method=="POST": u=req.POST.get("username",None) s=req.POST.get("sex",None) e=req.POST.get("email",None) #---------表中插入數據方式一 # info={"username":u,"sex":e,"email":e} # models.UserInfor.objects.create(**info) #---------表中插入數據方式二 models.UserInfor.objects.create( username=u, sex=s, email=e ) info_list=models.UserInfor.objects.all() return render(req,"userInfor.html",{"info_list":info_list}) return render(req,"userInfor.html")
URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL模式以及要爲該URL模式調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。
urlpatterns = [ url(正則表達式, views視圖函數,參數,別名), ]
參數說明:
簡單url配置
from django.conf.urls import url from django.contrib import admin from blog import views urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), #url(r'^articles/[0-9]{4}/$', views.year_archive), url(r'^articles/([0-9]{4})/$', views.year_archive), #no_named group url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
上面用括號括住的內容將被當作參數傳遞給視圖函數,()在正則中表示分組,這些分組是無名分組,將做爲普通參數傳遞進入視圖函數。
命名分組url配置
from django.conf.urls import url from blog import views urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), ]
上面形似(?P正則表達式)的形式在正則表達式中叫作命名分組,就是給該分組取了個名字,在這裏,有名分組將做爲key:value對的方式以關鍵字參數傳遞進視圖函數。
擴展參數配置
from django.conf.urls import url from blog import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]
第三個參數是一個字典,這個字典的鍵值對將做爲關鍵字參數傳遞進視圖函數,若是url中的命名分組和字典中的鍵相同,則字典中的鍵對應的值將會覆蓋url中的命名分組的值傳遞進視圖函數中。
別名配置
-------------------------------------urls-------------------------------------
urlpatterns = [
url(r'^index',views.index,name='bieming'), url(r'^admin/', admin.site.urls), # 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), ] ------------------------------------views------------------------------------- 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')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!
DOCTYPE
html>
<
html
lang="en">
<
head
>
<
meta
charset="UTF-8">
<
title
>Title</
title
>
</
head
>
<
body
>
{# <
form
action="/index/" method="post">#}
<
form
action="{% url 'bieming' %}" method="post">
用戶名:<
input
type="text" name="username">
密碼:<
input
type="password" name="password">
<
input
type="submit" value="submit">
</
form
>
</
body
>
</
html
>
|
第四個參數name=bieming做用就是給url取了個別名,以後在html中若是有form表單要提交到該url,則能夠經過{% url 「bieming」 %}轉換成相應的url。
添加子url
#At any point, your urlpatterns can 「include」 other URLconf modules. This #essentially 「roots」 a set of URLs below other ones. #For example, here’s an excerpt of the URLconf for the Django website itself. #It includes a number of other URLconfs: from django.conf.urls import include, url urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^blog', include('blog.urls')), ] --------------------------blog.urls---------------------------------- from django.conf.urls import url from blog import views urlpatterns = [ url(r'login/', views.login), url(r'home/', views.home) ]
在主項目的urls文件中先導入include,以後在url中的blog中設置include(「blog.urls」)將會將blog子項目中的urls文件中的全部Url添加到django的url路由中。注意,在新版的django中建議include中的url blog後面和blog裏面的url的前面都不須要加/,由於django會給咱們加好。
http請求中產生兩個核心對象:
所在位置:django.http
以前咱們用到的參數request就是HttpRequest 檢測方法:isinstance(request,HttpRequest)
# path: 請求頁面的全路徑,不包括域名 # # method: 請求中使用的HTTP方法的字符串表示。全大寫表示。例如 # # if req.method=="GET": # # do_something() # # elseif req.method=="POST": # # do_something_else() # # GET: 包含全部HTTP GET參數的類字典對象 # # POST: 包含全部HTTP POST參數的類字典對象 # # 服務器收到空的POST請求的狀況也是可能發生的,也就是說,表單form經過 # HTTP POST方法提交請求,可是表單中可能沒有數據,所以不能使用 # if req.POST來判斷是否使用了HTTP POST 方法;應該使用 if req.method=="POST" # # # # COOKIES: 包含全部cookies的標準Python字典對象;keys和values都是字符串。 # # FILES: 包含全部上傳文件的類字典對象;FILES中的每個Key都是<input type="file" name="" />標籤中 name屬性的值,FILES中的每個value同時也是一個標準的python字典對象,包含下面三個Keys: # # filename: 上傳文件名,用字符串表示 # content_type: 上傳文件的Content Type # content: 上傳文件的原始內容 # # # user: 是一個django.contrib.auth.models.User對象,表明當前登錄的用戶。若是訪問用戶當前 # 沒有登錄,user將被初始化爲django.contrib.auth.models.AnonymousUser的實例。你 # 能夠經過user的is_authenticated()方法來辨別用戶是否登錄: # if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware # 時該屬性纔可用 # # session: 惟一可讀寫的屬性,表明當前會話的字典對象;本身有激活Django中的session支持時該屬性纔可用。 #方法 get_full_path(), 好比:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()獲得的結果就是/index33/?name=123 req.path:/index33
對於HttpRequest對象來講,是由django自動建立的,可是,HttpResponse對象就必須咱們本身建立。每一個view請求處理方法必須返回一個HttpResponse對象。
HttpResponse類在django.http.HttpResponse
在HttpResponse對象上擴展的經常使用方法:
總結: render和redirect的區別:
render瀏覽器只是作了一次url請求,的到了一次返回頁面,而redirect瀏覽器第一次請求時服務器給瀏覽器發了個302跳轉,瀏覽器拿到新的url後又進行了一次請求,獲取到了新的頁面。
你可能已經注意到咱們在例子視圖中返回文本的方式有點特別。 也就是說,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的代碼分離開會更乾淨簡潔更容易維護。 咱們可使用 Django的 模板系統 (Template System)來實現這種模式,這就是本章要具體討論的問題。
模板組成:HTML代碼+邏輯控制代碼
語法格式: {{var_name}}
>>> python manange.py shell (進入該django項目的環境) >>> from django.template import Context, Template >>> t = Template('My name is {{ name }}.') >>> c = Context({'name': 'Stephane'}) >>> t.render(c) 'My name is Stephane.' # 同一模板,多個上下文,一旦有了模板對象,你就能夠經過它渲染多個context,不管什麼時候咱們均可以 # 像這樣使用同一模板源渲染多個context,只進行 一次模板建立而後屢次調用render()方法渲染會 # 更爲高效: # Low for name in ('John', 'Julie', 'Pat'): t = Template('Hello, {{ name }}') print t.render(Context({'name': name})) # Good t = Template('Hello, {{ name }}') for name in ('John', 'Julie', 'Pat'): print t.render(Context({'name': name}))
Django 模板解析很是快捷。 大部分的解析工做都是在後臺經過對簡短正則表達式一次性調用來完成。 這和基於 XML 的模板引擎造成鮮明對比,那些引擎承擔了 XML 解析器的開銷,且每每比 Django 模板渲染引擎要慢上幾個數量級。
from django.shortcuts import render,HttpResponse from django.template.loader import get_template #記得導入 # Create your views here. import datetime from django.template import Template,Context # def current_time(req): #原始的視圖函數 # now=datetime.datetime.now() # html="<html><body>如今時刻:<h1>%s.</h1></body></html>" %now # return HttpResponse(html) # def current_time(req): #django模板修改的視圖函數 # now=datetime.datetime.now() # t=Template('<html><body>如今時刻是:<h1 style="color:red">{{current_date}}</h1></body></html>') #t=get_template('current_datetime.html') # c=Context({'current_date':now}) # html=t.render(c) # return HttpResponse(html) #另外一種寫法(推薦) def current_time(req): now=datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date':now}) 推薦方式
在到目前爲止的例子中,咱們經過 context 傳遞的簡單參數值主要是字符串,然而,模板系統可以很是簡潔地處理更加複雜的數據結構,例如list、dictionary和自定義的對象。
在 Django 模板中遍歷複雜數據結構的關鍵是句點字符 (.)。
#最好是用幾個例子來講明一下。 # 首先,句點可用於訪問列表索引,例如: >>> from django.template import Template, Context >>> t = Template('Item 2 is {{ items.2 }}.') >>> c = Context({'items': ['apples', 'bananas', 'carrots']}) >>> t.render(c) 'Item 2 is carrots.' #假設你要向模板傳遞一個 Python 字典。 要經過字典鍵訪問該字典的值,可以使用一個句點: >>> from django.template import Template, Context >>> person = {'name': 'Sally', 'age': '43'} >>> t = Template('{{ person.name }} is {{ person.age }} years old.') >>> c = Context({'person': person}) >>> t.render(c) 'Sally is 43 years old.' #一樣,也能夠經過句點來訪問對象的屬性。 比方說, Python 的 datetime.date 對象有 #year 、 month 和 day 幾個屬性,你一樣能夠在模板中使用句點來訪問這些屬性: >>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year >>> d.month >>> d.day >>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.') >>> c = Context({'date': d}) >>> t.render(c) 'The month is 5 and the year is 1993.' # 這個例子使用了一個自定義的類,演示了經過實例變量加一點(dots)來訪問它的屬性,這個方法適 # 用於任意的對象。 >>> from django.template import Template, Context >>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') >>> c = Context({'person': Person('John', 'Smith')}) >>> t.render(c) 'Hello, John Smith.' # 點語法也能夠用來引用對象的方法。 例如,每一個 Python 字符串都有 upper() 和 isdigit() # 方法,你在模板中可使用一樣的句點語法來調用它們: >>> from django.template import Template, Context >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') >>> t.render(Context({'var': 'hello'})) 'hello -- HELLO -- False' >>> t.render(Context({'var': '123'})) '123 -- 123 -- True' # 注意這裏調用方法時並* 沒有* 使用圓括號 並且也沒法給該方法傳遞參數;你只能調用不需參數的 # 方法。
語法格式: {{obj|filter:param}}
# 1 add : 給變量加上相應的值 # # 2 addslashes : 給變量中的引號前加上斜線 # # 3 capfirst : 首字母大寫 # # 4 cut : 從字符串中移除指定的字符 # # 5 date : 格式化日期字符串 # # 6 default : 若是值是False,就替換成設置的默認值,不然就是用原本的值 # # 7 default_if_none: 若是值是None,就替換成設置的默認值,不然就使用原本的值 #實例: #value1="aBcDe" {{ value1|upper }}<br> #value2=5 {{ value2|add:3 }}<br> #value3='he llo wo r ld' {{ value3|cut:' ' }}<br> #import datetime #value4=datetime.datetime.now() {{ value4|date:'Y-m-d' }}<br> #value5=[] {{ value5|default:'空的' }}<br> #value6='<a href="#">跳轉</a>' {{ value6 }} {% autoescape off %} {{ value6 }} {% endautoescape %} {{ value6|safe }}<br> {{ value6|striptags }} #value7='1234' {{ value7|filesizeformat }}<br> {{ value7|first }}<br> {{ value7|length }}<br> {{ value7|slice:":-1" }}<br> #value8='http://www.baidu.com/?a=1&b=3' {{ value8|urlencode }}<br>
語法:{% tags %}
{% if %}標籤計算一個變量值,若是是「true」,即它存在、不爲空而且不是false的boolean值,系統則會顯示{% if %}和{% endif %}間的全部內容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
{% 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 %}和{% endfor %}之間的全部內容
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
<
ul
>
{% for obj in list %}
<
li
>{{ obj.name }}</
li
>
{% endfor %}
</
ul
>
#在標籤裏添加reversed來反序循環列表:
{% for obj in list reversed %}
...
{% endfor %}
#{% for %}標籤能夠嵌套:
{% for country in countries %}
<
h1
>{{ country.name }}</
h1
>
<
ul
>
{% for city in country.city_list %}
<
li
>{{ city }}</
li
>
{% endfor %}
</
ul
>
{% endfor %}
#系統不支持中斷循環,系統也不支持continue語句,{% for %}標籤內置了一個forloop模板變量,
#這個變量含有一些屬性能夠提供給你一些關於循環的信息
1,forloop.counter表示循環的次數,它從1開始計數,第一次循環設爲1:
{% for item in todo_list %}
<
p
>{{ forloop.counter }}: {{ item }}</
p
>
{% endfor %}
2,forloop.counter0 相似於forloop.counter,但它是從0開始計數,第一次循環設爲0
3,forloop.revcounter
4,forloop.revcounter0
5,forloop.first當第一次循環時值爲True,在特別狀況下頗有用:
{% for object in objects %}
{% if forloop.first %}<
li
class="first">{% else %}<
li
>{% endif %}
{{ object }}
</
li
>
{% endfor %}
# 富有魔力的forloop變量只能在循環中獲得,當模板解析器到達{% endfor %}時forloop就消失了
# 若是你的模板context已經包含一個叫forloop的變量,Django會用{% for %}標籤替代它
# Django會在for標籤的塊中覆蓋你定義的forloop變量的值
# 在其餘非循環的地方,你的forloop變量仍然可用
#{% empty %}
{{li }}
{% for i in li %}
<
li
>{{ forloop.counter0 }}----{{ i }}</
li
>
{% empty %}
<
li
>this is empty!</
li
>
{% endfor %}
# [11, 22, 33, 44, 55]
# 0----11
# 1----22
# 2----33
# 3----44
# 4----55
|
用於生成csrf_token的標籤,用於防治跨站攻擊驗證。注意若是你在view的index裏用的是render_to_response方法,不會生效
其實,這裏是會生成一個input標籤,和其餘表單標籤一塊兒提交給後臺的。
<form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
{% verbatim %} {{ hello }} {% endverbatim %}
步驟:
a、在app中建立templatetags模塊(必須的)
b、建立任意 .py 文件,如:my_tags.py
c、在使用自定義simple_tag和filter的html文件中導入以前建立的 my_tags.py :{% load my_tags %}
d、使用simple_tag和filter(如何調用)
e、在settings中的INSTALLED_APPS配置當前app,否則django沒法找到自定義的simple_tag.
------------------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)
1
2
3
4
5
6
7
8
9
10
11
12
|
-------------------------------.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不能夠
{% if num|filter_multi:30 > 100 %} {{ num|filter_multi:30 }} {% endif %}
在講解了模板加載機制以後,咱們再介紹一個利用該機制的內建模板標籤: {% include %} 。該標籤容許在(模板中)包含其它的模板的內容。 標籤的參數是所要包含的模板名稱,能夠是一個變量,也能夠是用單/雙引號硬編碼的字符串。 每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減小重複。
到目前爲止,咱們的模板範例都只是些零星的 HTML 片斷,但在實際應用中,你將用 Django 模板系統來建立整個 HTML 頁面。 這就帶來一個常見的 Web 開發問題: 在整個網站中,如何減小共用頁面區域(好比站點導航)所引發的重複和冗餘代碼?
解決該問題的傳統作法是使用 服務器端的 includes ,你能夠在 HTML 頁面中使用該指令將一個網頁嵌入到另外一箇中。 事實上, Django 經過剛纔講述的 {% include %} 支持了這種方法。 可是用 Django 解決此類問題的首選方法是使用更加優雅的策略—— 模板繼承 。
本質上來講,模板繼承就是先構造一個基礎框架模板,然後在其子模板中對它所包含站點公用部分和定義塊進行重載。
讓咱們經過修改 current_datetime.html 文件,爲 current_datetime 建立一個更加完整的模板來體會一下這種作法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!
DOCTYPE
HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<
html
lang="en">
<
head
>
<
title
>The current time</
title
>
</
head
>
<
body
>
<
h1
>My helpful timestamp site</
h1
>
<
p
>It is now {{ current_date }}.</
p
>
<
hr
>
<
p
>Thanks for visiting my site.</
p
>
</
body
>
</
html
>
|
這看起來很棒,但若是咱們要爲 hours_ahead 視圖建立另外一個模板會發生什麼事情呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!
DOCTYPE
HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<
html
lang="en">
<
head
>
<
title
>Future time</
title
>
</
head
>
<
body
>
<
h1
>My helpful timestamp site</
h1
>
<
p
>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</
p
>
<
hr
>
<
p
>Thanks for visiting my site.</
p
>
</
body
>
</
html
>
|
很明顯,咱們剛纔重複了大量的 HTML 代碼。 想象一下,若是有一個更典型的網站,它有導航條、樣式表,可能還有一些 JavaScript 代碼,事情必將以向每一個模板填充各類冗餘的 HTML 而了結。
解決這個問題的服務器端 include 方案是找出兩個模板中的共同部分,將其保存爲不一樣的模板片斷,而後在每一個模板中進行 include。 也許你會把模板頭部的一些代碼保存爲 header.html 文件:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head>
你可能會把底部保存到文件 footer.html :
<hr> <p>Thanks for visiting my site.</p> </body> </html>
對基於 include 的策略,頭部和底部的包含很簡單。 麻煩的是中間部分。 在此範例中,每一個頁面都有一個<\h1>My helpful timestamp site<\/h1> 標題,可是這個標題不能放在 header.html 中,由於每一個頁面的 <\title> 是不一樣的。 若是咱們將 <\h1> 包含在頭部,咱們就不得不包含 <\title> ,但這樣又不容許在每一個頁面對它進行定製。 何去何從呢?
Django 的模板繼承系統解決了這些問題。 你能夠將其視爲服務器端 include 的逆向思惟版本。 你能夠對那些不一樣 的代碼段進行定義,而不是 共同 代碼段。
第一步是定義 基礎模板,該框架以後將由子模板所繼承。 如下是咱們目前所講述範例的基礎模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!
DOCTYPE
HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<
html
lang="en">
<
head
>
<
title
>{% block title %}{% endblock %}</
title
>
</
head
>
<
body
>
<
h1
>My helpful timestamp site</
h1
>
{% block content %}{% endblock %}
{% block footer %}
<
hr
>
<
p
>Thanks for visiting my site.</
p
>
{% endblock %}
</
body
>
</
html
>
|
這個叫作 base.html 的模板定義了一個簡單的 HTML 框架文檔,咱們將在本站點的全部頁面中使用。 子模板的做用就是重載、添加或保留那些塊的內容。 (若是你一直按順序學習到這裏,保存這個文件到你的template目錄下,命名爲 base.html .)
咱們使用模板標籤: {% block %} 。 全部的 {% block %} 標籤告訴模板引擎,子模板能夠重載這些部分。 每一個{% block %}標籤所要作的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。
如今咱們已經有了一個基本模板,咱們能夠修改 current_datetime.html 模板來 使用它:
1
2
3
4
5
6
7
|
{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<
p
>It is now {{ current_date }}.</
p
>
{% endblock %}
|
再爲 hours_ahead 視圖建立一個模板,看起來是這樣的:
1
2
3
4
5
6
7
|
{% extends "base.html" %}
{% block title %}Future time{% endblock %}
{% block content %}
<
p
>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</
p
>
{% endblock %}
|
看起來很漂亮是否是? 每一個模板只包含對本身而言 獨一無二 的代碼。 無需多餘的部分。 若是想進行站點級的設計修改,僅需修改 base.html ,全部其它模板會當即反映出所做修改。
如下是其工做方式。 在加載 current_datetime.html 模板時,模板引擎發現了 {% extends %} 標籤, 注意到該模板是一個子模板。 模板引擎當即裝載其父模板,即本例中的 base.html 。
此時,模板引擎注意到 base.html 中的三個 {% block %} 標籤,並用子模板的內容替換這些 block 。所以,引擎將會使用咱們在 { block title %} 中定義的標題,對 {% block content %} 也是如此。 因此,網頁標題一塊將由{% block title %}替換,一樣地,網頁的內容一塊將由 {% block content %}替換。
注意因爲子模板並無定義 footer 塊,模板系統將使用在父模板中定義的值。 父模板 {% block %} 標籤中的內容老是被看成一條退路。
繼承並不會影響到模板的上下文。 換句話說,任何處在繼承樹上的模板均可以訪問到你傳到模板中的每個模板變量。
你能夠根據須要使用任意多的繼承次數。 使用繼承的一種常見方式是下面的三層法:
這個方法可最大限度地重用代碼,並使得向公共區域(如區域級的導航)添加內容成爲一件輕鬆的工做。
如下是使用模板繼承的一些訣竅:
django默認支持sqlite,mysql, oracle,postgresql數據庫。
下面是最經常使用的兩種數據庫的引擎名稱
<1> sqlite
django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動
引擎名稱:django.db.backends.sqlite3
<2> mysql
引擎名稱:django.db.backends.mysql
DATABASES = {
'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
DATABASES = {
'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': "blog", "USER":"root", "PASSWORD":"123456", "HOST":"127.0.0.1", "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()
問題解決!
用於實現面向對象編程語言裏不一樣類型系統的數據之間的轉換,換言之,就是用面向對象的方式去操做數據庫的建立表以及增刪改查等操做。
優勢:
好比 咱們查詢User表中的全部字段:新手可能會用select * from auth_user,這樣會由於多了一個匹配動做而影響效率的。
缺點:
author_obj=models.Author.objects.filter(id=2) print(author_obj.query)
下面要開始學習Django ORM語法了,爲了更好的理解,咱們來作一個基本的 書籍/做者/出版商 數據庫結構。 咱們這樣作是由於 這是一個衆所周知的例子,不少SQL有關的書籍也經常使用這個舉例。
實例:咱們來假定下面這些概念,字段和關係
做者模型:一個做者有姓名。
做者詳細模型:把做者的詳情放到詳情表,包含性別,email地址和出生日期,做者詳情模型和做者模型之間是一對一的關係(one-to-one)(相似於每一個人和他的身份證之間的關係),在大多數狀況下咱們沒有必要將他們拆分紅兩張表,這裏只是引出一對一的概念。
出版商模型:出版商有名稱,地址,所在城市,省,國家和網站。
書籍模型:書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many),一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many),也被稱做外鍵。
from django.db import models<br> class Publisher(models.Model): name = models.CharField(max_length=30, verbose_name="名稱") 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() class Meta: verbose_name = '出版商' verbose_name_plural = verbose_name 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
注意:記得在settings裏的INSTALLED_APPS中加入’app01’,而後再同步數據庫。
分析代碼:
每一個模型至關於單個數據庫表(多對多關係例外,會多生成一張關係表),每一個屬性也是這個表中的字段。屬性名就是字段名,它的類型(例如CharField)至關於數據庫的字段類型(例如varchar)。你們能夠留意下其它的類型都和數據庫裏的什麼字段對應。
模型之間的三種關係:一對一,一對多,多對多。
4.模型經常使用的字段類型以及參數
# AutoField # 一個 IntegerField, 添加記錄時它會自動增加. 你一般不須要直接使用這個字段; 若是你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.(參閱 _自動主鍵字段) # BooleanField # A true/false field. admin 用 checkbox 來表示此類字段. # CharField # 字符串字段, 用於較短的字符串. # # 若是要保存大量文本, 使用 TextField.admin 用一個 <input type="text"> 來表示此類字段 (單行輸入). # CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所容許的最大字符數. # # CommaSeparatedIntegerField # 用於存放逗號分隔的整數值. 相似 CharField, 必需要有 maxlength 參數. # DateField # 一個日期字段. 共有下列額外的可選參數: # # Argument 描述 # auto_now 當對象被保存時,自動將該字段的值設置爲當前時間.一般用於表示 "last-modified" 時間戳. # auto_now_add 當對象首次被建立時,自動將該字段的值設置爲當前時間.一般用於表示對象建立時間. # admin 用一個文本框 <input type="text"> 來表示該字段數據(附帶一個 JavaScript 日曆和一個"Today"快鍵. # # DateTimeField # 一個日期時間字段. 相似 DateField 支持一樣的附加選項. # admin 用兩上文本框 <input type="text"> 表示該字段順序(附帶JavaScript shortcuts). # # EmailField # 一個帶有檢查 Email 合法性的 CharField,不接受 maxlength 參數. # 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 須要如下步驟: # # 在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件. (出於性能考慮,這些文件並不保存到數據庫.) 定義 MEDIA_URL 做爲該目錄的公共 URL. 要確保該目錄對 WEB 服務器用戶賬號是可寫的. # 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django 使用 MEDIA_ROOT 的哪一個子目錄保存上傳文件. # 你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT). 出於習慣你必定很想使用 Django 提供的 get_<fieldname>_url 函數.舉例來講,若是你的 ImageField 叫做 mug_shot, 你就能夠在模板中以 {{ object.get_mug_shot_url }} 這樣的方式獲得圖像的絕對路徑. # 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 # # 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">)表示該字段保存的數據. # # ImageField # 相似 FileField, 不過要校驗上傳對象是不是一個合法圖片.它有兩個可選參數:height_field 和 width_field,若是提供這兩個參數,則圖片將按提供的高度和寬度規格保存. # # 該字段要求 Python Imaging Library. # # IntegerField # 用於保存一個整數. # # admin 用一個``<input type="text">``表示該字段保存的數據(一個單行編輯框) # # IPAddressField # 一個字符串形式的 IP 地址, (i.e. "24.124.1.30"). # # admin 用一個``<input type="text">``表示該字段保存的數據(一個單行編輯框) # # NullBooleanField # 相似 BooleanField, 不過容許 NULL 做爲其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項. # # admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes" 和 "No" ) 來表示這種字段數據. # # PhoneNumberField # 一個帶有合法美國風格電話號碼校驗的 CharField``(格式: ``XXX-XXX-XXXX). # PositiveIntegerField # 相似 IntegerField, 但取值範圍爲非負整數(這個字段應該是容許0值的....因此字段名字取得不太好,無符號整數就對了嘛). # PositiveSmallIntegerField # 相似 PositiveIntegerField, 取值範圍較小(數據庫相關) # 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. # # admin 用一個``<input type="text">``表示 SlugField 字段數據(一個單行編輯框) # # SmallIntegerField # 相似 IntegerField, 不過只容許某個取值範圍內的整數.(依賴數據庫) # # TextField # 一個容量很大的文本字段. # # admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框). # # TimeField # A time. Accepts the same auto-population options as DateField 和 DateTimeField. # # admin 用一個 <input type="text"> 文本框表示該字段保存的數據(附加一些JavaScript shortcuts). # # URLField # 用於保存 URL. 若 verify_exists 參數爲 True (默認), 給定的 URL 會預先檢查是否存在(即URL是否被有效裝入且沒有返回404響應). # # admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框) # # USStateField # 一個兩字母的美國州名縮寫. # # admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框) # # XMLField # 一個校驗值是否爲合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema 的文件系統路徑.
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()): 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) #注意: 若是第三張表是經過models.ManyToManyField()自動建立的,那麼綁定關係只有上面一種方式 # 若是第三張表是本身建立的: 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()
>>> Book.objects.filter(id=1).delete() (3, {'app01.Book_authors': 2, 'app01.Book': 1})
咱們表面上刪除了一條信息,實際卻刪除了三條,由於咱們刪除的這本書在Book_authors表中有兩條相關信息,這種刪除方式就是django默認的級聯刪除。
實例:
author=Author.objects.get(id=5) author.name="tenglan" author.save() #或者 Publisher.objects.filter(id=2).update(name="American publisher")
注意:
<1> 第二種方式修改不能用get的緣由是:update是QuerySet對象的方法,get返回的是一個model對象,它沒有update方法,而filter返回的是一個QuerySet對象(filter裏面的條件可能有多個條件符合,好比name=’alvin’,可能有兩個name=’alvin’的行數據)。
<2>在「插入和更新數據」小節中,咱們有提到模型的save()方法,這個方法會更新一行裏的全部列。 而某些狀況下,咱們只須要更新行裏的某幾列。
#---------------- update方法直接設定對應屬性---------------- models.Book.objects.filter(id=3).update(title="PHP") ##sql: ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3) #--------------- save方法會將全部屬性從新設定一遍,效率低----------- obj=models.Book.objects.filter(id=3)[0] obj.title="Python" obj.save() # SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", # "app01_book"."color", "app01_book"."page_num", # "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1; # # UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556, # "publisher_id" = 1 WHERE "app01_book"."id" = 3;
在這個例子裏咱們能夠看到Django的save()方法更新了不只僅是title列的值,還有更新了全部的列。 若title之外的列有可能會被其餘的進程所改動的狀況下,只更改title列顯然是更加明智的。更改某一指定的列,咱們能夠調用結果集(QuerySet)對象的update()方法,與之等同的SQL語句變得更高效,而且不會引發競態條件。
此外,update()方法對於任何結果集(QuerySet)均有效,這意味着你能夠同時更新多條記錄update()方法會返回一個整型數值,表示受影響的記錄條數。
注意,這裏由於update返回的是一個整形,因此無法用query屬性;對於每次建立一個對象,想顯示對應的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', }, } }
查詢API:
# 查詢相關API: # <1>filter(**kwargs): 它包含了與所給篩選條件相匹配的對象 # <2>all(): 查詢全部結果 # <3>get(**kwargs): 返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。 #-----------下面的方法都是對查詢的結果再進行處理:好比 objects.filter.values()-------- # <4>values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列 model的實例化對象,而是一個可迭代的字典序列 # <5>exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象 # <6>order_by(*field): 對查詢結果排序 # <7>reverse(): 對查詢結果反向排序 # <8>distinct(): 從返回結果中剔除重複紀錄 # <9>values_list(*field): 它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列 # <10>count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。 # <11>first(): 返回第一條記錄 # <12>last(): 返回最後一條記錄 # <13>exists(): 若是QuerySet包含數據,就返回True,不然返回False。
補充:
#擴展查詢,有時候DJANGO的查詢API不能方便的設置查詢條件,提供了另外的擴展查詢方法extra: #extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None (1) Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) (2) Blog.objects.extra( select=SortedDict([('a', '%s'), ('b', '%s')]), select_params=('one', 'two')) (3) q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) q = q.extra(order_by = ['-is_recent']) (4) Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
惰性機制:
所謂惰性機制:Publisher.objects.all()或者.filter()等都只是返回了一個QuerySet(查詢結果集對象),它並不會立刻執行sql,而是當調用QuerySet的時候才執行。
QuerySet特色:
#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) # 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()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存 for obj in objs.iterator(): print(obj.name) 固然,使用iterator()方法來防止生成cache,意味着遍歷同一個queryset時會重複執行查詢。因此使用iterator() 的時候要小心,確保你的代碼在操做一個大的queryset時沒有重複執行查詢 總結: queryset的cache是用於減小程序對數據庫的查詢,在一般的使用下會保證只有在須要的時候纔會查詢數據庫。 使用exists()和iterator()方法能夠優化程序對內存的使用。不過,因爲它們並不會生成queryset cache,可能 會形成額外的數據庫查詢。
對象查詢,單表條件查詢,多表條件關聯查詢
#--------------------對象形式的查找-------------------------- # 正向查找 ret1=models.Book.objects.first() print(ret1.title) print(ret1.price) print(ret1.publisher) print(ret1.publisher.name) #由於一對多的關係因此ret1.publisher是一個對象,而不是一個queryset集合 # 反向查找 ret2=models.Publish.objects.last() print(ret2.name) print(ret2.city) #如何拿到與它綁定的Book對象呢? print(ret2.book_set.all()) #ret2.book_set是一個queryset集合 #---------------了不得的雙下劃線(__)之單表條件查詢---------------- # 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") # models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感 # # models.Tb1.objects.filter(id__range=[1, 2]) # 範圍bettwen and # # startswith,istartswith, endswith, iendswith, #----------------了不得的雙下劃線(__)之多表條件關聯查詢--------------- # 正向查找(條件) # 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(publisher__address='北京').values('publisher__name') print(ret5) #[{'publisher__name': '人大出版社'}, {'publisher__name': '人大出版社'}] #正向查找(條件)之多對多 ret6=models.Book.objects.filter(title='Python').values('author__name') print(ret6) ret7=models.Book.objects.filter(author__name="alex").values('title') print(ret7) # 反向查找(條件) #反向查找之一對多: 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'}]
注意:條件查詢即與對象查詢對應,是指在filter,values等方法中的經過__來明確查詢條件。
聚合查詢和分組查詢
<1> aggregate(args,*kwargs):
經過對QuerySet進行計算,返回一個聚合值的字典。aggregate()中每個參數都指定一個包含在字典中的返回值。即在查詢集上生成聚合。
from django.db.models import Avg,Min,Sum,Max 從整個查詢集生成統計值。好比,你想要計算全部在售書的平均價錢。Django的查詢語法提供了一種方式描述全部 圖書的集合。 >>> Book.objects.all().aggregate(Avg('price')) {'price__avg': 34.35} aggregate()子句的參數描述了咱們想要計算的聚合值,在這個例子中,是Book模型中price字段的平均值 aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的 標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。若是你想要爲聚合值指定 一個名稱,能夠向聚合子句提供它: >>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} 若是你也想知道全部圖書價格的最大值和最小值,能夠這樣查詢: >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
<2> annotate(args,*kwargs):
能夠經過計算查詢結果中每個對象所關聯的對象集合,從而得出總計值(也能夠是平均值或總和),即爲查詢集的每一項生成聚合。
查詢alex出的書總價格
Book.objects.filter(authors__name=「alex」).values("title") Book.objects.filter(authors__name=「alex」).aggregate("price")
查詢各個做者出的書的總價格,這裏就涉及到分組了,分組條件是authors__name
Book.objects.values("authors__name").annotate(Sum("price"))
查詢各個出版社最便宜的書價是多少
Book.objects.values("authors__name").annotate(Min("price"))
F查詢和Q查詢
僅僅靠單一的關鍵字參數查詢已經很難知足查詢要求。此時Django爲咱們提供了F和Q查詢:
# F 使用查詢條件的值,專門取對象中某列值的操做 # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) # Q 構建搜索條件 from django.db.models import Q #1 Q對象(django.db.models.Q)能夠對關鍵字參數進行封裝,從而更好地應用多個查詢 q1=models.Book.objects.filter(Q(title__startswith='P')).all() print(q1)#[<Book: Python>, <Book: Perl>] # 二、能夠組合使用&,|操做符,當一個操做符是用於兩個Q的對象,它產生一個新的Q對象。 Q(title__startswith='P') | Q(title__startswith='J') # 三、Q對象能夠用~操做符放在前面表示否認,也可容許否認與不否認形式的組合 Q(title__startswith='P') | ~Q(pub_date__year=2005) # 四、應用範圍: # Each lookup function that takes keyword-arguments (e.g. filter(), # exclude(), get()) can also be passed one or more Q objects as # positional (not-named) arguments. If you provide multiple Q object # arguments to a lookup function, the arguments will be 「AND」ed # together. For example: Book.objects.get( Q(title__startswith='P'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) ) #sql: # SELECT * from polls WHERE question LIKE 'P%' # AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') # import datetime # e=datetime.date(2005,5,6) #2005-05-06 # 五、Q對象能夠與關鍵字參數查詢一塊兒使用,不過必定要把Q對象放在關鍵字參數查詢的前面。 # 正確: Book.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), title__startswith='P') # 錯誤: Book.objects.get( question__startswith='P', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
django admin是django自帶的一個後臺app,提供了後臺的管理功能。
一 認識ModelAdmin
管理界面的定製類,如需擴展特定的model界面需從該類繼承
二 註冊medel類到admin的兩種方式:
三 掌握一些經常使用的設置技巧