第四章 視圖
4.1 探究視圖
一、視圖說明
視圖(View)是Django的MTV架構模式的V部分,主要負責處理用戶請求和生成相應的相應部分,然后在頁面或其它類型文檔中顯示。也可以理解為視圖是MVC架構里面的C部分(控制器),主要處理功能和業務上的邏輯。
下面是視圖函數的return相應類型:
相應類型 | 說明 |
HttpResponse('Hello world') | HTTP狀態碼200,請求已成功被服務器接收 |
HttpResponseRedirect('/admin/') | HTTP狀態碼302,重定向Admin站點的URL |
HttpResponsePermanentRedirect('/admin/') | HTTP狀態碼301,永久重定向Admin站點的URL |
HttpResponseBadRequest('BadRequest') | HTTP狀態碼400,訪問的頁面不存在或者請求錯誤 |
HttpResponseNotFound('NotFound') | HTTP狀態碼404,網頁不存在或網頁的URL失效 |
HttpResponseNotForbidden('NotFound') | HTTP狀態碼403,沒有訪問權限 |
HttpResponseNotAllowed('NotAllowedGet') | HTTP狀態碼405,不允許使用該請求方式 |
HttpResponseServerError('ServerError') | HTTP狀態碼500,服務器內部錯誤 |
響應類型代表HTTP狀態碼,其核心作用是WEB Server服務器用來告訴客戶端當前的網頁請求發生了什么事,或者當前Web服務器的響應狀態。上述響應主要來自於django.http,該模塊是實現響應功能的核心。在實際開發中,可以使用該模塊實現文件下載功能,在index的urls.py和views.py中分別添加一下代碼:
#urls.py代碼 path('download.html',views.download), #views.py代碼 import csv def download(request): response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' writer = csv.writer(response) writer.writerow(['First row', 'A', 'B', 'C']) return response
上述文件下載功能說明如下:
1、當接收用戶的請求后,視圖函數download首先定義HttpResponse的相應類型為文件(text/csv)類型,生成response對象。
2、然后在response對象上定義Content-Disposition,設置瀏覽器下載文件的名稱。attachment設置文件的下載方式,filename為文件名。
3、最后使用csv模塊加載response對象,把數據寫入response對象所設置的CSV文件並將response對象返回到瀏覽器上,從而實現文件下載。運行結果如下圖:
http://127.0.0.1:8000/download.html
django.http除了實現文件下載之外,要使用該模塊生成精美的HTML網頁,可以在響應內容中編寫HTML源碼,如HttpResponse('<html><body>...</body></html>')。盡管這是一種可行的方法,但並不符合實際開發。因此,Django在django.http模塊上進行封裝,從而有了render()、render_to_response()和redirect()函數。
render()和render_to_response()實現的功能是一致的。render_to_response()自2.0版本依賴已開始啟用,並不代表在2.0版本無法使用,只是大部分開發者都使用render()。因此,本書只對render()進行講解,render()的語法如下:
render(request, template_name, context = None, content_type = None, status = None, using = None)
函數render()的參數request和template_name是必須參數,其余的參數是可選參數。各個參數如下:
1、request:瀏覽器向服務器發送的請求對象,包含用戶信息、請求內容和請求方式等。
2、template_name:HTML模板文件名,用於生成HTML網頁。
3、context:對HTML模板的變量賦值,以字典格式表示,默認情況下是一個空字典。
4、content_type:響應數據的數據格式,一般情況下使用默認值即可。
5、status:HTTP狀態碼。默認為200
6、using:設置HTML模板轉換生成HTML網頁的模板引擎
項目的templates有index.html模板,這是一個偽華為商城的網頁,static用於存放該HTML模板的靜態資源。我們在urls.py和views.py中編寫如下代碼:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates'), #增加如下這一行 os.path.join(BASE_DIR, 'index/templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] #在index下面的views.py中修改index函數如下 def index(request): return render(request, 'index/index.html', context={'title': '首頁'}, status=500)
從視圖函數的context={'title':'首頁'}可知,將index.html模板變量title的值設為首頁,返回的狀態碼為500。啟動項目,運行結果如下:
除了render函數外,還有redirect()函數。redirect()函數用於實現請求重定向,重定向的鏈接以字符串的形式表示,鏈接的地址信息可以支持相對路徑和絕對路徑,代碼如下:
#在index項目下面urls.py中編寫如下代碼: path('login.html',views.login) #在index項目下面views.py中的視圖函數編寫如下代碼: from django.shortcuts import render,redirect def login(request): #相對路徑,代表首頁地址 return redirect('/') #絕對路徑,完整的地址信息 #return redirect('http://127.0.0.1:8000/')
4.2 數據可視化
視圖除了接受用戶請求和返回響應內容之外,還可以與模型(Model)實現數據交互(操作數據庫)。視圖相當於一個處理中心,負責接收用戶請求,然后根據請求信息讀取並處理后台數據,最后生成HTML網頁返回給用戶。
from django.db import models # Create your models here. class Product(models.Model): id = models.IntegerField(primary_key=True) name = models.CharField(max_length=50) type = models.CharField(max_length=20)
上述代碼將Product類和數據表Product構成映射關系,代碼只是搭建兩者的關系,在數據庫中並沒有生成相應的數據表。需要通過python manage.py makemigrations創建
#根據models.py生成相關的.py文件,該文件用於創建數據表
python manage.py makemigrations
Tracking file by folder pattern: migrations Migrations for 'index': index\migrations\0001_initial.py - Create model Product Following files were affected E:\test5\MyDjango\index\migrations\0001_initial.py Process finished with exit code 0
#創建數據表
python manage.py migrate
Tracking file by folder pattern: migrations
Operations to perform:
Apply all migrations: admin, auth, contenttypes, index, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying index.0001_initial... OK
Applying sessions.0001_initial... OK
下面是執行完成后,在數據庫中可以看到新創建的數據庫表
從圖中可以看到Django會默認創建多個數據庫表,其中數據表index_product對應index的models.py所定義的Product類,其余的數據表都是Django內置的功能所生成的,主要用於Admin站點、用戶認證和Session會話功能。
下面在數據庫表index_product中添加如下所示的數據:
python manage.py shell
#導入數據里的類 In [1]: from index.models import Product
#使用create往數據庫里面添加數據 In [2]: Product.objects.create(name='榮耀',type='手機') Out[2]: <Product: Product object (None)> In [3]: Product.objects.create(name='暢玩',type='手機') Out[3]: <Product: Product object (None)> In [4]: Product.objects.create(name='華為',type='手機') Out[4]: <Product: Product object (None)> In [11]: Product.objects.create(name='移動電源',type='通用配件') Out[11]: <Product: Product object (None)>
完成數據表的數據添加后,接着將數據表的數據展現在網頁上。首先將模板文件index.html左側導航欄的代碼注釋掉,然后在同一位置添加Django的模板語法:
{# <ul id="cate_box" class="lf">#} {# <li>#} {# <h3><a href="#">手機</a></h3>#} {# <p><span>榮耀</span><span>暢玩</span><span>華為</span><span>Mate/P系列</span></p>#} {# </li>#} {# <li>#} {# <h3><a href="#">平板 & 穿戴</a></h3>#} {# <p><span>平板電腦 </span><span>手環</span><span>手表</span></p>#} {# </li>#} {# </ul>#} <ul id="cate_box" class="lf"> {% for type in type_list %} <li> <h3><a href="#">{{ type.type }}</a></h3> <p> {% for name in name_list %} {% if name.type == type.type %} <span>{{ name.name }}</span> {% endif %} {% endfor %} </p> </li> {% endfor %} </ul>
新添加的代碼是Django的模板語法,主要將視圖的變量傳遞給模板,通過模板引擎轉換成HTML語言。上述代碼使用循環和判斷語句對變量進行分析處理,具體的模板語法會在后續的章節中講解。最后在視圖函數中編寫代碼,將數據表的數據與模板連接起來,實現世界可視化,代碼如下:
def index(request): type_list = Product.objects.values('type').distinct() name_list = Product.objects.values('name','type') context = {'title': '首頁', 'type_list': type_list, 'name_list': name_list} return render(request, 'index.html', context=context, status=200)
上述代碼中,視圖函數處理流程如下:
1、type_list用於查詢數據表字段type的數據並將數據去重,name_list用於查詢數據表字段type和name的全部數據,這兩種獨特的查詢方式都是由Django內置的ORM框架提供的。
2、將查詢所得的數據以字典的數據格式寫入變量context中,變量context是render()函數的參數值,其作用是將變量傳遞給HTML模板。
3、當HTML模板接收到變量type_list和name_list后,模板引擎解析模板語法並生成HTML文件。運行結果如下:
從上述例子可以看到,如果想要將數據庫的數據展現在網頁上,需要由視圖、模型和模板共同實現,實現步驟如下:
1、定義數據模型,以類的方式定義數據表的字段。在數據庫創建數據表時,數據表有模型定義的類生成。
2、在視圖導入模型所定義的類,該類也稱為數據表對象,Django為數據表對象提供獨有的數據操作方法,可以實現數據庫操作,從而獲取數據表的數據。
3、視圖函數獲取數據后,將數據以字典、列表、或對象的方式傳遞給HTML模板,並由模板引擎接收和解析,最后生成相應的HTML網頁。
提示:
在上述模板視圖函數中,變量context是以字典的形式傳遞給HTML模板的。在實際開發過程中,如果傳遞的變量過多,使用變量context時就顯得非常冗余,而且不利於日后的維護和更新。因此,使用locals()取代變量context,代碼如下:
locals()使用技巧 def index(request): type_list = Product.objects.values('type').distinct() name_list = Product.objects.values('name','type') title = '首頁' return render(request, 'index.html',context=locals(), status=200)
locals()的使用方法:在視圖函數中所定義的變量名一定要與HTML模板的變量名相同才能生效,如視圖函數的type_list與HTML模板的type_list,兩者的變量名一致才能將視圖函數的變量傳遞給HTML模板。
4.3 獲取請求信息
視圖是用於接收並處理用戶的請求信息,請求信息存放在視圖函數的參數request中。為了進一步了解參數request的屬性,在PyCharm中使用debug模式啟動項目,並在視圖函數中設置斷點功能,然后查看request對象的全部屬性:
參數request的屬性
從圖中可以看到參數request的屬性,這代表用戶的請求信息,以下是開發過程中常用的屬性:
屬性 | 說明 | 實例 |
COOKIES | 獲取客戶端(瀏覽器)Cookie信息 | data = request.COOKIES |
FILES | 字典對象,包含所有的上傳文件。該字典有三個鍵:filename為上傳文件的文件名;content-type為上傳文件的類型;content為上傳文件的原始內容 | file = request.FILES |
GET | 獲取GET請求的請求參數,以字典形式存儲 | //如{'name':'TOM'}request.POST.get('name') |
META | 獲取客戶端的請求頭信息,以字典形式存儲 | //獲取客戶端的IP地址request.META.get('REMOTE_ADDR') |
POST | 獲取POST請求的請求參數,以字典形式存儲 | //如{'name':'TOM'}request.POST.get('name') |
method | 獲取該請求的請求方式(GET獲POST請求) | data = request.method |
path | 獲取當前請求的URL地址 | path = request.path |
user | 獲取當前請求的用戶信息 | //獲取用戶名name = request.user.username |
上述屬性中的GET、POST和method是每個Web開發人員必須掌握的基本屬性,屬性GET和POST用於獲取用戶的請求參數,屬性method用戶獲取用戶的請求方式。以視圖函數login為例:
#index/urls.py下面添加如下路由 path('login.html', views.login), #index/views.py添加如下內容 def login(request): if request.method == 'POST': name = request.POST.get('name') #絕對路徑,完整的地址信息 #return redirect('http://127.0.0.1:8000/') #相對地址,代表首頁地址 return redirect('/') else: if request.GET.get('name'): name = request.GET.get('name') else: name = 'Everyone' return HttpResponse('username is' + name)
視圖函數login分別使用了屬性GET、POST和method,說明如下:
1、首先使用method用戶的請求方式進行判斷,一般情況下,用戶打開瀏覽器訪問某個URL地址都是GET請求;而在網頁上輸入信息並點擊某個按鈕時,以POST請求居多,如用戶登錄、注冊等。
2、若判斷請求方式為POST(GET),則通過屬性POST(GET)來獲取用戶提交的請求參數。不同的請求方式需要使用不同的屬性來獲取用戶提交的請求參數。
在瀏覽器上分別輸入以下URL地址:
http://127.0.0.1:8000/index/login.html
http://127.0.0.1:8000/index/login.html?name=Tom
第二條URL地址多出了?name=Tom,這是GET請求的請求參數。GET請求參數以?為標識,請求參數以等值的形式表示,等號前面的是參數名,后面的是參數值,如果涉及多個參數,每個參數之間用&拼接。運行結果如下:
運行結果如圖
4.4 通用視圖
通用視圖是通過定義和聲明類的形式實現的,根據用途划分為三大類:TemplateView、ListView和DetailView。三者說明如下:
1、TemplateView直接返回HTML模板,但無法將數據庫的數據展示出來。
2、ListView能將數據庫的數據傳遞給HTML模板,通常獲取某個表的所有數據。
3、DetailView能將數據庫的數據傳遞給HTML模板,通常獲取數據表的單條數據。
根據4.2節實現的功能,我們將其視圖函數改用ListView實現。例如:
#index/urls.py下修改地址信息 #通用視圖ListView path('index/',views.ProductList.as_view()),
如果URL所指向的處理程序是由通用視圖執行,那么在編寫URL時,URL所指向的處理程序應當是一個通用視圖,並且該通用視圖上必須使用as_view()方法。因為通用視圖實質上是一個類。使用as_view()方法相當於對類進行實例化並由類方法as_view()執行處理。最后在views.py中編寫通用視圖ProductList的代碼,代碼如下:
# 通用視圖 from django.views.generic import ListView class ProductList(ListView): # context_object_name設置Html模版的變量名稱 context_object_name = 'type_list' # 設定HTML模版 template_name='index.html' # 查詢數據 queryset = Product.objects.values('type').distinct() # 重寫 get_queryset 方法,對模型product進行數據篩選。 def get_queryset(self): # 獲取URL的變量id print(self.kwargs['id']) # 獲取URL的參數name print(self.kwargs['name']) # 獲取請求方式 print(self.request.method) type_list = Product.objects.values('type').distinct() return type_list # 添加其他變量 def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['name_list'] = Product.objects.values('name','type') return context
通用視圖ProductList的代碼說明如下:
1、定義ProductList類,該類繼承自ListView類,具有ListView的所有特性。
2、context_object_name設置HTML模板的變量
3、template_name設置HTML模板的變量
4、queryset查詢數據庫數據,查詢結果會賦值給context_object_name所設置的變量
5、重寫函數get_queryset,該函數的功能與queryset實現的功能一致
6、重寫函數get_context_data,該函數設置HTML模板的其他變量。
通用視圖的代碼編寫規則有一定的固定格式,根據這個固定格式可以快速開發數據視圖。除此之外,通用視圖還可以獲取URL的參數和請求信息,使得通用視圖更加靈活,以get_queryset函數為例:
#在index模塊urls.py下面路徑 path('index/<id>.html', views.ProductList.as_view(), {'name':'phone'}), #在index模塊views.py下面添加下面函數 #通用視圖ProductList類 def get_queryset(self): #獲取URL的變量id print(self.kwargs['id']) #獲取URL的參數name print(self.kwargs['name']) #獲取請求方式 print(self.request.method) type_list = Product.objects.values('type').distinct() return type_list
上述代碼演示了如何在通用視圖中獲取URL的參數變量和用戶的請求信息,代碼說明如下:
1、首先對URL設置變量id和參數name,這兩者設置方式都是日常開發中經常使用的。
2、通用視圖在處理用戶請求時,URL的變量和參數都會存放在通用視圖的屬性kwargs中,因此使用self.kwargs['xxx']可以獲取變量值或參數值,xxx代表變量(參數)名。
3、要獲取用戶請求信息,可以從屬性self.request中獲取。self.request和視圖函數的參數request的使用方法是一致的。http://127.0.0.1:8000/index/index/5.html運行結果如下圖:
從上面的例子可以看出,通用視圖的代碼量感覺比視圖函數多,但是通用視圖是可以被繼承的。假如已經寫好了一個基於類的通用視圖,若要對其添加擴展功能,只需繼承原本這個類即可。如果寫的是視圖函數,其擴展性就沒有那么靈活,可能需要使用裝飾器等高級技巧,或者重新編寫新的視圖函數,而且新函數的部分代碼與原本函數的代碼相同。
4.5 本章小結
視圖是Django的MTV架構模式的V部分,主要負責處理用戶請求和生成相應的響應內容,然后在頁面或其他類型文檔中顯示。也可以理解為視圖是MVC架構里面的C部分(控制器),主要處理功能和業務上的邏輯。
視圖函數完成請求處理后,必須通過return方式返回數據內容給用戶,常用的返回方式由render()、render_to_response()和redirect()函數實現。其中,render()he render_to_response()實現的功能是一致的。render_to_sponse()自2.0版本以來已開始被棄用,並不代表在2.0無法使用,只是大部分開發者都使用render()。
render()的參數request和template_name是必須參數,其余的參數是可選參數。參數說明如下:
1、request:瀏覽器向服務器發送的請求對象,包含用戶信息、請求內容和請求方式等。
2、template_name:HTML模板文件名,用於生成HTML網頁。
3、context:對HTML模板的變量賦值,以字典格式表示,默認情況下是一個空字典。
4、content_type:響應數據的數據格式,一般情況下使用默認值即可。
5、status:HTTP狀態碼,默認為200。
6、using:設置HTML模板轉換生成HTML網頁的模板引擎。
如果想要將數據庫的數據展現在網頁上,需要有視圖、模型和模板共同實現,實現步驟如下:
1、定義數據模型,以類的方式定義數據表的字段。在數據庫創建數據表時,數據表由模型中定義的類生成。
2、在視圖中導入模型所定義的類,該類也稱為數據表對象,Django為數據表對象提供獨有的的數據操作方法,可以實現數據庫操作,從而獲取數據表的數據。
3、視圖函數獲取數據后,將數據以字典、列表或對象的方式傳遞給HTML模板,並有模板引擎接受和解析,最后生成相應的HTML網頁。
用戶的請求信息都存放在視圖函數的參數request中,其中屬性GET、POST和method是每個web開發人員必須掌握的基本屬性,屬性GET和POST用於獲取用戶的請求參數,屬性method用於獲取用戶的請求方式。
通用視圖是通過定義和聲明類的形式實現的,根據用途划分為三大類:TemplateVieW、ListView和DetailView。三者說明如下:
1、TemplateView直接返回HTML模板,但無法將數據庫的數據展示出來。
2、ListView能將數據庫的數據傳遞給HTML模板,通常獲取某個表的所有數據。
3、DetailView能將數據庫的數據傳遞給HTML模板,通常獲取數據表的單條數據。