軟件框架
定義
一個軟件是由其中各個軟件模塊組成的栏豺,每一個模塊都有特定的功能,模塊與模塊之間通過相互配合來完成軟件的開發(fā)搅裙。
軟件框架是針對某一類軟件設(shè)計問題而產(chǎn)生的
Django遵循的是MVC思想
MVC框架
MVC的產(chǎn)生理念:分工请毛,讓專門的人去做專門的事情(比如:輸入、處理鼓蜒、輸出)
MVC的核心思想:解耦
MVC是三個模塊的簡稱:
- M:model痹换,模型,和數(shù)據(jù)庫進(jìn)行交互
- V:View都弹,視圖娇豫,產(chǎn)生html頁面
- C:Controller,控制器畅厢,接收請求冯痢,進(jìn)行處理,與M和V進(jìn)行交互框杜,返回應(yīng)答
以通過瀏覽器注冊用戶信息為例:
Django簡介
Django浦楣,發(fā)音為[`d???ɡ??],是用python語言寫的開源web開發(fā)框架咪辱,并遵循MVC設(shè)計振劳。
MVT
Django是遵循MVC的一個Web框架,但是他有自己的一個名字油狂,叫做MVT
快速開發(fā)
和DRY
原則历恐。Do not repeat yourself。不要自己去重復(fù)一些工作
M:Model选调,模型夹供,和數(shù)據(jù)庫進(jìn)行交互,與MVC中的M功能相同
V:View仁堪,視圖哮洽,接收請求,進(jìn)行處理弦聂,與M和T交互鸟辅,返回應(yīng)答,與MVC中的C功能相同
T:template莺葫,模板匪凉,產(chǎn)生html頁面,和MVC終的V功能相同
MVT的各部分功能:
虛擬環(huán)境
在Linux上捺檬,如果用pip3安裝的包再层,會安裝在/user/local/lib/python3.xx/dis-packages下面。當(dāng)安裝同一個包的不同版本的時候堡纬,后安裝的版本會把原來安裝的版本覆蓋掉聂受。
如何解決這個問題?
使用虛擬環(huán)境烤镐,所謂虛擬環(huán)境蛋济,就是真實(shí)python環(huán)境的一個復(fù)制版本。在虛擬環(huán)境中使用的python是復(fù)制的python炮叶,安裝python包也是安裝在復(fù)制的python中
ps:如果是在pycharm里的設(shè)置里面安裝的碗旅,而不是用pip命令安裝的渡处,會默認(rèn)安裝在一個虛擬環(huán)境里面
Linux下安裝虛擬環(huán)境的命令:
sudo pip install virtualenv 安裝虛擬環(huán)境
sudo pip install virtualenvwrapper 安裝虛擬環(huán)境擴(kuò)展包
-
編輯家目錄下面的.bashrc文件,添加如下兩行內(nèi)容
- export WORKON_HOME=$HOME/.virtualenvs
- source /user/local/bin/virtualenvwrapper.sh
使用source .bashrc使其生效
創(chuàng)建python2的虛擬環(huán)境:mkvirtualenv 虛擬環(huán)境名稱
創(chuàng)建python3的虛擬環(huán)境:mkvirtualenv -p python3 虛擬環(huán)境名
進(jìn)入虛擬環(huán)境工作:workon 虛擬環(huán)境名
查看機(jī)器上有多少個虛擬環(huán)境:workon 空格 + 兩個tab鍵
退出虛擬環(huán)境:deactivate
刪除虛擬環(huán)境:rmvirtualenv 虛擬環(huán)境名
在虛擬環(huán)境中的一些命令:
- pip list 查看安裝了的包祟辟,pip freeze也可以
- pip install 包的名字(如果要指定版本號医瘫,再緊跟==版本號),如果使用sudo安裝川尖,仍然是安全到全局
Windows環(huán)境安裝虛擬環(huán)境:
- pip list查看是否已經(jīng)安裝virtualenv
- 如果沒有安裝過:pip install virtualenv登下,安裝成功后,會提示:Success fully installed virtualenv-xxx
- 在一個目錄下創(chuàng)建虛擬環(huán)境:virtualenv 虛擬環(huán)境的名字
- 進(jìn)入虛擬環(huán)境所在目錄的scripts叮喳,查看scripts目錄結(jié)構(gòu),可以看到激活虛擬環(huán)境的命令:activate.bat
- 執(zhí)行activate命令
項(xiàng)目創(chuàng)建
django-admin startproject 項(xiàng)目名字(注意:需要先進(jìn)入虛擬環(huán)境缰贝,Linux和Windows相同)
創(chuàng)建成功后馍悟,目錄結(jié)構(gòu)如下:
其中:
-
__init__.py
說明其所在路徑是一個python的包 - settings.py是項(xiàng)目的配置文件,比如項(xiàng)目要使用哪個數(shù)據(jù)庫剩晴,就在里面配置
- urls.py url:在訪問網(wǎng)站時锣咒,我們輸入的網(wǎng)站就是一個url的地址。urls.py文件是進(jìn)行url路由的配置(當(dāng)我們輸入一個地址后赞弥,他怎么給我們找到對應(yīng)的處理函數(shù)是誰)
- wsgi.py Django是遵循wsgi協(xié)議的框架毅整,wsgi.py就是web服務(wù)器和django交互的一個入口
- manage.py 是我們項(xiàng)目的管理文件,是一個可執(zhí)行文件绽左,通過該文件悼嫉,我們可以管理整個項(xiàng)目
創(chuàng)建django應(yīng)用
一個項(xiàng)目由很多個應(yīng)用組成,每一個應(yīng)用完成一個功能模塊
創(chuàng)建應(yīng)用的命令如下:python manage.py startapp 應(yīng)用名
每創(chuàng)建一個應(yīng)用拼窥,里面就會有如下文件:
其中:
-
__init__.py
說明該應(yīng)用是一個python模塊 - models.py中寫和數(shù)據(jù)庫相關(guān)的內(nèi)容
- views.py 接收請求戏蔑,進(jìn)行處理,與M和T進(jìn)行交互鲁纠,返回應(yīng)答总棵。定義處理函數(shù)(在django中稱視圖函數(shù))
- tests.py寫測試代碼的文件,不怎么需要關(guān)心
- admin.py跟網(wǎng)站的后臺管理相關(guān)的文件
- migrations文件夾:存放由模型類生成的遷移文件
在我們建立應(yīng)用之后改含,必須建立應(yīng)用和項(xiàng)目之間的聯(lián)系情龄,即:對應(yīng)用進(jìn)行注冊:在settings.py中修改installed_apps配置項(xiàng),eg:注冊booktest應(yīng)用
運(yùn)行項(xiàng)目
運(yùn)行一個web項(xiàng)目捍壤,就需要一個web服務(wù)器骤视,而django本身給我們提供了一個web服務(wù)器,使用python manage.py runsevser運(yùn)行該服務(wù)器白群,然后即可根據(jù)提示用瀏覽器訪問
ORM
O:object尚胞,對象
R:relations,關(guān)系
M:mapping帜慢,映射
ORM的作用:建立類和表的對應(yīng)關(guān)系
ORM讓我們能夠通過對類和對象的操作實(shí)現(xiàn)對表的操作笼裳,就不需要再去寫sql語句唯卖。這是元類的一種最經(jīng)典的應(yīng)用
在創(chuàng)建的項(xiàng)目文件夾下的models.py文件中,我們設(shè)計和表對應(yīng)的類躬柬,這個類被稱做模型類
ORM的另一個作用:根據(jù)設(shè)計的類生成數(shù)據(jù)庫中的表
模型(M)
模型類設(shè)計和表生成
模型類設(shè)計
在項(xiàng)目文件夾下的models.py文件中拜轨,比如我們定義一個圖書類
from django.db import models
# Create your models here.
class BookInfo(models.Model):
# 必須繼承了Model類之后,他才是一個模型類
# 模型類的名字就對應(yīng)的表的名字為:應(yīng)用名_小寫的模型類名
# 模型類中的類屬性就對應(yīng)于表中的一個字段(列)
btitle = models.CharField(max_length=20) #書名
#通過models.CharField體現(xiàn)btitle的類型是一個字符串,max_length制定字符串的最大長度
bpub_date = models.DateField() #出版日期
#models.DateField說明其是一個日期類型
#在django中允青,id不需要我們定義橄碾,他會幫我們自動生成
模型類的字段屬性和選項(xiàng)
模型類屬性命名限制
- 不能是python關(guān)鍵字
- 不允許使用連續(xù)的下劃線,不是由django的查詢方式?jīng)Q定的
- 定義屬性時需要指定字段類型颠锉,通過字段類型的參數(shù)指定選擇法牲,語法如下: 屬性名 = models.字段類型(選項(xiàng))
字段類型
使用時需要引入django.db.models包
AutoField 自動增長的IntegerField,通常不用指定琼掠,不指定時django會自動創(chuàng)建屬性名為id的自動增長屬性拒垃,在遷移文件中可以查看到
BooleanField 布爾字段,值為True或False
NullBooleanField 支持Null瓷蛙,True悼瓮,F(xiàn)alse三種值
CharField(max_length=最大長度) 字符串,參數(shù)max_length指定最大字符個數(shù)艰猬,必須指定横堡,不然報錯
TextField 大文本字段,一般超過4k個字
IntegerField 整數(shù)
DecimalField(max_digits=None, decimal_places=None) 十進(jìn)制浮點(diǎn)數(shù)冠桃,第一個參數(shù)表示總位命贴,第二個參數(shù)表示小數(shù)位數(shù)
FloatField 浮點(diǎn)數(shù),參數(shù)同上腊满。注意:float的精確度比decimal小
-
DateField([auto_now=False, auto_now_add=False]) 日期套么,有兩個可選參數(shù):
- auto_now 表示每次保存對象時,自動設(shè)置該字段為當(dāng)前時間碳蛋,用于“最后一次修改”的時間戳胚泌,它總是使用當(dāng)前日期,默認(rèn)為false
- auto_now_add 表示當(dāng)對象第一次被創(chuàng)建時自動設(shè)置當(dāng)前時間肃弟,用于創(chuàng)建的時間戳玷室,它總是使用當(dāng)前日期,默認(rèn)false
- 兩個參數(shù)是相互排斥的笤受,不能同時使用
TimeField 時間參數(shù)同DateField
DateTimeField 日期時間穷缤,參數(shù)同上
FileField 上傳文件字段
ImageField 繼承于FileField,對上傳的內(nèi)容進(jìn)行校驗(yàn)箩兽,確保是有效的圖片
選項(xiàng)
作為Field()中的參數(shù)
通過選項(xiàng)實(shí)現(xiàn)的字段的約束津肛,選項(xiàng)如下:
- default 默認(rèn)值。設(shè)置默認(rèn)值
- primary_key 若為True汗贫,則該字段會成為模型的主鍵字段身坐,默認(rèn)值是false秸脱,一般作為AutoField的選項(xiàng)使用
- unique 唯一性約束,如果為True部蛇,這個字段在表中必須有唯一值摊唇,默認(rèn)值是False
- db_index 若值為True,則在表中會為這個字段創(chuàng)建索引涯鲁,默認(rèn)值為False
- db_column 字段的名稱巷查,如果未指定,則使用屬性的名稱
- null 如果為True抹腿,表示允許為空岛请,默認(rèn)值是False
- blank 如果為True,則該字段允許為空白幢踏,默認(rèn)值是False
更多的可以見官方文檔
對比:null是數(shù)據(jù)庫的概念髓需,blank是后臺管理頁面表單驗(yàn)證范疇的
注意:當(dāng)修改模型類之后,如果添加的選項(xiàng)不影響表的結(jié)構(gòu)房蝉,則不需要重新做遷移,eg:blank和default
模型類生成表
一微渠、 生成遷移文件
遷移文件是根據(jù)模型類生成的搭幻,生成的遷移文件存放在migrations文件夾下
命令:python manage.py makemigrations
二、執(zhí)行遷移生成表
命令:python manage.py migrate
根據(jù)遷移文件生成表
注意:django默認(rèn)使用的數(shù)據(jù)庫是sqlite3逞盆,在項(xiàng)目文件夾下檀蹋,與項(xiàng)目同名的文件夾下的settings.py文件中DATABASES字段處可以看到
當(dāng)文件遷移后,我們就可以在項(xiàng)目文件夾下看到一個名為db.sqlite3的文件云芦,即我們的數(shù)據(jù)庫文件俯逾,可以直接使用相應(yīng)軟件打開
通過模型類操作數(shù)據(jù)表
- 使用python manage.py shell進(jìn)入項(xiàng)目終端
- from booktest.models import BookInfo (from 應(yīng)用名.models import 自己定義的類的名字)
- 創(chuàng)建類的對象:b = BookInfo()
- 增加實(shí)例屬性(注意:這個實(shí)例屬性的名字必須和類屬性的名字一樣):b.btitle = "天龍八部"
- from datetime import date
- b.bpub_date = date(1990,1,1)
- 然后就把這個實(shí)例屬性里面保存的數(shù)據(jù)添加到數(shù)據(jù)庫:b.save(),注意:這個方法是繼承自models.Model
- 用 模型類名.objects查看對應(yīng)表里面的數(shù)據(jù)(注意:是模型類名舅逸,而不是上面的變量名b)桌肴,eg:查看id=1的記錄:b2 = BookInfo.objects.get(id=1),b2是一個BookInfo類的對象琉历。即:把查出來的數(shù)據(jù)是保存在一個對象里面坠七,然后可以用類似:b2.btitle查看btitle對應(yīng)的數(shù)據(jù)
- 更改數(shù)據(jù):b2.bpub_date = date(1990,10,10),即:直接更改查詢的對象的屬性值即可旗笔,然后用b2.save()將保存同步到數(shù)據(jù)庫
- 如何刪除一條記錄:b2.delete()
建立兩張表之間的關(guān)系:外鍵
from django.db import models
# Create your models here.
class BookInfo(models.Model):
# 必須繼承了Model類之后彪置,他才是一個模型類
# 模型類中的類屬性就對應(yīng)于表中的一個字段(列)
btitle = models.CharField(max_length=20) #書名
#通過models.CharField體現(xiàn)btitle的類型是一個字符串,max_length制定字符串的最大長度
bpub_date = models.DateField() #出版日期
#models.DateField說明其是一個日期類型
#在django中,id不需要我們定義蝇恶,他會幫我們自動生成
class HeroInfo(models.Model):
hname = models.CharField(max_length=20) #名字
hgender = models.BooleanField(default=False) #性別拳魁,布爾類型,default制定默認(rèn)值
hcomment = models.CharField(max_length=128) #備注
# 在有一對多的關(guān)系的兩個類中撮弧,需要在“多”的類中定義外鍵潘懊,建立“一”和“多”的關(guān)系
# 關(guān)系屬性對應(yīng)的表的字段名格式:關(guān)系屬性名_id
hbook = models.ForeignKey('BookInfo',on_delete=models.CASCADE) # 建立了圖書類和英雄人物類之間的關(guān)系姚糊,注意:如果兩個類不在一個應(yīng)用里面,則需要寫成 應(yīng)用.類名卦尊,eg:booktest.BookInfo
注意:往“多”類的屬性里面賦值的時候叛拷,相關(guān)聯(lián)屬性其對應(yīng)值必須是與其關(guān)聯(lián)的類的對象,eg:
對該屬性進(jìn)行查看岂却,發(fā)現(xiàn)其是一對象:
查看與“一”相關(guān)聯(lián)的“多”的信息(eg:此處與圖書相關(guān)聯(lián)的英雄的信息)
注意:這里的heroinfo是小寫
查詢表中的所有內(nèi)容:類名.objects.all()忿薇,返回的是一個列表,其中的元素為每一條數(shù)據(jù)的對象
后臺管理
在所創(chuàng)建應(yīng)用的對應(yīng)文件夾下躏哩,有一個admin.py署浩,(eg:本案例中為:/myProject/booktest/admin.py)通過它,我們實(shí)現(xiàn)后臺的管理
步驟:
-
本地化
-
語言和時區(qū)的本地化:修改settings.py文件
在這里插入圖片描述
-
登錄管理頁面需要有一個管理員的賬戶:創(chuàng)建管理員:python manage.py createsuperuser
-
使用:python manage.py runserver啟動服務(wù)器扫尺,然后就可以通過瀏覽器進(jìn)行管理筋栋,127.0.0.1:8000/admin,然后就可以幫助我們管理數(shù)據(jù)庫里面的數(shù)據(jù)正驻。注意:服務(wù)器的默認(rèn)端口是8000弊攘,但是我們也可以在運(yùn)行時直接指定:python manage.py runserver 127.0.0.1:8001
在這里插入圖片描述 -
注冊模型類
在應(yīng)用下的admin.py中注冊模型類,告訴django框架根據(jù)注冊的模型類來生成對應(yīng)表管理頁面
admin文件:
注冊后的管理界面:
- 自定義管理頁面:自定義模型管理類伤靠。模型管理類就是告訴django在生成的管理頁面上顯示哪些內(nèi)容(比如捣域,我們讓其在瀏覽 器中顯示圖書名稱的同時顯示出版時間)
class BookInfoAdmin(admin.ModelAdmin):
# 自定義模型管理類,名字可以隨便取宴合,但是通常是:表的名字+Admin
list_display = ['id','btitle','bpub_date'] # 固定寫法焕梅,列表中寫要在頁面中顯示的內(nèi)容
# 同時,在注冊的時候卦洽,我們需要讓該模型類知道自己的管理類是誰
admin.site.register(BookInfo, BookInfoAdmin) # 注意贞言,該語句不能分兩次寫,否則會報錯
視圖(V)
在django中逐样,通過瀏覽器去請求一個頁面的時候蜗字,是使用視圖函數(shù)來處理這個請求的,視圖函數(shù)處理后脂新,要給瀏覽器返回頁面內(nèi)容
視圖函數(shù)的使用
一挪捕、定義視圖函數(shù)
在應(yīng)用下的views.py中,eg:定義一個名為index的視圖函數(shù)(注意:視圖函數(shù)必須有一個參數(shù):request)
from django.http import HttpResponse
def index(request):
# 視圖函數(shù)必須有一個request參數(shù)争便,它是一個HttpRequest對象
# 當(dāng)用戶輸入http://127.0.0.1:8000/index的時候级零,要給瀏覽器返回一個內(nèi)容
# 視圖的作用,就是在里面會進(jìn)行處理,如果要用到數(shù)據(jù)庫奏纪,就與M交互鉴嗤,如果要產(chǎn)生頁面,就與T交互
# 視圖函數(shù)必須返回一個HttpResponse對象序调,需要從django.http中導(dǎo)入
# 所謂要產(chǎn)生頁面就與T(template)進(jìn)行交互:template就是一段html代碼醉锅,當(dāng)V拿到tempalte后,
# 將里面的部分?jǐn)?shù)據(jù)用從M(數(shù)據(jù)庫)拿來的數(shù)據(jù)進(jìn)行替換发绢,然后返回硬耍,瀏覽器就可以直接解析這段html代碼從而在瀏覽器上顯示對應(yīng)內(nèi)容
return HttpResponse("<h1>hello django!</h1>")
# 我們知道,一個地址對應(yīng)一個處理函數(shù)边酒,那么如何讓django知道哪個地址是對應(yīng)哪個處理函數(shù)经柴?
# 答:進(jìn)行url路由的配置
二、進(jìn)行url的配置
首先我們要在應(yīng)用對應(yīng)的目錄下建立urls.py文件墩朦,然后在其中創(chuàng)建一個列表坯认,如下:
from django.conf.urls import url
from booktest import views
urlpatterns = [
# 通過url函數(shù)設(shè)置url路由的配置項(xiàng)
url(r'^index', views.index) #建立/index和視圖index之間的關(guān)系
]
同時,我們也需要在項(xiàng)目的urls里面添加配置項(xiàng)
from django.contrib import admin
from django.urls import path,include
from django.conf.urls import url
urlpatterns = [
path('admin/', admin.site.urls), # 配置項(xiàng)
url(r'^', include('booktest.urls')) # 將booktest應(yīng)用中的urls文件包含進(jìn)來
# url函數(shù)第一個參數(shù)是一個正則表達(dá)式
]
效果圖如下:
流程分析:
當(dāng)我們在瀏覽器中輸入127.0 .0.1:8000/index的時候氓涣,django拿到的是/index
-
然后就會拿這一部分去查找對應(yīng)的視圖
- 先到項(xiàng)目的urls文件里面去查找牛哺,拿該字符串與urlpatterns中每一項(xiàng)的第一個參數(shù)(正則表達(dá)式)進(jìn)行匹配
- 如果匹配上了(由于我們在第二項(xiàng)只匹配了開頭,那么無論匹配什么都能成功)劳吠,就會執(zhí)行后面的動作荆隘,于是django就拿著字符串到應(yīng)用中去與應(yīng)用中的urls進(jìn)行匹配,但是注意:在整個匹配過程中赴背,/index中,最前面的/不參與正則的匹配晶渠,匹配成功凰荚。(
在進(jìn)行匹配時,會去掉匹配成功的部分褒脯,比如此處便瑟,我們在項(xiàng)目的urls中進(jìn)行匹配時,由于只匹配了個開頭番川,沒有具體的數(shù)據(jù)到涂,所以django還是拿著index去應(yīng)用的urls中匹配,如果我們假設(shè)在項(xiàng)目的urls中匹配到了i颁督,那么django就只會拿著ndex去應(yīng)用中匹配
) - 然后一看對應(yīng)的是一個視圖践啄,django就會去調(diào)用這個視圖,然后獲得返回的數(shù)據(jù)(
"<h1>hello django!</h1>"
) - 于是瀏覽器就可以進(jìn)行顯示
注意:在應(yīng)用的urls文件中進(jìn)行url匹配的時候谆刨,一定要嚴(yán)格匹配開頭結(jié)尾沧奴,避免出現(xiàn)意外
模板(T)
django中的模板不僅僅是個html文件鹊奖,還可以在其中定義變量渐逃,也可以寫類似于編程語言的語句
模板文件的使用
創(chuàng)建模板文件夾(通常是在項(xiàng)目文件夾下創(chuàng)建templates)
-
配置模板目錄(在項(xiàng)目的settings.py文件下)伐谈,如下:
在這里插入圖片描述 -
使用模板文件
- 加載模板文件:去模板目錄下面獲取html文件的內(nèi)容烂完,得到一個模板對象(通常,不同的應(yīng)用會用到不同的模板文件诵棵,所以我們會再在templates下面建立與應(yīng)用名同名的文件夾抠蚣,用以存儲該應(yīng)用用到的模板文件)
- 定義模板上下文:向模板文件傳遞數(shù)據(jù),即:如果模板中使用了變量履澳,需要將變量的數(shù)據(jù)給他
- 模板渲染嘶窄,讀出變量后,將變量的位置替換掉奇昙,即得到一個標(biāo)準(zhǔn)的html文件
eg:
模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板文件</title>
</head>
<body>
<h1>這是一個模板文件</h1>
<p>我在這里使用了模板變量护侮,在模板中使用變量,如果不是在代碼段內(nèi)储耐,就要使用兩個花括號括起來:{{content}}</p>
<ul>模板中的代碼段要寫在兩個%中間:{% for i in list %}
<li>{{i}}</li>
{% endfor %}
<!-- 在模板中羊初,如果使用代碼,有開始就一定有結(jié)束什湘,比如endfor长赞,比如endif-->
</ul>
</body>
</html>
應(yīng)用程序中的views文件
# -*- coding:utf-8 -*-
from django.shortcuts import render
# Create your views here.
from django.http import HttpResponse
from django.template import loader,RequestContext
def index(request):
# # 1. 加載模板文件
# temp = loader.get_template('booktest/index.html') #注意:這里的路徑是相對與templates的
# # 返回值是一個模板對象
#
# # 2. 定義模板上下文:給模板文件傳數(shù)據(jù)。所謂定義模板上下文闽撤,就是創(chuàng)建一個RequestContext對象
# context = RequestContext(request, {}) # RequestContext的第一個對象需要是一個request
# context.push(locals())
# # 要給模板文件傳遞的數(shù)據(jù)就放在第二個參數(shù)里面得哆,其是一個字典,通過鍵-值對的方式傳遞
# # 由于我們這里沒有用到變量哟旗,所以傳空字典即可
#
# # 3. 模板渲染:產(chǎn)生標(biāo)準(zhǔn)的html內(nèi)容
# # 模板對象temp里面有一個render方法贩据,能夠把RequestContext對象中對應(yīng)的位置替換成對應(yīng)的值
# # 然后返回替換后的內(nèi)容:一個標(biāo)準(zhǔn)的html文件
# res_html = temp.render(request=request,context=locals())
# # 4. 返回給瀏覽器
#
# return HttpResponse(res_html)
return render(request,'booktest/index.html',{"content":"hello world", "list": list(range(10))})
# 注意:實(shí)際上以上幾個過程都已經(jīng)被進(jìn)一步封裝到了render中,我們可以直接寫成:render(request, '/booktest/index.html',{})
# 第三個參數(shù)是我們要傳遞給模板文件的數(shù)據(jù)闸餐,但是建議自己手動替換饱亮。如果不傳的話也可以直接不寫
效果圖:
知識補(bǔ)充
- 在配置url的時候,只要對正則表達(dá)式分組舍沙,django就會將該分組作為參數(shù)傳遞給后面的參數(shù)近上,eg:
from django.conf.urls import url
from booktest import views
urlpatterns = [
url(r'^index', views.index),
url(r'^show_books$', views.show_books),
url(r'^show_books/(\d+)$', views.detail),
# 在配置url的時候,只要對正則表達(dá)式分組拂铡,django就會將該分組作為參數(shù)傳遞給后面的參數(shù)壹无,這里就是將匹配到的數(shù)字傳遞給views.detail函數(shù)
# ps:新版django可以使用path來代替url
]
- 模板文件中 for循環(huán)里面的empty項(xiàng),當(dāng)要遍歷的東西為空的時候感帅,就執(zhí)行empy
<h1>{{ book.btitle }}</h1>
英雄信息如下:<br>
<ul>
{% for hero in heros %}
<li>{{hero.hname}}---{{hero.hcomment}}</li>
{% empty %}
<!-- empty:用在for循環(huán)中斗锭,如果遍歷東西為空,就會執(zhí)行這里的東西 -->
<li>沒有英雄信息</li>
{% endfor %}
</ul>
Django中數(shù)據(jù)庫的配置
在django中留瞳,通過方便的配置就可以進(jìn)行數(shù)據(jù)庫的切換拒迅,直接更改項(xiàng)目的settings文件
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
"""使用mysql數(shù)據(jù)庫"""
"""注意:在使用mysql數(shù)據(jù)庫的時候需要使用一個叫MySQLdb的模塊,我們直接安裝pymysql即可"""
"""然后在項(xiàng)目文件夾下的settings文件中加上如下代碼:
import pymysql
pymysql.install_as_MySQLdb()
"""
'ENGINE': 'django.db.backends.mysql', #說明使用mysql數(shù)據(jù)庫
'NAME': 'bj18', #說明要使用的數(shù)據(jù)庫的名字,注意璧微,數(shù)據(jù)庫必須事先手動創(chuàng)建
# 配置用戶名和密碼
'USER': 'root',
'PASSWORD': "",
'HOST': 'localhost', #指定數(shù)據(jù)庫所在的ip地址作箍,因?yàn)橥ǔN覀冇玫臄?shù)據(jù)庫并不是在本地,如果是連本機(jī)前硫,可以直接使用localhost
'PORT': 3306, #配置數(shù)據(jù)庫的端口
}
}
關(guān)于報錯問題
:
-
raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__) django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.
- 解決辦法:找到Python安裝路勁下的Python36-32\Lib\site-packages\django\db\backends\mysql\base.py文件胞得,將文件中的如下代碼注釋`
if version < (1, 3, 3):
raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)
-
注釋后,筆者又遇到了一個報錯:
query = query.decode(errors='replace')
- 解決辦法:更進(jìn)一步修改 operations.py屹电。把其中的
query = query.decode(errors='replace')
修改為query = query.encode(errors='replace')
- 解決辦法:更進(jìn)一步修改 operations.py屹电。把其中的
-
其他報錯:
-
SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0xca in position 2: invalid conti nuation byte
- 解決辦法:在報錯的文件第一行添加:
# -*- coding:utf-8 -*-
- 解決辦法:在報錯的文件第一行添加:
-
重定向
這里說的重定向阶剑,就是當(dāng)一個視圖函數(shù)處理完請求后,繼續(xù)返回到某一頁面
代碼示例:
from datetime import date
from django.http import HttpResponseRedirect
def create(request):
"""新增一本圖書"""
b = BookInfo()
b.btitle = "流行蝴蝶劍"
b.bpub_date = date(2000,11,12)
b.save()
# return HttpResponse('ok')
# 返回應(yīng)答:讓瀏覽器再訪問/index/危号,這就需要用到:from django.http import HttpResponseRedirect
return HttpResponseRedirect(redirect_to='/index')
# HttpResponseRedirect也有簡單的寫法:
# from django.shortcuts import redirect
# return redirect('index')
相應(yīng)html代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>圖書信息</title>
</head>
<body>
<h1>圖書信息如下:</h1>
<a href="/create">新增</a>
<!-- 注意:這里的/create相當(dāng)于127.0.0.1:8000/create-->
<!-- 但是如果不加/,就是和之前的地址進(jìn)行拼接牧愁,比如,如果之前的地址是127.0.0.1:8000/index外莲,那么此時就是127.0.0.1:8000/index/create -->
<ul>
{% for book in books %}
<li>{{ book.btitle }}---{{ book.bpub_date }}</li>
{% empty %}
<li>信息為空</li>
{% endfor %}
</ul>
</body>
</html>
當(dāng)我們點(diǎn)擊添加的時候猪半,本來會跳轉(zhuǎn)到127.0.0.1:8000/create,但是由于其視圖函數(shù)處理后進(jìn)行了重定向偷线,所以我們在瀏覽器就看不到這個跳轉(zhuǎn)的過程
通過模型類查詢表中的數(shù)據(jù)
更改配置使其產(chǎn)生日志文件
在Linux里面讓mysql產(chǎn)生日志文件mysql.log
- 使用
sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
命令打開mysql的配置文件磨确,去除68,69行的注釋 -
sudo service mysql restart
)重啟mysql服務(wù)声邦,就會產(chǎn)生mysql日志文件 - 打開MySQL的日志文件乏奥。/var/log/mysql/mysql.log 是mysql日志文件所在的位置。
- 如果使用
sudo tail -f /var/log/mysql/mysql.log
可以實(shí)時查看其中的日志文件
查詢函數(shù)
通過 模型類.objects屬性可以調(diào)用如下函數(shù)亥曹,實(shí)現(xiàn)對模型類對應(yīng)的數(shù)據(jù)表的查詢邓了,注意,要是模型類媳瞪,而不是它的實(shí)例
類名就相當(dāng)于(注意驶悟,不是)表的名字,模型類.xxx就是在表中查找數(shù)據(jù)
以下格式:(函數(shù)名:功能材失;返回值;說明)
- get:返回表中滿足條件的一條且僅能有一條數(shù)據(jù)硫豆;返回值是一個模型類對象龙巨;參數(shù)中寫查詢條件,如果查詢到多條語句熊响,拋出異常MultipleObjectsReturned旨别,查詢不到語句,拋出DoesNotExist
- all:返回模型類對應(yīng)表格中的所有數(shù)據(jù)汗茄;返回值是QuerySet類型秸弛;查詢集
- filter:返回滿足條件的數(shù)據(jù);返回值是QuerySet類型;參數(shù)寫查詢條件
- exclude:返回不滿足條件的數(shù)據(jù)递览;返回值是QuerySet類型叼屠;參數(shù)寫查詢條件
- order_by:對查詢結(jié)果進(jìn)行排序;返回值是QuerSet類型绞铃;參數(shù)中寫根據(jù)哪些字段進(jìn)行排序镜雨,eg:以id升序排列,參數(shù)就為'id'儿捧,如果以id降序荚坞,'-id'
查詢時使用的條件
條件的格式:模型類的屬性名__條件名=值\
在查詢時,可以傳多個條件菲盾,他們之間是且的關(guān)系
# 判等:exact
BookInfo.objects.get(id=1) #完全的寫法為:BookInfo.objects.get(id__exact=1)
# 模糊查詢
# 包含:contains
BookInfo.objects.filter(btitle__contains='傳') #查詢名字中含有“傳”字的書
# 以xxx結(jié)尾:endswith
# 以xxx開始:starswith
# 空查詢:isnull
BookInfo.objects.filter(btitle__isnull=False) #查詢書名不為空的數(shù)據(jù)
# 范圍查詢:in
# eg:查詢id為1或3或5的圖書
BookInfo.objects.filter(id__in=[1,3,5])
# 比較查詢
# gt:great than,大于 lt:less than颓影,小于 gte:great euqal,大于等于 lte:less than equal懒鉴,小于等于
# eg:查詢編號大于3的圖書
BookInf.objects.filter(id__gt=3)
# 日期查詢
BookInfo.objects.filter(bpub_date__year=19990) #date有一個屬性為year
from datetime import date
BookInfo.objects.filter(bpub_date__gt=date(1980,1,1))
對于QuerySet诡挂,可以直接繼續(xù)調(diào)用上面的方法
eg:把id大于3的圖書信息按閱讀量從大到小排序顯示:BookInfo.objects.filter(id__gt=3).order_by('-bread'),因?yàn)閒ilter返回的是一個QuerySet疗我,所以可以直接繼續(xù)調(diào)用order_by
Q對象
作用:用于查詢時條件之間的邏輯關(guān)系:not and or 咆畏,可以對Q對象進(jìn)行& | -操作,~取反
使用之前需要先導(dǎo)入:from django.db.models import Q
eg:
BookInfo.objects.fitler(Q(id=1) | Q(id=2))
BookInfo.objects.fitler(Q(id=1) | Q(id=2))
F對象
作用:用于類屬性之間的比較
使用之前需要先導(dǎo)入:from django.db.models import F
eg:查詢圖書閱讀量大于評論量
BookInfo.objects.filter(bread__gt=F('bcomment'))
# F對象還可以進(jìn)行算數(shù)的運(yùn)算
# 查詢圖書閱讀量大于兩倍評論量的信息
BookInfo.objects.filter(bread__gt=2F('bcomment')*2)
聚合函數(shù)
作用:對查詢結(jié)果進(jìn)行聚合操作
sum count avg max min
aggregate:調(diào)用這個函數(shù)來使用聚合吴裤。返回值是一個字典
使用之間需要先導(dǎo)入聚合類:from django.db.models import Sum,Count,Max,Min,Avg
案例:
# 查詢所有圖書的數(shù)目
from django.db.models import Count,Sum
BookInfo.objects.all().aggregate(Count('id'))
# 注意:count的參數(shù)不能為*
# 查詢所有圖書閱讀量的總和
BookInfo.objects.aggregate(Sum('bread'))
count函數(shù)旧找,注意,這里說的不是上面的Count
返回值是一個數(shù)
eg:查詢所有圖書的數(shù)目
BookInfo.objects.all().count()
# 同樣的麦牺,如果是對一個表中的所有數(shù)據(jù)進(jìn)行查詢钮蛛,.all()都可以省略
# 即:可以寫成:BookInfo.objects.count()
查詢集
all filter exclude order_by 調(diào)用這些函數(shù)會產(chǎn)生一個查詢集
查詢集特性
- 惰性查詢:只有在實(shí)際使用查詢集中的數(shù)據(jù)的時候才會發(fā)生對數(shù)據(jù)庫的真正查詢,eg:當(dāng)book = BookInfo.objects.all()的時候剖膳,不會產(chǎn)生數(shù)據(jù)的查詢
- 緩存:當(dāng)使用的是同一個查詢集時魏颓,第一次的時候會發(fā)生數(shù)據(jù)庫的查詢,然后把結(jié)果緩存起來吱晒,之后再使用這個查詢集時甸饱,使用的是緩存中的結(jié)果
限制查詢集
可以對一個查詢集進(jìn)行 取下標(biāo)或者切片 操作來限制查詢集的結(jié)果
對一個查詢集進(jìn)行切片操作會產(chǎn)生一個新的查詢集,下標(biāo)不允許為負(fù)
eg:
books = BookInfo.objects.all()
bk2 = books[0:3]
取出查詢集第一條數(shù)據(jù)的兩種方式:
- b[0]:如果不存在仑濒,會拋出IndexError異常
- b[0:1].get():如果不存在叹话,會拋出DoesNotExist異常
exists:判斷一個查詢集中是否有數(shù)據(jù),返回結(jié)果為True或False
用法:查詢集.exists()
模型類之間的關(guān)系
和表一樣墩瞳,模型類之間也有三種關(guān)系
一對多關(guān)系
例:圖書類-英雄類
models.ForeignKey() 定義在多的類中
多對多關(guān)系
例:新聞類-新聞類型類驼壶,一篇新聞可能是體育新聞,還可能是國際新聞喉酌,同樣热凹,體育新聞下也有多篇新聞
models.ManyToManyField() 定義在哪個類中都可以
一對一關(guān)系
例:員工基本信息類-員工詳細(xì)信息類
models.OneToOneField 定義在哪個類中都可以
關(guān)聯(lián)查詢(一對多)
我們把在“多”的類中定義的屬性叫做關(guān)聯(lián)屬性
案例:
# 查詢圖書id為1的圖書關(guān)聯(lián)的所有英雄的信息
b = BookInfo.objects.get(id=1)
b.heroinfo_set.all()
# 查詢id為1的英雄關(guān)聯(lián)的圖書信息
h = HeroInfo.objects.get(id=1)
h.hbook()
通過模型類實(shí)現(xiàn)關(guān)聯(lián)查詢
例:查詢圖書信息泵喘,要求圖書管理的英雄的描述包含“八”
Book.Info.objects.filter(heroinfo__hcoment__contains=8)
heroinfo是對應(yīng)的“多”的類名的小寫,hcomment是“多”的屬性(相當(dāng)于表中的字段)般妙,contains是條件表達(dá)式
最終要拿到哪個表中的數(shù)據(jù)纪铺,就通過哪個表來查
例:查詢圖書信息,要求圖書中的英雄的id大于3
BookInfo.objects.filter(heroinfo__id__gt=3)
例:查詢書名為“天龍八部”的所有英雄
Hero.objects.filter(hbook__bititle="天龍八部“)
注意:如果前面要查詢的類中沒有該關(guān)系屬性股冗,后面的參數(shù)中就必須寫類名霹陡;如果前面類中有該關(guān)系屬性,寫的應(yīng)該是屬性名
插入止状、更新和刪除
調(diào)用一個模型類對象的save方法的時候就可以實(shí)現(xiàn)對模型類對應(yīng)數(shù)據(jù)表的插入和更新
調(diào)用一個模型類對象的delete方法的時候烹棉,就可以實(shí)現(xiàn)對模型類對應(yīng)數(shù)據(jù)的刪除
自關(guān)聯(lián)
自關(guān)聯(lián)是一種特殊的一對多的關(guān)系,只是這些一對多的關(guān)系都在一張表里面
示例:
class AreaInfo(models.Model):
"""地區(qū)模型類"""
atitle = models.CharField(max_length=20)
# 關(guān)系屬性怯疤,代表當(dāng)前地區(qū)的父級地區(qū)
aParent = models.ForeignKey('self', null=True, blank=True,on_delete=models.CASCADE) #代表這個類與他自身有了這種關(guān)聯(lián)
管理器
BookInfo.objects.all() --> objects到底是一個什么東西浆洗?
答:objects是django幫助我們自動生成的管理器對象,通過這個管理器可以實(shí)現(xiàn)對數(shù)據(jù)的查詢
objects是models.Manager類的一個對象集峦。自定義管理器后Django不再幫助我們生成默認(rèn)的objects管理器
示例:
class BookInfo(models.Model):
"""圖書模型類"""
btitle = models.CharField(max_length=20)
bpub_date = models.DateField()
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False) #刪除的時候伏社,執(zhí)行邏輯刪除,而非真正的刪除塔淤,默認(rèn)不刪除
book = models.Manager() #自定義一個管理器對象
此時摘昌,我們就不能夠通過BookInfo.objects.xxx來查詢數(shù)據(jù),而是使用:BookInfo.book.xxx
通常高蜂,我們是自己定義一個類聪黎,繼承models.Manager類,然后讓其作為管理器類
自定義管理器類的作用:
- 通過對方法的重寫备恤,改變查詢的結(jié)果集
代碼示例:不返回邏輯上被刪除了的數(shù)據(jù)
class BookInfoManager(models.Manager):
def all(self):
# 1. 調(diào)用父類的all方法獲取所有數(shù)據(jù)
books = super().all()
books.filter(isDelete=False)
return books
- 添加額外的方法以簡化操作
代碼示例:
class BookInfoManager(models.Manager):
def all(self):
# 1. 調(diào)用父類的all方法獲取所有數(shù)據(jù)
books = super().all()
books.filter(isDelete=False)
return books
# 2. 封裝函數(shù):操作模型類對應(yīng)的數(shù)據(jù)表(增刪查改)
def create_book(self, btitle, bpub_date):
obj = BookInfo()
obj.btitle = btitle
obj.bpub_date = bpub_date
obj.save()
return obj
效果:
注意:在models.Manager里面默認(rèn)給我們封裝好了一個create方法稿饰,不過必須要通過關(guān)鍵字傳參
以上代碼還有 一個問題:即,一旦模型類的名字發(fā)生改變露泊,我們就必須手動更改管理器內(nèi)的代碼喉镰,我們可以通過self.model來獲得模型類的名稱
代碼示例:
class BookInfoManager(models.Manager):
def all(self):
# 1. 調(diào)用父類的all方法獲取所有數(shù)據(jù)
books = super().all()
books.filter(isDelete=False)
return books
# 2. 封裝函數(shù):操作模型類對應(yīng)的數(shù)據(jù)表(增刪查改)
def create_book(self, btitle, bpub_date):
model_class = sel.model #獲得model所在的模型類
obj = model_class() #創(chuàng)建對象
obj.btitle = btitle
obj.bpub_date = bpub_date
obj.save()
return obj
模型管理器類和模型類的關(guān)系
元選項(xiàng)
情景:由于我們生成的表的名字為”應(yīng)用名_小寫的模型類名",那么當(dāng)我們的應(yīng)用名一旦發(fā)生改變惭笑,該模型類對應(yīng)的表名就跟著改變侣姆,但是這個類對應(yīng)的表名已經(jīng)生成了,這就會導(dǎo)致無法訪問原表沉噩,會報錯
如何解決:讓模型類對應(yīng)的表名不依賴于應(yīng)用的名字:通過元選項(xiàng)指定表名铺敌,在模型類里面定義一個元類,通過該類里面的db_table屬性來定義表名
代碼示例:
class BookInfo(models.Model):
"""圖書模型類"""
btitle = models.CharField(max_length=20)
bpub_date = models.DateField()
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
isDelete = models.BooleanField(default=False) #刪除的時候屁擅,執(zhí)行邏輯刪除,而非真正的刪除产弹,默認(rèn)不刪除
objects = BookInfoManager() #自定義一個管理器對象
class Meta:
db_table = "bookinfo" # 指定模型類對應(yīng)的表名為bookinfo
如果僅僅是要建立與表的對應(yīng)關(guān)系(模型類中的名字與鮮美 存表中的名字不一致)派歌,直接這樣即可弯囊;如果我們是要用此法更改名字,需要我們再重新做一下遷移
視圖(V)
視圖的功能:接收請求胶果,進(jìn)行處理匾嘱,與M和T進(jìn)行交互,返回應(yīng)答
返回html內(nèi)容HttpResponse早抠,一可能重定向redirect
視圖函數(shù)的使用
使用
使用
-
定義視圖函數(shù)
- request參數(shù)必須有霎烙。是一個HttpRequest對象,參數(shù)名可以變化蕊连,但不要更改
配置url:建立url與視圖函數(shù)之間的對應(yīng)關(guān)系
url配置過程
- 在項(xiàng)目的urls文件中包含具體應(yīng)用的urls文件悬垃,在具體應(yīng)用的urls文件中寫url和視圖的對應(yīng)關(guān)系
- url配置項(xiàng)是定義在一個叫做urlpatterns的列表中,其中的每一個元素就是一個配置項(xiàng)甘苍,每一個配置項(xiàng)都調(diào)用url函數(shù)(現(xiàn)在已經(jīng)是調(diào)用path函數(shù)了尝蠕,不過url函數(shù)仍然可以)
匹配過程
- 去除域名和后面的參數(shù),剩下/aindex,再把前面的/去掉载庭,剩下aindex
- 拿aindex先到項(xiàng)目的url.py文件中進(jìn)行從上到下的匹配看彼,匹配成功之后執(zhí)行后面對應(yīng)的處理動作,就是把匹配成功的部分a字符去除囚聚,然后拿剩下的部分index到應(yīng)用的urls.py文件中再進(jìn)行從上到下的匹配靖榕。
- 如果匹配成功則調(diào)用相應(yīng)的視圖產(chǎn)生內(nèi)容返回給客戶端。如果匹配失敗則產(chǎn)生404錯誤顽铸。
錯誤視圖
當(dāng)我們訪問一個不存在的頁面時茁计,會看到一個如下的錯誤信息
這通常是為了調(diào)試方便,會將我們網(wǎng)站的地址配置顯示出來跋破,小實(shí)際應(yīng)用中是不可取的簸淀,根據(jù)紅色框內(nèi)的提示信息,我們可以通過更改配置來顯示標(biāo)準(zhǔn)的404頁面
- 找到項(xiàng)目的settings文件
- 更改DEBUG=FALSE # 關(guān)閉debug模式
- 更改ALLOWED_HOSTS=['*'] #表示允許所有ip訪問
- 如果要顯示自定義的頁面毒返,則需要的templates目錄下面自定義一個404.html文件租幕。注意,這個文件不需要我們自己配置拧簸,django會自動調(diào)用劲绪,同時會向該文件傳一個名為request_path的模板變量,表示用戶請求的頁面
404錯誤通常源于:
- url沒有配置
- url配置錯誤
如果是自己的視圖里面出錯盆赤,在瀏覽器端會提示500錯誤贾富,如果我們不想使用默認(rèn)的,也可以自己在templates下面自定義一個名為500的html
通常牺六,我們在開發(fā)過程中颤枪,要打開DEBUG模式
捕獲url參數(shù)
進(jìn)行url匹配時,把所需要的捕獲的部分設(shè)置成一個正則表達(dá)式組淑际,這樣django框架就會自動把匹配成功后相應(yīng)組的內(nèi)容作為參數(shù)傳遞給視圖函數(shù)畏纲。一旦進(jìn)行了捕獲扇住,在視圖函數(shù)中就必須聲明。
- 位置參數(shù)盗胀,直接在正則表達(dá)式中分組即可艘蹋,參數(shù)名可以隨便指定
- 關(guān)鍵字參數(shù):在位置參數(shù)的基礎(chǔ)上給正則表達(dá)式組命名即可。
(?P<組名>正則表達(dá)式)
票灰,關(guān)鍵字參數(shù)中女阀,視圖中參數(shù)名必須和正則表達(dá)式組名一致
視圖函數(shù)中的request參數(shù)
request就是HttpRequest類型的對象,其中包含著瀏覽器請求的一些信息屑迂,就是把WSGI模型中的application函數(shù)中的env進(jìn)行了一層包裝
案例:模擬登錄
先在settings文件中注釋掉'django.middleware.csrf.CsrfViewMiddleware',
浸策,否則會出現(xiàn)403錯誤。也可以from django.views.decorators.csrf import csrf_exempt屈糊,然后用csrf_exempt裝飾對應(yīng)的視圖函數(shù)
應(yīng)用urls配置文件:
from booktest import views
from django.urls import path
urlpatterns = [
path(r'index', views.index),
path(r'login', views.login),
path(r'login_check',views.login_check)
]
將應(yīng)用urls包含到項(xiàng)目urls文件中的代碼:略
views文件代碼:
# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect
# Create your views here.
from datetime import date
from django.http import HttpResponse, HttpResponseRedirect
def index(request):
return HttpResponse("hello world")
def login(request):
return render(request,'booktest/login.html',{})
def login_check(request):
# 1. 獲取提交的用戶名和密碼
# 2. 進(jìn)行登錄的校驗(yàn)
# 3. 返回應(yīng)答
"""
request對象有兩個屬性:他們的是QueryDict類型的對象的榛,和字典很相似,可以通過鍵取出值
request.POST:保存的是post的提交參數(shù)
request.GET:保存的是get方式提交的參數(shù)
QueryDict的用法:
from django.http.request import QueryDict
q = QueryDict('a=1&b=2&c=3') #實(shí)例化了一個QueryDict對象逻锐,他里面存儲了abc三個屬性
q['a'] #就能夠獲得a的值1夫晌,還可以通過q.get('a")獲得
# 同字典一樣,如果是用中括號取昧诱,如果鍵不存在晓淀,會拋異常,如果是get方法取盏档,鍵不存在會返回none凶掰,不會報錯
# get方法也可以默認(rèn)值
#QueryDict和字典的本質(zhì)區(qū)別
#在字典中,一個鍵只能對應(yīng)一個值蜈亩,而QueryDict一個鍵可以對應(yīng)多個值
#eg:
q1 = QueryDict('a=1&a=2&a=3&b=4')
q1['a'] #返回3
q1.get('a') #返回3
q1.getlist('a') #返回一個列表懦窘,其中元素為1、2稚配、3
"""
username = request.POST.get('username') #post提交的數(shù)據(jù)中畅涂,鍵就是表單的name值
password = request.POST.get('password')
#實(shí)際情況下,用戶名和密碼應(yīng)該是到數(shù)據(jù)庫中去查找
#模擬:用戶名為smart道川,密碼為123
if username=="smart" and password == "123":
#正常情況下午衰,我們登錄成功后,會跳轉(zhuǎn)到一個頁面冒萄,這里我們跳轉(zhuǎn)到首頁
return redirect('/index')
else:
#用戶和密碼錯誤就還是跳轉(zhuǎn)到登錄頁面
return redirect('/login')
login.html代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登錄</title>
</head>
<body>
<!--表單的兩種提交方式:-->
<!--1. POST:提交的參數(shù)在請求頭里面臊岸,如果數(shù)據(jù)比較重要,采用POST-->
<!--2.GET:提交的參數(shù)在url中-->
<form method="post" action="/login_check">
<!-- 表單中的action就指定我們提交的地址-->
<!-- method指定表單提交的方式-->
用戶名:<input type="text", name="username"><br>
密碼:<input type="password", name="password"><br>
<input type="submit", value="登錄">
</form>
</body>
</html>
注意:此種方式中尊流,我們采用的是全局刷新如果登錄頁面內(nèi)容過多帅戒,產(chǎn)生的體驗(yàn)相當(dāng)不好,此問題可以結(jié)合ajax采用局部刷新
ajax實(shí)現(xiàn)局部刷新的流程:
注意:后臺返回的數(shù)據(jù)就在function的data里面
ps:css崖技、js逻住、images等靜態(tài)文件施流,在django中,都要放在項(xiàng)目下的static文件夾中鄙信,然后到settings中進(jìn)行配置,如下:
#在最后一行:
STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')] #設(shè)置靜態(tài)文件的保存目錄
通常忿晕,每種靜態(tài)文件都放在一個文件夾下面装诡,eg:js就放在js文件夾下面,css践盼、images類似
ajax請求
在項(xiàng)目文件夾下的static/js下鸦采,有jquery文件
ajax就是異步的javascript,其異步體現(xiàn)在咕幻,當(dāng)發(fā)起了ajax請求之后渔伯,不等待回調(diào)函數(shù)的執(zhí)行,而是繼續(xù)往下執(zhí)行代碼
當(dāng)然肄程,也可以發(fā)起同步的ajax請求锣吼,只需添加:'async':false即可
注意:ajax的請求都是在后臺
test_ajax.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax</title>
<script src="/static/js/jquery-3.4.1.js"></script>
<script>
$(function(){
$('#btnAjax').click(function(){ //當(dāng)點(diǎn)擊該按鈕的時候,發(fā)起ajax請求
$.ajax({
'url':'/ajax_handle',
'type':'get',
// type不寫的話默認(rèn)就是get
'dataType':'json'
}).done(function(data){
//進(jìn)行處理
alert(data.res)
})
})
})
</script>
</head>
<body>
<input type="button" value="ajax請求" id="btnAjax">
<!--一點(diǎn)擊按鈕的時候蓝厌,就發(fā)起一個ajax的請求-->
</body>
</html>
應(yīng)用下的urls文件
from booktest import views
from django.urls import path
urlpatterns = [
path(r'index', views.index),
# path(r'create', views.create), #新增一本圖書
# path(r'areas',views.areas),
path(r'login', views.login),
path(r'login_check',views.login_check),
path(r'test_ajax',views.ajax_test), #顯示ajax頁面
path(r'ajax_handle',views.ajax_handle),
]
應(yīng)用下的views.py
from django.shortcuts import render,redirect
def ajax_test(request):
return render(request,'booktest/test_ajax.html')
from django.http import HttpResponse, JsonResponse
def ajax_handle(request):
# 注意:是return 一個JsonResponse的對象
# 假設(shè)返回的是{'res':1}
return JsonResponse({'res':1})
效果圖:
同時玄叠,如果我們用瀏覽器,查看“網(wǎng)絡(luò)”拓提,會發(fā)現(xiàn)它并沒有請求整個頁面读恃,即:實(shí)現(xiàn)了局部刷新
在使用ajax實(shí)現(xiàn)局部刷新時,我們首先要分析出訪問地址時需要攜帶的參數(shù)以及視圖函數(shù)處理完成之后代态,所返回的json格式
HttpRequest對象講解
屬性
- 下面除非特別說明寺惫,屬性都是只讀的。
- path:一個字符串蹦疑,表示請求的頁面的完整路徑西雀,不包含域名和參數(shù)部分。
- method:一個字符串必尼,表示請求使用的HTTP方法蒋搜,常用值包括:'GET'、'POST'判莉。
- 在瀏覽器中給出地址發(fā)出請求采用get方式豆挽,如超鏈接。
- 在瀏覽器中點(diǎn)擊表單的提交按鈕發(fā)起請求券盅,如果表單的method設(shè)置為post則為post請求帮哈。
- encoding:一個字符串,表示提交的數(shù)據(jù)的編碼方式锰镀。如果為None則表示使用瀏覽器的默認(rèn)設(shè)置娘侍,一般為utf-8咖刃。這個屬性是可寫的,可以通過修改它來修改訪問表單數(shù)據(jù)使用的編碼,接下來對屬性的任何訪問將使用新的encoding值。
- GET:QueryDict類型對象对碌,類似于字典欣福,包含get請求方式的所有參數(shù)。
- POST:QueryDict類型對象,類似于字典,包含post請求方式的所有參數(shù)。
- FILES:一個類似于字典的對象箩帚,包含所有的上傳文件。
- COOKIES:一個標(biāo)準(zhǔn)的Python字典黄痪,包含所有的cookie紧帕,鍵和值都為字符串。
- session:一個既可讀又可寫的類似于字典的對象桅打,表示當(dāng)前的會話是嗜,只有當(dāng)Django 啟用會話的支持時才可用,詳細(xì)內(nèi)容見"狀態(tài)保持"油额。
狀態(tài)保持
http協(xié)議是無狀態(tài)的叠纷,下一次去訪問一個頁面時,并不知道上一次對這個頁面做了什么
cookie
cookie是由服務(wù)器生成潦嘶,保存在瀏覽器端的一小段文本信息
cookie的特點(diǎn):
- 以鍵值對方式進(jìn)行存儲涩嚣。
- 通過瀏覽器訪問一個網(wǎng)站時,會將瀏覽器存儲的跟網(wǎng)站相關(guān)的所有cookie信息發(fā)送給該網(wǎng)站的服務(wù)器掂僵。request.COOKIES
- cookie是基于域名安全的航厚。www.baidu.com www.tudou.com
- cookie是有過期時間的,如果不指定锰蓬,默認(rèn)關(guān)閉瀏覽器之后cookie就會過期幔睬。
設(shè)置cookie:需要一個HttpRespose類的對象,或者是它的子類對象芹扭,通過它的set_cookie方法麻顶,我們可以設(shè)置cookie
瀏覽器發(fā)給服務(wù)器的cookie保存在request對象的COOKIES里面,它是一個標(biāo)準(zhǔn)的字典
HttpResponse有哪些子類舱卡?答:HttpResponseRedirect以及JsonResponse都是
代碼示例:
from datetime import datetime,timedelta
from django.http import HttpResponse, HttpResponseRedirect
def set_cookie(request):
response = HttpResponse('set_cookie')
# 設(shè)置一個cookie信息
response.set_cookie('num',1, expires=datetime.now()+timedelta(days=14))
#第一個參數(shù)就是"鍵",第二個參數(shù)就是"值"辅肾;后面是指定過期時間為14天
#指定過期時間也可以使用max_age,單位為s,但是可以直接寫計算式
response.set_cookie('num2',2, expires=datetime.now()+timedelta(days=14)) #可以設(shè)置多個cookie
return response
# 當(dāng)我們return一個response的時候轮锥,這個cookie會交給瀏覽器保存
def get_cookie(request):
num = request.COOKIES['num']
return HttpResponse(num)
除了以上代碼外矫钓,只需要配置urls文件即可
session
session與cookie最大的區(qū)別是cookie保存在瀏覽器端而session保存在服務(wù)器端
對于敏感、重要的信息,建議要儲在服務(wù)器端新娜,不能存儲在瀏覽器中赵辕,如用戶名、余額概龄、等級还惠、驗(yàn)證碼等信息。
session的特點(diǎn)
- session是以鍵值對進(jìn)行存儲的私杜。
- session依賴于cookie吸重。唯一的標(biāo)識碼保存在sessionid cookie中。
- session也是有過期時間歪今,如果不指定,默認(rèn)兩周就會過期颜矿。
在用django創(chuàng)建的mysql數(shù)據(jù)庫中寄猩,有一張django_session表,專門用來存儲session
代碼示例:
def get_cookie(request):
num = request.COOKIES['num']
return HttpResponse(num)
from django.http import HttpResponse
def set_session(request):
request.session['username'] = 'smart'
request.session['password'] = '123'
return HttpResponse('設(shè)置session')
def get_session(request):
username = request.session['username']
password = request.session['password']
return HttpResponse(username+":"+password)
除此之外骑疆,只需要設(shè)置urls即可
效果圖:數(shù)據(jù)是經(jīng)過base64編碼的
session的其他方法
- 取值:request.session.get('key', defaultValue)
- 清楚所有session:request.session.clear()田篇,在存儲中刪除值的部分,但是該條記錄還在
- 刪除整條數(shù)據(jù):request.session.flush箍铭,刪除后泊柬,該條記錄都已經(jīng)不存在
- 刪除session中的指定鍵及值,在存儲中只刪除某個鍵及對應(yīng)值:del request.session.['key']
- 設(shè)置會話的超時時間诈火,如果沒有指定過期時間兽赁,則默認(rèn)是兩個星期:request.session.set_expiry(value),是設(shè)置sessionid這個cookie的冷守,整數(shù)刀崖,單位為s,即:刪除后拍摇,瀏覽器端的sessionid將過期亮钦,就無法通過sessionid去服務(wù)器端獲取其對應(yīng)值
- 判斷session中是否存在某key:request.session.has_key['key']
模板(T)
模板的功能
產(chǎn)生html頁面,控制頁面上展示的內(nèi)容充活,模板文件不僅僅是一個html文件蜂莉。
模板文件包含兩個部分的內(nèi)容:
- 靜態(tài)內(nèi)容:css、js混卵、html
- 動態(tài)內(nèi)容:用于動態(tài)去產(chǎn)生一些網(wǎng)頁內(nèi)容映穗,通過模板語言來產(chǎn)生
模板文件的加載順序
- 首先去配置的模板目錄下面去找模板文件,由于該配置項(xiàng)是一個列表淮菠,所以我們也可以添加多個男公,查找的時候從前往后查找。
- 去INSTALLED_APPS下面的每個應(yīng)用的templates去找模板文件,前提是應(yīng)用中必須有templates文件夾枢赔。
模板語言
模板文件中的動態(tài)內(nèi)容即由模板語言產(chǎn)生
模板變量
模板變量名是由數(shù)字澄阳,字母,下劃線和點(diǎn)組成的踏拜,不能以下劃線開頭碎赢。
使用模板變量:{{模板變量名}},如果是在{%%}的代碼區(qū)速梗,可以直接使用
模板變量的解析順序:例如:{{ book.btitle }}
- 首先把book當(dāng)成一個字典肮塞,把btitle當(dāng)成鍵名,進(jìn)行取值book['btitle']
- 把book當(dāng)成一個對象姻锁,把btitle當(dāng)成屬性枕赵,進(jìn)行取值book.btitle
- 把book當(dāng)成一個對象,把btitle當(dāng)成對象的方法位隶,進(jìn)行取值book.btitle
例如:{{book.數(shù)字}}
- 首先把book當(dāng)成一個字典拷窜,把0當(dāng)成鍵名,進(jìn)行取值book[0]
- 把book當(dāng)成一個列表涧黄,把0當(dāng)成下標(biāo)篮昧,進(jìn)行取值book[0]
如果解析失敗,不會報錯笋妥,而是用空字符串進(jìn)行替換
使用模板變量時懊昨,.前面的可能是一個字典,可能是一個對象春宣,還可能是一個列表酵颁。
模板標(biāo)簽
{% 代碼段 %}
for循環(huán):
{% for x in 列表 %}
# 列表不為空時執(zhí)行
{% empty %}
# 列表為空時執(zhí)行
{% endfor %}
可以通過{{ forloop.counter }}得到for循環(huán)遍歷到了第幾次。
{% if 條件 %}
{% elif 條件 %}
{% else %}
{% endif %}
關(guān)系比較操作符:> < >= <= == !=
注意:進(jìn)行比較操作時月帝,比較操作符兩邊必須有空格材义。
邏輯運(yùn)算:not and or
過濾器
過濾器用于對模板變量進(jìn)行操作。
過濾器的本質(zhì)是一個函數(shù)嫁赏,可能需要參數(shù)
使用格式:模板變量|過濾器:參數(shù)
常用過濾器:
- date:改變?nèi)掌诘娘@示格式其掂。
- length:求長度。字符串潦蝇,列表.
- default:如果前面的計算結(jié)果為False款熬,則使用給定的默認(rèn)值,否則攘乒,使用原來的
代碼示例:
{% for book in books %}
{% if book.id <= 2 %}
<li class = 'red'>{{ book.btitle }}---{{ book.bpub_date | date:'Y年-m月-d日' }}</li>
<!-- Ymd是date函數(shù)的參數(shù)贤牛,Y表示以4位顯示年,m表示以兩位數(shù)字顯示月则酝,d... -->
{% endif %}
{% endfor %}
個人對過濾器的理解殉簸,用過濾器過濾后的返回值替換原來的變量
更多相關(guān)內(nèi)容見官網(wǎng)
自定義過濾器
- 在應(yīng)用下新建一個包,名為:templatetags,一個字母都不能錯
- 然后在其里面新建一個py文件般卑,文件名隨意武鲁,我們的過濾器就寫在這里面
過濾器的本質(zhì)就是python的函數(shù)
自定義過濾器的參數(shù)至少一個(前面的模板變量),最多兩個
代碼示例:
from django.template import Library
register = Library() #創(chuàng)建一個Library對象
@register.filter #用該對象中的filter裝飾自己定義的函數(shù)蝠检,使其成為一個過濾器
def mod(num): #在使用過濾器的時候沐鼠,前面的部分就會作為第一個參數(shù)自動傳遞給過濾器,如果是需要多個參數(shù)叹谁,則使用方法如下:表達(dá)式或變量 | 過濾器:第二個參數(shù)(過濾器最多兩個參數(shù))
return num % 2 == 0 #過濾掉奇數(shù)
- 在要使用該過濾器的模板文件中的開始位置饲梭,對該過濾器進(jìn)行加載:{% load 自定義的過濾器的名字 %}
模板注釋
注意:模板注釋在瀏覽器上查看網(wǎng)頁源代碼的時候是無法看到的,但是html注釋的內(nèi)容是可以看到的
單行注釋:{# 注釋內(nèi)容 #}
-
多行注釋:
{% comment %}
注釋內(nèi)容
{% endcomment %}模板繼承
模板繼承也是為了重用html頁面內(nèi)容
我們把每個頁面都一樣的內(nèi)容寫在父模板文件中
代碼示例:
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}Base{% endblock title %}</title>
</head>
<body>
<h1>導(dǎo)航欄</h1>
{#頁面不同的地方,需要在父模板中預(yù)留位置析苫,這個位置我們稱為預(yù)留快#}
{#子模板就可以重寫父模板中預(yù)留塊的內(nèi)容#}
{% block block_name %}
父模板的預(yù)留塊中可以寫內(nèi)容监氢,也可以不寫,這里我們直接令預(yù)留塊的名字為block_name
{% endblock block_name %}
<h1>版權(quán)信息</h1>
</body>
</html>
child.html
{% extends 'booktest/base.html' %}
{#注意:路徑是相對于templates的#}
{#繼承了之后藤违,里面就不能再寫其他內(nèi)容#}
{#重寫父模板中的預(yù)留塊#}
{#如果不重寫預(yù)留塊中的內(nèi)容,就直接按照父模板中的內(nèi)容顯示#}
{% block block_name %}
{# 在子模板中纵揍,可以使用{{ block.super }}獲取父模板中預(yù)留塊的內(nèi)容#}
{{ block.super }}<br>
這是子模板中的內(nèi)容
{% endblock block_name %}
html轉(zhuǎn)義
在視圖函數(shù)中對模板傳參時(eg:render中)顿乒,會對以下字符自動轉(zhuǎn)義
小于號< 轉(zhuǎn)換為 <
大于號> 轉(zhuǎn)換為 >
單引號' 轉(zhuǎn)換為 '
雙引號" 轉(zhuǎn)換為 "
與符號& 轉(zhuǎn)換為 &
如何關(guān)閉轉(zhuǎn)義:
法一:使用safe過濾器(不需要第二個參數(shù))
法二:使用autoescape標(biāo)簽,off表示關(guān)閉轉(zhuǎn)義泽谨,on表示打開轉(zhuǎn)義璧榄。用法如下:
{% autoescape off %}
模板語言代碼
{% endautoescape %}
safe和autoescape的區(qū)別:safe是對一個變量起作用,而autoescape是對其內(nèi)部代碼段中的所有變量都關(guān)閉轉(zhuǎn)義
模板硬編碼中的字符串默認(rèn)不會經(jīng)過轉(zhuǎn)義
何為模板硬編碼中的字符串吧雹?即骨杂,寫“死”了的部分,示例如下:
{{ test | default:'<h1>hello</h1>' }}
其中雄卷,默認(rèn)的參數(shù)就不會被轉(zhuǎn)義
要對硬編碼進(jìn)行轉(zhuǎn)義搓蚪,必須手動執(zhí)行,eg:將<
手動寫成<
scrf攻擊
即:跨站請求的偽造
登錄裝飾器
情景示例:
在進(jìn)行網(wǎng)站開發(fā)的時候丁鹉,有些頁面是用戶登錄后才能訪問的妒潭,假如用戶訪問了這個地址,需要進(jìn)行登錄的判斷揣钦,如果用戶登錄的話雳灾,可以進(jìn)行后續(xù)的操作,如果沒有登錄冯凹,跳轉(zhuǎn)到登錄頁谎亩。
我們就可以把這個登錄驗(yàn)證加在對應(yīng)頁面的視圖函數(shù)中
但是如果頁面過多,每個都要自己寫,就很麻煩匈庭,我們可以將其放在一個裝飾器里面
csrf偽造
上圖說明:
當(dāng)我們訪問正常網(wǎng)站時夫凸,一切正常,在我們的電腦上保存有sessionid嚎花,假設(shè)我們更改了在該網(wǎng)站的密碼(假設(shè)之后沒有退出寸痢,即瀏覽器一直保存有該sessionid);而當(dāng)我們訪問另一個網(wǎng)站時紊选,假設(shè)我們點(diǎn)擊了其上面的某按鈕或圖片時啼止,該網(wǎng)站向第一個網(wǎng)站發(fā)送了一個請求,該網(wǎng)站就能夠偽造我們的身份更改我們在第一個網(wǎng)站的密碼
csrf偽造成功的兩個關(guān)鍵點(diǎn):
- 登錄正常網(wǎng)站之后兵罢,你的瀏覽器保存有sessionid献烦,且你沒有退出
- 你不小心訪問了另外一個網(wǎng)站,并且你點(diǎn)擊了頁面上的按鈕
這即所謂的跨站請求偽造
Django默認(rèn)啟用了csfr防護(hù)卖词,即settings中的MIDDLEWARE_CLASSES中的django.middleware.csfr.CsrfViewMiddleware'項(xiàng)巩那。該防護(hù)只針對post提交。所以重要的數(shù)據(jù)用post提交
但是我們會發(fā)現(xiàn)打開之后此蜈,當(dāng)我們直接訪問/change_pwd后即横,訪問/change_pwd_action也會失敗(403)裆赵。此時东囚,就需要我們在post提交數(shù)據(jù)時加上{% csrf_token %}標(biāo)簽,eg战授,在表單提交post數(shù)據(jù)時页藻,在表單內(nèi)加上這個標(biāo)簽:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登錄</title>
</head>
<body>
<form method="post" action="/login_check">
{% csrf_token %}
用戶名:<input type="text", name="username" value={{username}}><br>
密碼:<input type="password", name="password" value={{password}}><br>
<input type="checkbox" name="remember">記住用戶名<br>
<input type="submit", value="登錄">
</form>
</body>
</html>
防護(hù)的原理
- 渲染模板文件時我們在模板中寫的{% csrf_token %}標(biāo)簽會被替換成一個名字叫做csrfmiddlewaretoken的隱藏域。
- 服務(wù)器交給瀏覽器保存一個名字為csrftoken的cookie信息植兰。
- 提交表單時份帐,兩個值都會發(fā)給服務(wù)器,服務(wù)器進(jìn)行比對楣导,如果一樣废境,則csrf驗(yàn)證通過,否則失敗筒繁。
- 這種情況下彬坏,當(dāng)我們上方所說的另一個網(wǎng)站想訪問第一個時,由于它沒有該csrfmiddlewaretoken的隱藏域(因?yàn)椤傲硪粋€網(wǎng)站”是無法查看我們電腦本地的網(wǎng)頁源代碼的膝晾,也就拿不到該隱藏域)栓始,就會請求失敗,報403
驗(yàn)證碼
在用戶注冊血当、登錄頁面幻赚,為了防止暴力請求(不斷嘗試密碼)禀忆,可以加入驗(yàn)證碼功能,如果驗(yàn)證碼錯誤落恼,則不需要繼續(xù)處理箩退,可以減輕業(yè)務(wù)服務(wù)器、數(shù)據(jù)庫服務(wù)器的壓力佳谦。(只是因?yàn)槌绦蜃R別驗(yàn)證碼中的圖片的難度比較大戴涝,所以降低了暴力請求的概率,而不是絕對防止)
可以用Pillow這個包產(chǎn)生驗(yàn)證碼
from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO
...
def verify_code(request):
#引入隨機(jī)函數(shù)模塊
import random
#定義變量钻蔑,用于畫面的背景色啥刻、寬、高
bgcolor = (random.randrange(20, 100), random.randrange(
20, 100), 255) #用rgb方式定義顏色
width = 100
height = 25
#創(chuàng)建畫面對象咪笑,并設(shè)置寬高
im = Image.new('RGB', (width, height), bgcolor)
#創(chuàng)建畫筆對象
draw = ImageDraw.Draw(im)
#調(diào)用畫筆的point()函數(shù)繪制噪點(diǎn)
for i in range(0, 100): #循環(huán)遍歷100次可帽,在畫面上添加噪點(diǎn)
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill=fill) #指定在哪個點(diǎn)畫,畫的顏色是什么
#定義驗(yàn)證碼的備選值
str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
#隨機(jī)選取4個值作為驗(yàn)證碼
rand_str = ''
for i in range(0, 4):
rand_str += str1[random.randrange(0, len(str1))]
#構(gòu)造字體對象窗怒,ubuntu的字體路徑為“/usr/share/fonts/truetype/freefont”
font = ImageFont.truetype('FreeMono.ttf', 23)
#構(gòu)造字體顏色
fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
#繪制4個字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
#釋放畫筆
del draw
#存入session映跟,用于做進(jìn)一步驗(yàn)證(對比用戶輸入的驗(yàn)證碼對不對)
request.session['verifycode'] = rand_str
#內(nèi)存文件操作
buf = BytesIO()
#將圖片(im)保存在內(nèi)存中,文件類型為png
im.save(buf, 'png')
#將內(nèi)存中的圖片數(shù)據(jù)返回給客戶端扬虚,MIME類型為圖片png
return HttpResponse(buf.getvalue(), 'image/png')
效果圖:
注意:如果報
OSError: cannot open resource
錯誤努隙,通常是view中使用的字體不存在,如果是windows辜昵,直接采用Windows/Fonts下面存在的字體即可
校驗(yàn)用戶輸入的驗(yàn)證碼時荸镊,從session中取出正確值,再和用戶的輸入值相對比即可
url反向解析
比如路鹰,當(dāng)我們使用超鏈接在多個頁面間跳轉(zhuǎn)的時候,需要指呆跳轉(zhuǎn)到何處收厨,如果我們直接寫“死”了晋柱,當(dāng)頁面的地址發(fā)生變化時,我們就必須手動更改诵叁,非常麻煩雁竞。
這種情況下,我們沒有必要把它寫“死”拧额,可以在鏈接處動態(tài)獲取碑诉。
用法:
-
在項(xiàng)目的urls文件中進(jìn)行include時,指定namespace
在這里插入圖片描述 - 在應(yīng)用的urls文件中配置url時侥锦,指定name
- 在模板文件中使用該地址時进栽,格式如下:
- {% url 'namespace名字:name' %} 例如{% url 'booktest:fan2'%}
- 帶位置參數(shù)(通過url提交的參數(shù)):
{% url 'namespace名字:name' 參數(shù)1 參數(shù)2 %} 例如{% url 'booktest:fan2' 1%},這里的參數(shù)在通過urls查找視圖函數(shù)的時候恭垦,會自動拼接成(/xxx)的形式快毛,然后傳遞給對應(yīng)的視圖函數(shù) - 帶關(guān)鍵字參數(shù)(通過url提交的參數(shù)格嗅,即,給組指定了名字):
{% url 'namespace名字:name' 關(guān)鍵字參數(shù) %} 例如{% url 'booktest:fan2' id=1 %}
eg:
<a href="{{ url 'booktest:index'}}">首頁</a>
<!-- 其中唠帝,booktest是在項(xiàng)目urls文件中include時指定的namespace的名字屯掖,index是在應(yīng)用url中指定的name -->
在重定向的時候使用反向解析
from django.core.urlresolvers import reverse
重定向示例:
from django.core.urlresolvers import reverse
def test_redirect(request):
url = reverse("booktest:index")
# namespace:name
# 如果有位置參數(shù):url = reverse('booktest:show_args",args=(1,2)) #將參數(shù)以元組的形式傳遞過去
# 關(guān)鍵字參數(shù):url = reverse('booktest:show_kwargs',kwargs={c':3,'d':4}),則在調(diào)用對應(yīng)的視圖函數(shù)的時候,會以關(guān)鍵字的形式進(jìn)行傳參襟衰,傳遞給對應(yīng)的視圖函數(shù)
return redirect(url) #導(dǎo)入redirect的語句省略
其他技術(shù)
靜態(tài)文件
使用
在網(wǎng)頁使用的css文件贴铜,js文件和圖片叫做靜態(tài)文件
- 在項(xiàng)目中新建文件夾static,通常瀑晒,每一個類別的文件都新建一個文件夾來保存
在這里插入圖片描述'/abc/images/xxx'
竹宋,而我們的所有靜態(tài)文件還是在項(xiàng)目的static文件夾下存儲
在這里插入圖片描述<img src = '/static/images/mm.jpg'
如何在模板文件(html)中動態(tài)獲取STATIC_URL?
示例:
<!DOCTYPE html>
{% load staticfiles %} }
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板文件</title>
</head>
<body>
<img src="{% static 'images/mm.jpg' %}">
{# 完成staticurl和路徑的拼接#}
{# static就對應(yīng)這staticurl的配置#}
</body>
</html>
中間件
中間件函數(shù)是django框架給我們預(yù)留的函數(shù)接口蜈七,讓我們可以干預(yù)請求和應(yīng)答的過程秒拔。這個函數(shù)的名字和參數(shù)都已經(jīng)固定好了,必須按照規(guī)定的格式寫
eg:阻止某ip訪問自己的網(wǎng)站
獲取瀏覽器端的ip地址
使用request對象的META屬性:request.META.['REMOTE_ADDR']
阻止某ip訪問本網(wǎng)站的所有頁面的方法一
利用裝飾器飒硅,當(dāng)發(fā)現(xiàn)請求的ip是某個我們想阻止的ip時砂缩,進(jìn)行另外的處理
阻止某ip訪問本網(wǎng)站的所有頁面的方法二
在應(yīng)用下新建一個py文件,名字可以變三娩,但是我們通常叫做middleware
-
在里面定義process_view函數(shù)庵芭,注意,名字是固定的雀监,在視圖函數(shù)調(diào)用之前双吆,會先調(diào)用此函數(shù)。其參數(shù)為request, view_func,*views_args, **view_kwargs)会前。這個函數(shù)需要放到一個類里面
- request:和視圖的request一樣
- view_func:要調(diào)用的視圖函數(shù)
- view_args好乐、*view_kwargs:視圖函數(shù)的位置參數(shù)和關(guān)鍵字參數(shù)
代碼示例(middleware.py):
from django.http import HttpResponse
class BolckedIPSMiddleware(object):
# 中間件類
EXCLUDE_IPS = ['192.168.14.74']
def process_view(self,request, view_func, *view_args, **view_kwargs):
# 中間件函數(shù)
user_ip = request.META['REMOTE_ADDR']
if user_ip in BolckedIPSMiddleware.EXCLUDE_IPS:
return HttpResponse('forbidden')
-
在項(xiàng)目的settings下 的MIDDLEWARE中對該我們自己定義的中間件類進(jìn)行注冊,eg:
在這里插入圖片描述
詳解
中間件類的名字可以自己韧咭恕(常以MIDDLECLASS)結(jié)尾蔚万,但是里面的中間件函數(shù)名稱是固定的
常用中間件預(yù)留函數(shù): 名字和參數(shù)都是固定的
-
__init__
:服務(wù)器重啟之后,響應(yīng)第一個請求的時候調(diào)用临庇。 - process_request:是在產(chǎn)生request對象笛坦,進(jìn)行url匹配之前調(diào)用区转。必須有一個request參數(shù),即圖中的request對象
- process_view:是url匹配之后版扩,調(diào)用視圖函數(shù)之前废离。參數(shù)為request, view_func, *view_args, **view_kwargs。分別為request對象礁芦、接下來要調(diào)用的視圖函數(shù)的 名字 蜻韭、視圖函數(shù)所需要用到的位置參數(shù)和關(guān)鍵字參數(shù)
- process_response:視圖函數(shù)調(diào)用之后,內(nèi)容返回給瀏覽器之前柿扣。必須有request和response參數(shù)肖方,分別為request對象是視圖函數(shù)的返回值
- process_exception:視圖函數(shù)出現(xiàn)異常,會調(diào)用這個函數(shù)未状。參數(shù)為request和exception俯画。分別為request對象和異常對象
- 如果注冊的多個中間件類中包含process_exception函數(shù)的時候,調(diào)用的順序跟注冊的順序是相反的司草。
在類中艰垂,只定義需要用到的即可,不必全部定義
中間件函數(shù)的執(zhí)行流程: 其中的紅色箭頭部分表示人為的干預(yù)埋虹,讓其提前返回
以下部分待改
上傳圖片
配置上傳文件保存目錄
需要先創(chuàng)建一個目錄猜憎,配置說明上傳文件就保存在該目錄下方
示例:
新建目錄:(目錄的位置和名字不固定)
在項(xiàng)目settings中配置目錄:
通過后臺管理頁面上傳
以圖片為例:
- 設(shè)計模型類
class PicClass(models.Model):
gpic = models.ImageField(upload_to = "booktest") #upload_to指定上傳目錄。注意:是上傳到我們配置了的目錄下的哪個目錄(我們這里配置的是static/media目錄)
-
遷移搔课。
遷移的注意事項(xiàng):1. 每有一次遷移成功胰柑,就會在diango_migrations表下有一條記錄,在后面繼續(xù)遷移時爬泥,如果發(fā)現(xiàn)該表中已經(jīng)有該記錄柬讨,則不再遷移;2. 如果報xxx表已經(jīng)存在袍啡,就到migrations目錄下的對應(yīng)文件中刪除已經(jīng)存在的表即可
在應(yīng)用下的admin里面注冊模型類
假設(shè)我們通過瀏覽器上傳了圖片踩官,就會發(fā)現(xiàn)該表中多了一跳紀(jì)律,它里面存儲的是相對路徑
用戶自定義上傳圖片
- 定義用戶上傳圖片的頁面并顯示葬馋,就是自己定義一個表單卖鲤。只有定義 了method和enctype肾扰,才能上傳文件
在模板文件show_upload.html中包含如下代碼:
<form method='post' actioon='/upload_handle' enctype='multipart/form-data'>
<!-- 在/upload_handle這個地址對應(yīng)的視圖函數(shù)里面保存文件 -->
{% csrf_token %}
<input type='file' name='pic'><br>
<input type='submit' value='上傳'>
</form>
視圖函數(shù):
from django.conf import settings #獲取配置的上傳目錄
def show_upload(request):
""""顯示上傳圖片的頁面"""
return render(request,'booktest/show_upload.html')
def upload_handle(request):
"""上傳圖片處理"""
url的配置以及部分模塊的導(dǎo)入省略
admin站點(diǎn)
控制管理頁展示
類ModelAdmin可以控制模型在Admin界面中的展示方式畴嘶,主要包括在列表頁的展示方式、添加修改頁的展示方式集晚。
在應(yīng)用下的admin.py中窗悯,注冊模型類前定義管理類。eg:AreaAdmin
管理類有兩種使用方式:
- 注冊參數(shù)
- 裝飾類
注冊參數(shù):在應(yīng)用下的admin.py文件中偷拔,注冊模型類: