python之django
阿新 • • 發佈:2018-12-10
歡迎學習django課程
MVC
- 大部分開發語言中都有MVC框架
- MVC框架的核心思想是:解耦
- 降低各功能模組之間的耦合性,方便變更,更容易重構程式碼,最大程度上實現程式碼的重用
- m表示model,主要用於對資料庫層的封裝
- v表示view,用於向用戶展示結果
- c表示controller,是核心,用於處理請求、獲取資料、返回結果
MVT
- Django是一款python的web開發框架
- 與MVC有所不同,屬於MVT框架
- m表示model,負責與資料庫互動
- v表示view,是核心,負責接收請求、獲取資料、返回結果
- t表示template,負責呈現內容到瀏覽器
簡介
- 通過簡單示例,使用django完成基本流程的開發,學習django的主要的知識點,在後續課程中會逐個知識點進行深入講解
- 以“圖書-英雄”管理為示例
主要知識點介紹
- 環境搭建
- 定義模型
- 使用後臺管理
- 編寫檢視
- 定義模板
建立虛擬環境
- 建立:mkvirtualenv [虛擬環境名稱]
- 刪除:rmvirtualenv [虛擬環境名稱]
- 進入:workon [虛擬環境名稱]
- 退出:deactivate
- 所有的虛擬環境,都位於/home/.virtualenvs目錄下
- 進入虛擬環境前的提示:
- 進入虛擬環境後的提示:
- 檢視當前的所有虛擬環境:workon [兩次tab鍵]
- 檢視虛擬環境中已經安裝的包
pip list
pip freeze
安裝django
- 建議安裝1.8.2版本,這是一個穩定性高、使用廣、文件多的版本
pip install django==1.8.2
- 檢視版本:進入python shell,執行如下程式碼
import django
django.get_version()
- 說明:使用pip install django命令進行安裝時,會自動刪除舊版本,再安裝新版本
建立專案
- 命令django-admin startproject test1
- 進入test1目錄,目錄結構如下圖:
目錄說明
- manage.py:一個命令列工具,可以使你用多種方式對Django專案進行互動
- 內層的目錄:專案的真正的Python包
- _init _.py:一個空檔案,它告訴Python這個目錄應該被看做一個Python包
- settings.py:專案的配置
- urls.py:專案的URL宣告
- wsgi.py:專案與WSGI相容的Web伺服器入口
設計介紹
- 本示例完成“圖書-英雄”資訊的維護,需要儲存兩種資料:圖書、英雄
- 圖書表結構設計:
- 表名:BookInfo
- 圖書名稱:btitle
- 圖書釋出時間:bpub_date
- 英雄表結構設計:
- 表名:HeroInfo
- 英雄姓名:hname
- 英雄性別:hgender
- 英雄簡介:hcontent
- 所屬圖書:hbook
- 圖書-英雄的關係為一對多
資料庫配置
- 在settings.py檔案中,通過DATABASES項進行資料庫設定
- django支援的資料庫包括:sqlite、mysql等主流資料庫
- Django預設使用SQLite資料庫
建立應用
在一個專案中可以建立一到多個應用,每個應用進行一種業務處理 建立應用的命令:
python manage.py startapp booktest
- 應用的目錄結構如下圖
定義模型類
- 有一個數據表,就有一個模型類與之對應
- 開啟models.py檔案,定義模型類
- 引入包from django.db import models
- 模型類繼承自models.Model類
- 說明:不需要定義主鍵列,在生成時會自動新增,並且值為自動增長
- 當輸出物件時,會呼叫物件的str方法
from django.db import models
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
生成資料表
- 啟用模型:編輯settings.py檔案,將booktest應用加入到installed_apps中
- 生成遷移檔案:根據模型類生成sql語句
python manage.py makemigrations
- 遷移檔案被生成到應用的migrations目錄
- 執行遷移:執行sql語句生成資料表
python manage.py migrate
測試資料操作
- 進入python shell,進行簡單的模型API練習
python manage.py shell
- 進入shell後提示如下:
- 引入需要的包:
from booktest.models import BookInfo,HeroInfo
from django.utils import timezone
from datetime import *
- 查詢所有圖書資訊:
BookInfo.objects.all()
- 新建圖書資訊:
b = BookInfo()
b.btitle="射鵰英雄傳"
b.bpub_date=datetime(year=1990,month=1,day=10)
b.save()
- 查詢圖書資訊:
b=BookInfo.objects.get(pk=1)
- 輸出圖書資訊:
b
b.id
b.btitle
- 修改圖書資訊:
b.btitle=u"天龍八部"
b.save()
- 刪除圖書資訊:
b.delete()
關聯物件的操作
- 對於HeroInfo可以按照上面的操作方式進行
- 新增,注意新增關聯物件
h=HeroInfo()
h.htitle=u'郭靖'
h.hgender=True
h.hcontent=u'降龍十八掌'
h.hBook=b
h.save()
- 獲得關聯集合:返回當前book物件的所有hero
b.heroinfo_set.all()
- 有一個HeroInfo存在,必須要有一個BookInfo物件,提供了建立關聯的資料:
h=b.heroinfo_set.create(htitle=u'黃蓉',hgender=False,hcontent=u'打狗棍法')
h
伺服器
- 執行如下命令可以開啟伺服器
python manage.py runserver ip:port
- 可以不寫ip,預設埠為8000
- 這是一個純python編寫的輕量級web伺服器,僅在開發階段使用
- 伺服器成功啟動後,提示如下資訊
- 預設埠是8000,可以修改埠
python manage.py runserver 8080
- 開啟瀏覽器,輸入網址“127.0.0.1:8000”可以開啟預設頁面
- 如果修改檔案不需要重啟伺服器,如果增刪檔案需要重啟伺服器
- 通過ctrl+c停止伺服器
管理操作
- 站點分為“內容釋出”和“公共訪問”兩部分
- “內容釋出”的部分負責新增、修改、刪除內容,開發這些重複的功能是一件單調乏味、缺乏創造力的工作。為此,Django會根據定義的模型類完全自動地生成管理模組
使用django的管理
- 建立一個管理員使用者
python manage.py createsuperuser,按提示輸入使用者名稱、郵箱、密碼
- 啟動伺服器,通過“127.0.0.1:8000/admin”訪問,輸入上面建立的使用者名稱、密碼完成登入
- 進入管理站點,預設可以對groups、users進行管理
管理介面本地化
- 編輯settings.py檔案,設定編碼、時區
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'
向admin註冊booktest的模型
- 開啟booktest/admin.py檔案,註冊模型
from django.contrib import admin
from models import BookInfo
admin.site.register(BookInfo)
- 重新整理管理頁面,可以對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
新增、修改頁屬性
- fields:屬性的先後順序
fields = ['bpub_date', 'btitle']
- fieldsets:屬性分組
fieldsets = [
('basic',{'fields': ['btitle']}),
('more', {'fields': ['bpub_date']}),
]
關聯物件
-
對於HeroInfo模型類,有兩種註冊方式
- 方式一:與BookInfo模型類相同
- 方式二:關聯註冊
-
按照BookInfor的註冊方式完成HeroInfo的註冊
- 接下來實現關聯註冊
from django.contrib import admin
from models import BookInfo,HeroInfo
class HeroInfoInline(admin.StackedInline):
model = HeroInfo
extra = 2
class BookInfoAdmin(admin.ModelAdmin):
inlines = [HeroInfoInline]
admin.site.register(BookInfo, BookInfoAdmin)
- 可以將內嵌的方式改為表格
class HeroInfoInline(admin.TabularInline)
布林值的顯示
- 釋出性別的顯示不是一個直觀的結果,可以使用方法進行封裝
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']
檢視
- 在django中,檢視對WEB請求進行迴應
- 檢視接收reqeust物件作為第一個引數,包含了請求的資訊
- 檢視就是一個Python函式,被定義在views.py中
#coding:utf-8
from django.http import HttpResponse
def index(request):
return HttpResponse("index")
def detail(request,id):
return HttpResponse("detail %s" % id)
- 定義完成檢視後,需要配置urlconf,否則無法處理請求
URLconf
- 在Django中,定義URLconf包括正則表示式、檢視兩部分
- Django使用正則表示式匹配請求的URL,一旦匹配成功,則呼叫應用的檢視
- 注意:只匹配路徑部分,即除去域名、引數後的字串
- 在test1/urls.py插入booktest,使主urlconf連線到booktest.urls模組
url(r'^', include('booktest.urls')),
- 在booktest中的urls.py中新增urlconf
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index),
url(r'^([0-9]+)/$', views.detail),
]
模板
- 模板是html頁面,可以根據檢視中傳遞的資料填充值
- 建立模板的目錄如下圖:
- 修改settings.py檔案,設定TEMPLATES的DIRS值
'DIRS': [os.path.join(BASE_DIR, 'templates')],
- 在模板中訪問檢視傳遞的資料
{{輸出值,可以是變數,也可以是物件.屬性}}
{%執行程式碼段%}
定義index.html模板
<!DOCTYPE html>
<html>
<head>
<title>首頁</title>
</head>
<body>
<h1>圖書列表</h1>
<ul>
{%for book in booklist%}
<li>
<a href="{{book.id}}">
{{book.btitle}}
</a>
</li>
{%endfor%}
</ul>
</body>
</html>
定義detail.html模板
- 在模板中訪問物件成員時,都以屬性的方式訪問,即方法也不能加括號
<!DOCTYPE html>
<html>
<head>
<title>詳細頁</title>
</head>
<body>
<h1>{{book.btitle}}</h1>
<ul>
{%for hero in book.heroinfo_set.all%}
<li>{{hero.hname}}---{{hero.hcontent}}</li>
{%endfor%}
</ul>
</body>
</html>
使用模板
- 編輯views.py檔案,在方法中呼叫模板
from django.http import HttpResponse
from django.template import RequestContext, loader
from models import BookInfo
def index(request):
booklist = BookInfo.objects.all()
template = loader.get_template('booktest/index.html')
context = RequestContext(request, {'booklist': booklist})
return HttpResponse(template.render(context))
def detail(reqeust, id):
book = BookInfo.objects.get(pk=id)
template = loader.get_template('booktest/detail.html')
context = RequestContext(reqeust, {'book': book})
return HttpResponse(template.render(context))
去除模板的硬編碼
- 在index.html模板中,超連結是硬編碼的,此時的請求地址為“127.0.0.1/1/”
<a href="{{book.id}}">
- 看如下情況:將urlconf中詳細頁改為如下,連結就找不到了
url(r'^book/([0-9]+)/$', views.detail),
- 此時的請求地址應該為“127.0.0.1/book/1/”
- 問題總結:如果在模板中地址硬編碼,將來urlconf修改後,地址將失效
- 解決:使用命名的url設定超連結
- 修改test1/urls.py檔案,在include中設定namespace
url(r'^admin/', include(admin.site.urls, namespace='booktest')),
- 修改booktest/urls.py檔案,設定name
url(r'^book/([0-9]+)/$', views.detail, name="detail"),
- 修改index.html模板中的連結
<a href="{%url 'booktest:detail' book.id%}">
Render簡寫
- Django提供了函式Render()簡化檢視呼叫模板、構造上下文
from django.shortcuts import render
from models import BookInfo
def index(reqeust):
booklist = BookInfo.objects.all()
return render(reqeust, 'booktest/index.html', {'booklist': booklist})
def detail(reqeust, id):
book = BookInfo.objects.get(pk=id)
return render(reqeust, 'booktest/detail.html', {'book': book})
總結
- 安裝配置django執行的環境
- 編寫模型,使用簡單API與資料庫互動
- 使用django的後臺管理中維護資料
- 通過檢視接收請求,通過模型獲取資料,展示出來
- 呼叫模板完成展示
作業
- 設計一對多關係的兩個模型類
- 使用django後臺管理
- 通過檢視、urlconf、模板完成資料的展示
- 控制在2個小時之內完成
ORM簡介
- MVC框架中包括一個重要的部分,就是ORM,它實現了資料模型與資料庫的解耦,即資料模型的設計不需要依賴於特定的資料庫,通過簡單的配置就可以輕鬆更換資料庫
- ORM是“物件-關係-對映”的簡稱,主要任務是:
- 根據物件的型別生成表結構
- 將物件、列表的操作,轉換為sql語句
- 將sql查詢到的結果轉換為物件、列表
- 這極大的減輕了開發人員的工作量,不需要面對因資料庫變更而導致的無效勞動
- Django中的模型包含儲存資料的欄位和約束,對應著資料庫中唯一的表
使用MySql資料庫
- 在虛擬環境中安裝mysql包
pip install mysql-python
- 在mysql中建立資料庫
create databases test2 charset=utf8
- 開啟settings.py檔案,修改DATABASES項
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test2',
'USER': '使用者名稱',
'PASSWORD': '密碼',
'HOST': '資料庫伺服器ip,本地可以使用localhost',
'PORT': '埠,預設為3306',
}
}
開發流程
- 在models.py中定義模型類,要求繼承自models.Model
- 把應用加入settings.py檔案的installed_app項
- 生成遷移檔案
- 執行遷移生成表
- 使用模型類進行crud操作
使用資料庫生成模型類
python manage.py inspectdb > booktest/models.py
定義模型
- 在模型中定義屬性,會生成表中的欄位
- django根據屬性的型別確定以下資訊:
- 當前選擇的資料庫支援欄位的型別
- 渲染管理表單時使用的預設html控制元件
- 在管理站點最低限度的驗證
- django會為表增加自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設定某屬性為主鍵列後,則django不會再生成預設的主鍵列
- 屬性命名限制
- 不能是python的保留關鍵字
- 由於django的查詢方式,不允許使用連續的下劃線
定義屬性
- 定義屬性時,需要欄位型別
- 欄位型別被定義在django.db.models.fields目錄下,為了方便使用,被匯入到django.db.models中
- 使用方式
- 匯入from django.db import models
- 通過models.Field建立欄位型別的物件,賦值給屬性
- 對於重要資料都做邏輯刪除,不做物理刪除,實現方法是定義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, 這個欄位在表中必須有唯一值
關係
- 關係的型別包括
- ForeignKey:一對多,將欄位定義在多的端中
- ManyToManyField:多對多,將欄位定義在兩端中
- OneToOneField:一對一,將欄位定義在任意一端中
- 可以維護遞迴的關聯關係,使用'self'指定,詳見“自關聯”
- 用一訪問多:物件.模型類小寫_set
bookinfo.heroinfo_set
- 用一訪問一:物件.模型類小寫
heroinfo.bookinfo
- 訪問id:物件.屬性_id
heroinfo.book_id
元選項
- 在模型類中定義類Meta,用於設定元資訊
- 元資訊db_table:定義資料表名稱,推薦使用小寫字母,資料表的預設名稱
<app_name>_<model_name>
- ordering:物件的預設排序欄位,獲取物件的列表時使用,接收屬性構成的列表
class BookInfo(models.Model):
...
class Meta():
ordering = ['id']
- 字串前加-表示倒序,不加-表示正序
class BookInfo(models.Model):
...
class Meta():
ordering = ['-id']
- 排序會增加資料庫的開銷
示例演示
- 建立test2專案,並建立booktest應用,使用mysql資料庫
- 定義圖書模型
class BookInfo(models.Model):
btitle = models.CharField(max_length=20)
bpub_date = models.DateTimeField()
bread = models.IntegerField(default=0)
bcommet = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False)
- 英雄模型
class HeroInfo(models.Model):
hname = models.CharField(max_length=20)
hgender = models.BooleanField(default=True)
isDelete = models.BooleanField(default=False)
hcontent = models.CharField(max_length=100)
hbook = models.ForeignKey('BookInfo')
- 定義index、detail檢視
- index.html、detail.html模板
- 配置url,能夠完成圖書及英雄的展示
測試資料
- 模型BookInfo的測試資料
insert into booktest_bookinfo(btitle,bpub_date,bread,bcommet,isDelete) values
('射鵰英雄傳','1980-5-1',12,34,0),
('天龍八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飛狐','1987-11-11',58,24,0)
- 模型HeroInfo的測試資料
insert into booktest_heroinfo(hname,hgender,hbook_id,hcontent,isDelete) values
('郭靖',1,1,'降龍十八掌',0),
('黃蓉',0,1,'打狗棍法',0),
('黃藥師',1,1,'彈指神通',0),
('歐陽鋒',1,1,'蛤蟆功',0),
('梅超風',0,1,'九陰白骨爪',0),
('喬峰',1,2,'降龍十八掌',0),
('段譽',1,2,'六脈神劍',0),
('虛竹',1,2,'天山六陽掌',0),
('王語嫣',0,2,'神仙姐姐',0),
('令狐沖',1,3,'獨孤九劍',0),
('任盈盈',0,3,'彈琴',0),
('嶽不群',1,3,'華山劍法',0),
('東方不敗',0,3,'葵花寶典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若蘭',0,4,'黃衣',0),
('程靈素',0,4,'醫術',0),
('袁紫衣',0,4,'六合拳',0)
類的屬性
- objects:是Manager型別的物件,用於與資料庫進行互動
- 當定義模型類時沒有指定管理器,則Django會為模型類提供一個名為objects的管理器
- 支援明確指定模型類的管理器
class BookInfo(models.Model):
...
books = models.Manager()
- 當為模型類指定管理器後,django不再為模型類生成名為objects的預設管理器
管理器Manager
- 管理器是Django的模型進行資料庫的查詢操作的介面,Django應用的每個模型都擁有至少一個管理器
- 自定義管理器類主要用於兩種情況
- 情況一:向管理器類中新增額外的方法:見下面“建立物件”中的方式二
- 情況二:修改管理器返回的原始查詢集:重寫get_queryset()方法
class BookInfoManager(models.Manager):
def get_queryset(self):
return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
class BookInfo(models.Model):
...
books = BookInfoManager()
建立物件
- 當建立物件時,django不會對資料庫進行讀寫操作
- 呼叫save()方法才與資料庫互動,將物件儲存到資料庫中
- 使用關鍵字引數構造模型物件很麻煩,推薦使用下面的兩種之式
- 說明: _init _方法已經在基類models.Model中使用,在自定義模型中無法使用,
- 方式一:在模型類中增加一個類方法
class BookInfo(models.Model):
...
@classmethod
def create(cls, title, pub_date):
book = cls(btitle=title, bpub_date=pub_date)
book.bread=0
book.bcommet=0
book.isDelete = False
return book
引入時間包:from datetime import *
呼叫:book=BookInfo.create("hello",datetime(1980,10,11));
儲存:book.save()
- 方式二:在自定義管理器中新增一個方法
- 在管理器的方法中,可以通過self.model來得到它所屬的模型類
class BookInfoManager(models.Manager):
def create_book(self, title, pub_date):
book = self.model()
book.btitle = title
book.bpub_date = pub_date
book.bread=0
book.bcommet=0
book.isDelete = False
return book
class BookInfo(models.Model):
...
books = BookInfoManager()
呼叫:book=BookInfo.books.create_book("abc",datetime(1980,1,1))
儲存:book.save()
- 在方式二中,可以呼叫self.create()建立並儲存物件,不需要再手動save()
class BookInfoManager(models.Manager):
def create_book(self, title, pub_date):
book = self.create(btitle = title,bpub_date = pub_date,bread=0,bcommet=0,isDelete = False)
return book
class BookInfo(models.Model):
...
books = BookInfoManager()
呼叫:book=Book.books.create_book("abc",datetime(1980,1,1))
檢視:book.pk
例項的屬性
- DoesNotExist:在進行單個查詢時,模型的物件不存在時會引發此異常,結合try/except使用
例項的方法
- str (self):重寫object方法,此方法在將物件轉換成字串時會被呼叫
- save():將模型物件儲存到資料表中
- delete():將模型物件從資料表中刪除
簡介
- 查詢集表示從資料庫中獲取的物件集合
- 查詢集可以含有零個、一個或多個過濾器
- 過濾器基於所給的引數限制查詢的結果
- 從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句
- 接下來主要討論如下知識點
- 查詢集
- 欄位查詢:比較運算子,F物件,Q物件
查詢集
- 在管理器上呼叫過濾器方法會返回查詢集
- 查詢集經過過濾器篩選後返回新的查詢集,因此可以寫成鏈式過濾
- 惰性執行:建立查詢集不會帶來任何資料庫的訪問,直到呼叫資料時,才會訪問資料庫
- 何時對查詢集求值:迭代,序列化,與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
- get():返回單個滿足條件的物件
限制查詢集
- 查詢集返回列表,可以使用下標的方式進行限制,等同於sql中的limit和offset子句
- 注意:不支援負數索引
- 使用下標後返回一個新的查詢集,不會立即執行查詢
- 如果獲取一個物件,直接使用[0],等同於[0:1].get(),但是如果沒有資料,[0]引發IndexError異常,[0:1].get()引發DoesNotExist異常
查詢集的快取
- 每個查詢集都包含一個快取來最小化對資料庫的訪問
- 在新建的查詢集中,快取為空,首次對查詢集求值時,會發生資料庫查詢,django會將查詢的結果存在查詢集的快取中,並返回請求的結果,接下來對查詢集求值將重用快取的結果
- 情況一:這構成了兩個查詢集,無法重用快取,每次查詢都會與資料庫進行一次互動,增加了資料庫的負載
print([e.title for e in Entry.objects.all()])
print([e.title for e in Entry.objects.all()])
- 情況二:兩次迴圈使用同一個查詢集,第二次使用快取中的資料
querylist=Entry.objects.all()
print([e.title for e in querylist])
print([e.title for e in querylist])
- 何時查詢集不會被快取:當只對查詢集的部分進行求值時會檢查快取,但是如果這部分不在快取中,那麼接下來查詢返回的記錄將不會被快取,這意味著使用索引來限制查詢集將不會填充快取,如果這部分資料已經被快取,則直接使用快取中的資料
欄位查詢
- 實現where子名,作為方法filter()、exclude()、get()的引數
- 語法:屬性名稱__比較運算子=值
- 表示兩個下劃線,左側是屬性名稱,右側是比較型別
- 對於外來鍵,使用“屬性名_id”表示外來鍵的原始值
- 轉義:like語句中使用了%與,匹配資料中的%與,在過濾器中直接寫,例如:filter(title__contains="%")=>where title like '%\%%',表示查詢標題中包含%的
比較運算子
- exact:表示判等,大小寫敏感;如果沒有寫“比較運算子”,表示判等
filter(isDelete=False)
- contains:是否包含,大小寫敏感
exclude(btitle__contains='傳')
- startswith、endswith:以value開頭或結尾,大小寫敏感
exclude(btitle__endswith='傳')
- isnull、isnotnull:是否為null
filter(btitle__isnull=False)
- 在前面加個i表示不區分大小寫,如iexact、icontains、istarswith、iendswith
- in:是否包含在範圍內
filter(pk__in=[1, 2, 3, 4, 5])
- gt、gte、lt、lte:大於、大於等於、小於、小於等於
filter(id__gt=3)
- year、month、day、week_day、hour、minute、second:對日期間型別的屬性進行運算
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
- 跨關聯關係的查詢:處理join查詢
- 語法:模型類名<屬性名><比較>
- 注:可以沒有__<比較>部分,表示等於,結果同inner join
- 可返向使用,即在關聯的兩個模型中都可以使用
filter(heroinfo_ _hcontent_ _contains='八')
- 查詢的快捷方式:pk,pk表示primary key,預設的主鍵是id
filter(pk__lt=6)
聚合函式
- 使用aggregate()函式返回聚合函式的值
- 函式:Avg,Count,Max,Min,Sum
from django.db.models import Max
maxDate = list.aggregate(Max('bpub_date'))
- count的一般用法:
count = list.count()
F物件
- 可以使用模型的欄位A與欄位B進行比較,如果A寫在了等號的左邊,則B出現在等號的右邊,需要通過F物件構造
list.filter(bread__gte=F('bcommet'))
- django支援對F()物件使用算數運算
list.filter(bread__gte=F('bcommet') * 2)
- F()物件中還可以寫作“模型類__列名”進行關聯查詢
list.filter(isDelete=F('heroinfo__isDelete'))
- 對於date/time欄位,可與timedelta()進行運算
list.filter(bpub_date__lt=F('bpub_date') + timedelta(days=1))
Q物件
- 過濾器的方法中關鍵字引數查詢,會合併為And進行
- 需要進行or查詢,使用Q()物件
- Q物件(django.db.models.Q)用於封裝一組關鍵字引數,這些關鍵字引數與“比較運算子”中的相同
from django.db.models import Q
list.filter(Q(pk_ _lt=6))
- Q物件可以使用&(and)、|(or)操作符組合起來
- 當操作符應用在兩個Q物件時,會產生一個新的Q物件
list.filter(pk_ _lt=6).filter(bcommet_ _gt=10)
list.filter(Q(pk_ _lt=6) | Q(bcommet_ _gt=10))
- 使用~(not)操作符在Q物件前表示取反
list.filter(~Q(pk__lt=6))
- 可以使用&|~結合括號進行分組,構造做生意複雜的Q物件
- 過濾器函式可以傳遞一個或多個Q物件作為位置引數,如果有多個Q物件,這些引數的邏輯為and
- 過濾器函式可以混合使用Q物件和關鍵字引數,所有引數都將and在一起,Q物件必須位於關鍵字引數的前面
自連線
- 對於地區資訊,屬於一對多關係,使用一張表,儲存所有的資訊
- 類似的表結構還應用於分類資訊,可以實現無限級分類
- 新建模型AreaInfo,生成遷移
class AreaInfo(models.Model):
atitle = models.CharField(max_length=20)
aParent = models.ForeignKey('self', null=True, blank=True)
- 訪問關聯物件
上級物件:area.aParent
下級物件:area.areainfo_set.all()
- 加入測試資料(在workbench中,參見“省市區mysql.txt”)
- 在booktest/views.py中定義檢視area
from models import AreaInfo
def area(request):
area = AreaInfo.objects.get(pk=130100)
return render(request, 'booktest/area.html', {'area': area})
- 定義模板area.html
<!DOCTYPE html>
<html>
<head>
<title>地區</title>
</head>
<body>
當前地區:{{area.atitle}}
<hr/>
上級地區:{{area.aParent.atitle}}
<hr/>
下級地區:
<ul>
{ %for a in area.areainfo_set.all%}
<li>{{a.atitle}}</li>
{ %endfor%}
</ul>
</body>
</html>
- 在booktest/urls.py中配置一個新的urlconf
urlpatterns = [
url(r'^area/$', views.area, name='area')
]
總結
- 使用mysql資料庫
- 定義模型,生成遷移
- 定義管理器
- 查詢
- 自連線
作業
- 熟練完成當天程式碼
- 上次作業中定義的模型,改為使用mysql資料庫操作
- 對模型資料進行查詢
檢視
- 檢視接受Web請求並且返回Web響應
- 檢視就是一個python函式,被定義在views.py中
- 響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤等等
- 響應處理過程如下圖:
URLconf
- 在settings.py檔案中通過ROOT_URLCONF指定根級url的配置
- urlpatterns是一個url()例項的列表
- 一個url()物件包括:
- 正則表示式
- 檢視函式
- 名稱name
- 編寫URLconf的注意:
- 若要從url中捕獲一個值,需要在它周圍設定一對圓括號
- 不需要新增一個前導的反斜槓,如應該寫作'test/',而不應該寫作'/test/'
- 每個正則表示式前面的r表示字串不轉義
- 請求的url被看做是一個普通的python字串,進行匹配時不包括get或post請求的引數及域名
http://www.itcast.cn/python/1/?i=1&p=new,只匹配“/python/1/”部分
- 正則表示式非命名組,通過位置引數傳遞給檢視
url(r'^([0-9]+)/$', views.detail, name='detail'),
- 正則表示式命名組,通過關鍵字引數傳遞給檢視,本例中關鍵字引數為id
url(r'^(?P<id>[0-9]+)/$', views.detail, name='detail'),
- 引數匹配規則:優先使用命名引數,如果沒有命名引數則使用位置引數
- 每個捕獲的引數都作為一個普通的python字串傳遞給檢視
- 效能:urlpatterns中的每個正則表示式在第一次訪問它們時被編譯,這使得系統相當快
包含其它的URLconfs
- 在應用中建立urls.py檔案,定義本應用中的urlconf,再在專案的settings中使用include()
from django.conf.urls import include, url
urlpatterns = [
url(r'^', include('booktest.urls', namespace='booktest')),
]
- 匹配過程:先與主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定義名稱空間,用於反解析
URL的反向解析
- 如果在檢視、模板中使用硬編碼的連結,在urlconf發生改變時,維護是一件非常麻煩的事情
- 解決:在做連結時,通過指向urlconf的名稱,動態生成連結地址
- 檢視:使用django.core.urlresolvers.reverse()函式
- 模板:使用url模板標籤
定義檢視
- 本質就是一個函式
- 檢視的引數
- 一個HttpRequest例項
- 通過正則表示式組獲取的位置引數
- 通過正則表示式組獲得的關鍵字引數
- 在應用目錄下預設有views.py檔案,一般檢視都定義在這個檔案中
- 如果處理功能過多,可以將函式定義到不同的py檔案中
新建views1.py
#coding:utf-8
from django.http import HttpResponse
def index(request):
return HttpResponse("你好")
在urls.py中修改配置
from . import views1
url(r'^$', views1.index, name='index'),
錯誤檢視
- 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
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物件
- 定義在django.http.QueryDict
- request物件的屬性GET、POST都是QueryDict型別的物件
- 與python字典不同,QueryDict型別的物件用來處理同一個鍵帶有多個值的情況
- 方法get():根據鍵獲取值
- 只能獲取鍵的一個值
- 如果一個鍵同時擁有多個值,獲取最後一個值
dict.get('鍵',default)
或簡寫為
dict['鍵']
- 方法getlist():根據鍵獲取值
- 將鍵的值以列表返回,可以獲取一個鍵的多個值
dict.getlist('鍵',default)
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物件由程式設計師建立
- 不呼叫模板,直接返回資料
#coding=utf-8
from django.http import HttpResponse
def index(request):
return HttpResponse('你好')
- 呼叫模板
from django.http import HttpResponse
from django.template import RequestContext, loader
def index(request):
t1 = loader.get_template('polls/index.html')
context = RequestContext(request, {'h1': 'hello'})
return HttpResponse(t1.render(context))
屬性
- 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二選一
- 如果不指定過期時間,則兩個星期後過期
from django.http import HttpResponse
from datetime import *
def index(request):
response = HttpResponse()
if request.COOKIES.has_key('h1'):
response.write('<h1>' + request.COOKIES['h1'] + '</h1>')
response.set_cookie('h1', '你好', 120)
# response.set_cookie('h1', '你好', None, datetime(2016, 10, 31))
return response
- delete_cookie(key):刪除指定的key的Cookie,如果key不存在則什麼也不發生
子類HttpResponseRedirect
- 重定向,伺服器端跳轉
- 建構函式的第一個引數用來指定重定向的地址
在views1.py中
from django.http import HttpResponse,HttpResponseRedirect
def index(request):
return HttpResponseRedirect('js/')
def index2(request,id):
return HttpResponse(id)
在應用的urls.py中增加一個url物件
url(r'^([0-9]+)/$', views1.index2, name='index2'),
- 請求位址列如圖:
- 請求結果的位址列如圖:
- 推薦使用反向解析
from django.core.urlresolvers import reverse
def index(request):
return HttpResponseRedirect(reverse('booktest:index2', args=(1,)))
子類JsonResponse
- 返回json資料,一般用於非同步請求
- _init _(data)
- 幫助使用者建立JSON編碼的響應
- 引數data是字典物件
- JsonResponse的預設Content-Type為application/json
from django.http import JsonResponse
def index2(requeset):
return JsonResponse({'list': 'abc'})
簡寫函式
render
- render(request, template_name[, context])
- 結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的HttpResponse物件
- request:該request用於生成response
- template_name:要使用的模板的完整名稱
- context:新增到模板上下文的一個字典,檢視將在渲染模板之前呼叫它
from django.shortcuts import render
def index(request):
return render(request, 'booktest/index.html', {'h1': 'hello'})
重定向
- redirect(to)
- 為傳遞進來的引數返回HttpResponseRedirect
- to推薦使用反向解析
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
def index(request):
return redirect(reverse('booktest:index2'))
得到物件或返回404
- get_object_or_404(klass, args, *kwargs)
- 通過模型管理器或查詢集呼叫get()方法,如果沒找到物件,不引發模型的DoesNotExist異常,而是引發Http404異常
- klass:獲取物件的模型類、Manager物件或QuerySet物件
- **kwargs:查詢的引數,格式應該可以被get()和filter()接受
- 如果找到多個物件將引發MultipleObjectsReturned異常
from django.shortcuts import *
def detail(request, id):
try:
book = get_object_or_404(BookInfo, pk=id)
except BookInfo.MultipleObjectsReturned:
book = None
return render(request, 'booktest/detail.html', {'book': book})
將settings.py中的DEBUG改為False
將請求地址輸入2和100檢視效果
得到列表或返回404
- get_list_or_404(klass, args, *kwargs)
- klass:獲取列表的一個Model、Manager或QuerySet例項
- **kwargs:查尋的引數,格式應該可以被get()和filter()接受
from django.shortcuts import *
def index(request):
# list = get_list_or_404(BookInfo, pk__lt=1)
list = get_list_or_404(BookInfo, pk__lt=6)
return render(request, 'booktest/index.html', {'list': list})
將settings.py中的DEBUG改為False
狀態保持
- http協議是無狀態的:每次請求都是一次新的請求,不會記得之前通訊的狀態
- 客戶端與伺服器端的一次通訊,就是一次會話
- 實現狀態保持的方式:在客戶端或伺服器端儲存與會話有關的資料
- 儲存方式包括cookie、session,會話一般指session物件
- 使用cookie,所有資料儲存在客戶端,注意不要儲存敏感資訊
- 推薦使用sesison方式,所有資料儲存在伺服器端,在客戶端cookie中儲存session_id
- 狀態保持的目的是在一段時間內跟蹤請求者的狀態,可以實現跨頁面訪問當前請求者的資料
- 注意:不同的請求者之間不會共享這個資料,與請求者一一對應
啟用session
- 使用django-admin startproject建立的專案預設啟用
- 在settings.py檔案中
項INSTALLED_APPS列表中新增:
'django.contrib.sessions',
項MIDDLEWARE_CLASSES列表中新增:
'django.contrib.sessions.middleware.SessionMiddleware',
- 禁用會話:刪除上面指定的兩個值,禁用會話將節省一些效能消耗
使用session
- 啟用會話後,每個HttpRequest物件將具有一個session屬性,它是一個類字典物件
- get(key, default=None):根據鍵獲取會話的值
- clear():清除所有會話
- flush():刪除當前的會話資料並刪除會話的Cookie
- del request.session['member_id']:刪除會話
使用者登入示例
- 操作效果如下圖:
- 在views.py檔案中建立檢視
from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse
def index(request):
uname = request.session.get('uname')
return render(request, 'booktest/index.html', {'uname': uname})
def login(request):
return render(request, 'booktest/login.html')
def login_handle(request):
request.session['uname'] = request.POST['uname']
return redirect(reverse('main:index'))
def logout(