視圖設置
一個視圖函數,簡稱視圖,是一個簡單的Python 函數,它接受Web請求并且返回Web響應。代碼寫在哪里也無所謂,只要它在你的應用目錄下面。但是為了方便視圖一般被定義在“應用/views.py”文件中。
視圖的第一個參數必須為HttpRequest實例,還可能包含下參數如:
- 通過正則表達式組獲得參數
視圖必須返回一個HttpResponse對象或子對象作為響應。
一.錯誤視圖
以下錯誤以常見的404報錯為例
方法1:
如果想要看到錯誤視圖,而不是錯誤調試信息的需要設置setting.py配置文件的調試開關設置為False:ALLOWED_HOSTS允許所有主機訪問。
DEBUG = False
ALLOWED_HOSTS = ["*",]
?然后在templates目錄下,創建404.HTML視圖
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>404報錯視圖</title>
</head>
<body>
<h1>錯誤視圖</h1>
<p>404報錯視圖</p></body>
</html>
再運行Django框架,即可在項目運行后得到如下所示:
若想要修改其他措施視圖依舊如此,如創建報錯500,403等
方法2:
在總目錄下使用固定的變量handler404或handler500等,不需要從專門的errviews視圖中顯式導入函數,Django會延遲導入,自己尋找
主目錄如下:
from django.contrib import admin
from django.urls import path,includeurlpatterns = [path('admin/', admin.site.urls),path('',include('myday.urls')),
]
handler404 = 'myday.errviews.E404'
errviews錯誤視圖如下寫入
from django.shortcuts import render,HttpResponsedef E404(request,exception=None):# print(exception)return HttpResponse("報錯404")
結果如下:
def get_info(request, id):#post = Post.post_object.get(title=title)post = get_object_or_404(Post,id=id) # 不區分大小寫return JsonResponse({'title': post.title,'content': post.content,# 其他需要的字段})
2.錯誤視圖2
當動態路由配置錯誤時,會報500錯誤,既服務器錯誤,我們可以使用get_object_or_404,在用戶訪問錯誤的路由時,報錯404
這樣當用戶訪問不存在的 ID 時,Django 會自動返回 404 頁面,而不是拋出 DoesNotExist 異常導致 500 錯誤。
def get_info(request, id):#post = Post.post_object.get(title=title)post = get_object_or_404(Post,id=id) # 不區分大小寫return JsonResponse({'title': post.title,'content': post.content,# 其他需要的字段})
二.HttpRequest 對象
1.META
包含所有的HTTP 頭部,使用字典形式返回
def meta_info(request):m = request.METAstr = ""for key,values in m.items():str += f"{key}:{values}\n"return HttpResponse(str)
2.get
獲取路由中的信息,查詢參數
def view_get(request):A = request.GET.getlist('a') # 多個參數時,使用列表B = request.GET.get('b')C = request.GET.get('c')return JsonResponse({ # json返回列表形式'a': A,'b': B,'c': C})
實例:
1.編輯視圖函數
def get(request):return render(request, 'get/get.html')def get1(request):a_value = request.GET.getlist('a') # 多個參數時,使用列表b_value = request.GET.get('b')c_value = request.GET.get('c')return JsonResponse({ # json返回列表形式'a_value': a_value,'b_value': b_value,'c_value': c_value})
2.配置路由
app_name用于后面在HTML文件中指定文件的路由
from django.contrib import admin
from django.urls import path,include,re_path
from .views import *
app_name = 'myday'
urlpatterns = [path("get/",get),path("get1/",get1,name='get1'),
]
?3.HTML文件
在模板中{% url 'myday:name'%}中的name,需要在urls中對路由進行name編寫
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>get請求</title>
</head>
<body>
<a href="{% url 'myday:get1' %}?a=1&a=2&b=2&c=3">get1,一鍵多值等</a>
</body>
</html>
3.post
在進行form表單提交的時候將 method 方式改成post方式,提交表單時會發起POST請求。需要使用HttpRequest對象的POST屬性接收參數,POST屬性返回QueryDict類型的對象
在表單進行提交時,控件name屬性的值作為鍵,value屬性的值為值,構成鍵值對提交,如果控件沒有name屬性那么將不進行提交。
1.先將settings中的csrf注釋
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware',# 'django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]
2.編寫視圖函數
def post(request):return render(request, 'get-post/post.html')def post1(request):p_dict = request.POST # 獲取post請求參數uname = p_dict.get('uname')upwd = p_dict.get('upwd')ugender = p_dict.get('ugender')uhobby = p_dict.getlist('uhobby') # 愛好是一鍵多值,使用getlist方法獲取所有愛好context = {'uname':uname,'upwd':upwd,'ugender':ugender,'uhobby':uhobby}return render(request,'get-post/post1.html',context=context)
3.HTML文件
提交表單HTML
<html lang="en">
<head><meta charset="UTF-8"><title>表單</title>
</head>
<body>
<form method="post" action="{% url 'myday:post1' %}">姓名:<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="燙頭"/>燙頭<br><input type="submit" value="提交"/>
</form></body>
</html>
接收查看效果?
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>姓名:
<p>{{ uname }}</p>
<br>
密碼:{{ upwd }}
<br>
性別:{{ ugender }}
<br>愛好:{{ uhobby }}</body>
</html>
4.編輯路由
from django.contrib import admin
from django.urls import path,include,re_path
from .views import *
app_name = 'myday'
urlpatterns = [path("post/",post,name='post'),path("post1/",post1,name='post1'),
]
三.HttpResponse對象
1.HttpResponse 屬性
- content:表示返回的內容
- charset:表示response采用的編碼字符集,默認為utf-8
- status_code:返回的HTTP響應狀態碼
- content-type:指定返回數據的的MIME類型,默認為'text/html'
2.設置cookie
- max_age 以秒為單位,如果Cookie 只應該持續客戶端瀏覽器的會話時長則應該為None(默認值)。
- expires 應該是一個 UTC "Wdy, DD-Mon-YY HH:MM:SS GMT" 格式的字符串,或者一個datetime.datetime 對象。如果expires 是一個datetime 對象,則max_age 會通過計算得到。
- max_age與expires二選一
- 如果不指定過期時間,則關閉瀏覽器過期
- 如果你想設置一個跨域的Cookie,請使用domain 參數。例如,domain=".lawrence.com" 將設置一個www.lawrence.com、blogs.lawrence.com 和calendars.lawrence.com 都可讀的Cookie。否則,Cookie 將只能被設置它的域讀取。
- 如果你想阻止客服端的JavaScript 訪問Cookie,可以設置httponly=True。
def set_cookie(request):response = HttpResponse("設置cookie")response.set_cookie('username', 'admin')response.set_cookie('password','123456',max_age=60*60*12) # 設置超時時間 60*60*12秒return response
def delete_cookie(request):response = HttpResponse('刪除cookie')response.delete_cookie('password') # 刪除鍵為password 的cookiereturn response
def get_cookie(request):cookie = request.COOKIES # 獲取cookieusername = cookie.get('username')password = cookie.get('password')response = HttpResponse('%s %s'%(username,password)) # 將獲取到的cookie返回return response
from django.contrib import admin
from django.urls import path,include,re_path
from .views import *
app_name = 'myday'
urlpatterns = [path("set_cookie/",set_cookie),path("delete_cookie/",delete_cookie),path("get_cookie/",get_cookie),
]
3.子類 HttpResponseRedirect 重定向對象
當一個視圖處理完成后,不需要向客戶端呈現數據,而是跳轉到其他頁面,比如用戶登錄成功之后,需要跳轉到登錄成功后的頁面。此時就需要模擬一個用戶請求的效果,從一個視圖轉到另外一個視圖,就稱為重定向。
django中提供 HttpResponseRedirect 對象實現重定向功能。HttpResponseRedirect 定義在django.http模塊中
下面我們用模擬登錄做示例: 定義一個登錄頁面模板。當登錄成功之后跳轉到博客首頁。如果賬號密碼校驗錯誤,返回原來的登錄頁面。?
1.設置視圖函數
第一個用于返回登錄界面,第二個用于核對post提交的信息
在執行if語句時,因為當用戶登錄成功時,通常需要跳轉到一個新頁面(如首頁?/index
),而不是直接渲染同一個頁面。
在執行else語句時
-
原因:當登錄失敗時,需要:
-
顯示錯誤信息(如“帳號或密碼錯誤”)。
-
保留用戶輸入(通過?
uname
?和?upwd
?回填表單,避免用戶重新輸入)。 -
保持當前頁面狀態(仍然是登錄頁,無需跳轉)。
-
-
為什么不重定向?
-
如果重定向(如?
return HttpResponseRedirect('/login')
),會導致:-
丟失錯誤信息和用戶輸入(重定向是新的 GET 請求,無法直接傳遞?
context
)。 -
需要額外機制(如 Django Messages 框架或 Session)臨時存儲錯誤信息,增加復雜度。
-
-
def login(request):return render(request, 'login.html')
def login1(request):if request.method == 'GET':return render(request, 'login.html',context={"no_login":0})if request.method =="POST":list = request.POSTuname = list.get('uname')upwd = list.get('upwd')if uname == 'admin' and upwd == '123':# 如果接收到的帳號密碼與初始設在的一樣那么表示登錄成功需要跳轉到首頁# 重定向# 效果同樣# return redirect('/myweb/index/')return HttpResponseRedirect('/index')else:# 帳號或密碼錯誤,重新返回登錄模板,并將用戶輸入的賬號密碼當參數傳遞回去。context = {'iserror': '帳號或密碼錯誤','uname': uname,'upwd': upwd,"no_login": 1}return render(request, 'login.html', context=context)
2.登錄界面HTML
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form method="post" action="{% url 'myday:login1' %}">用戶名:<input name="uname" value="{{ uname }}"> #用于回填表單密碼: <input name="upwd" value="{{ upwd }}"><input type="submit" value="登錄">
</form>
{% if no_login %}<p>{{ iserror }}</p><p>{{ uname }}</p>
{% endif %}
<span style="color: red"></span></body>
</html>
?3.配置路由
from django.contrib import admin
from django.urls import path,include,re_path
from .views import *
app_name = 'myday'
urlpatterns = [path("login/",login),path("login1/",login1,name='login1'),
]
4.改進:使用reverse
reverse (viewname, args=(參數,)) viewname是您要解析的網址路徑。args是參數元祖
Django 提供了更簡潔的?redirect()
?函數,內部自動調用?reverse()
:
def login1(request):name = "admin"pwd = "123"if request.method == 'GET':return render(request, 'login.html',context={"no_login":0})if request.method =="POST":list = request.POSTuname = list.get('uname')upwd = list.get('upwd')if uname == name and upwd == pwd:# 如果接收到的帳號密碼與初始設在的一樣那么表示登錄成功需要跳轉到首頁# 重定向# 效果同樣# return redirect('/myweb/index/')#return HttpResponseRedirect(reverse("myday:index"))return redirect("myday:index") #使用reverseelse:# 帳號或密碼錯誤,重新返回登錄模板,并將用戶輸入的賬號密碼當參數傳遞回去。context = {'iserror': '帳號或密碼錯誤','uname': uname,'upwd': upwd,"no_login": 1}return render(request, 'login.html', context=context)
四.session設置
對于敏感、重要的信息不能存在瀏覽器,也就不能使用cookie,如用戶名、余額、等級、驗證碼等信息 如何確保數據的安全性呢,那就是將數據保存在服務端。這就利用到session技術。
Session 對象存儲特定用戶會話所需的屬性及配置信息。這樣,當用戶在應用程序的 Web 頁之間跳轉時,存儲在 Session 對象中的變量將不會丟失,而是在整個用戶會話中一直存在下去。當用戶請求來自應用程序的 Web 頁時,如果該用戶還沒有會話,則 Web 服務器將自動創建一個 Session 對象。當會話過期或被放棄后,服務器將終止該會話。
django項目默認啟用Session: settings.py文件,在項MIDDLEWARE_CLASSES中啟用Session中間件
存儲方式: 設置SESSION_ENGINE項指定Session數據存儲的方式,可以存儲在數據庫、緩存、Redis等 存儲在數據庫中,如下設置可以寫,也可以不寫,這是默認存儲方式
SESSION_ENGINE='django.contrib.sessions.backends.db'
存儲在緩存中:存儲在本機內存中,如果丟失則不能找回,比數據庫的方式讀寫更快
SESSION_ENGINE='django.contrib.sessions.backends.cache'
混合存儲:優先從本機內存中存取,如果沒有則從數據庫中存取
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
如果存儲在數據庫中,需要在項INSTALLED_APPS中安裝Session應用
1.設置session
def set_session(request):request.session['is_login']=1request.session['username']="admin"request.session['name']="name1"#設置session過期時間#request.session.set_expiry(1800) #單位是秒,0是關閉瀏覽器后會過期,None是不會過期return HttpResponse("set_session")def get_session(request):print(request.session.keys())is_login=request.session.get('is_login')username=request.session.get('username')name=request.session.get('name')return JsonResponse({'is_login':is_login,'username':username,'name':name,})def del_session(request):del request.session['is_login']#request.session.clear()#清空數據,但會保留sessionkey 數據庫里還有key#request.session.flush()#刪除整個會話, 數據庫沒有這條記錄return HttpResponse("del_session")
五.動態路由
在視圖中編寫index與detail函數,并給detail函數增加一個動態路由
def index(request):post_list = Post.post_object.all()return render(request,'index1.html',{'post_list':post_list})def detail(request,id):post = get_object_or_404(Post,id=id)return render(request,'detail1.html',{'post':post})
在urls中使用正則化進行動態路由,注意路由后一定要加入name這個參數,不然后面HTML渲染模板會報錯
from django.contrib import admin
from django.urls import path,include,re_path
from .views import *
app_name = 'myday'
urlpatterns = [path("index/",index,name="index"),re_path("detail/(\d+)",detail,name="detail"),
]
?index.html 在題目處進行鏈接跳轉到詳情頁
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p>內容</p>
{% for post in post_list %}<a href="{% url 'myday:detail' post.id %}"><p style="color: red">題目:{{ post.title }}</p></a><p>內容:{{ post.content }}</p><p>作者:{{ post.author }}</p>
{% endfor %}</body>
</html>
?各個詳情頁的detail.html文件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p>題目{{ post.title }}</p>
<p>標簽{{ post.tag }}</p>
<p>分類{{ post.category }}</p>
</body>
</html>
六.基于類的通用視圖
在開發網站的過程中,有一些視圖函數雖然處理的對象不同,但是其大致的代碼邏輯是一樣的。比如一個博客和一個論壇,通常其首頁都是展示一系列的文章列表或者帖子列表。對處理首頁的視圖函數來說,雖然其處理的對象一個是文章,另一個是帖子,但是其處理的過程是非常類似的。先從數據庫取出文章或者帖子列表,然后將這些數據傳遞給模板并渲染模板。
Django 把這些相同的邏輯代碼抽取了出來,寫成了一系列的通用視圖函數,即基于類的通用視圖。
先導入類視圖
from django.views.generic import ListView
在我們的博客應用中,index視圖函數是從數據庫中獲取文章(Post)列表數據的: 博客首頁的函數視圖
def index(request):post_list = Post.post_object.all()return render(request, 'personal_blog/index.html', context={"post_list": post_list})
將 index 視圖函數改寫為類視圖
class IndexView(ListView):model = Posttemplate_name = 'personal_blog/index.html'context_object_name = 'post_list'
要寫一個類視圖,首先要繼承django提供的類視圖,這里是獲取POST文章列表,繼承ListView列表視圖。列表視圖 ListView 就是用來獲取模型類的列表數據,所以我們首頁需要繼承ListView.
- model。將 model 指定為 Post,告訴 Django 我要獲取的模型是 Post。
- template_name。指定這個視圖渲染的模板是 'personal_blog/index.html'。
- context_object_name。指定獲取的模型列表數據保存的變量名。這個變量會被傳遞給模板,相當于函數視圖中的context參數。
- index 視圖函數首先通過 Post.objects.all() 從數據庫中獲取文章(Post)列表數據,并將其保存到 post_list 變量中。在類視圖中這個過程 ListView 已經幫我們做了。我們只需告訴 ListView 去數據庫獲取的模型是 Post,即指定 model = Post
- 將獲得的模型數據列表保存到 post_list 里,即指定 context_object_name = 'post_list'。
- 然后渲染 'personal_blog/index.html' 模板文件,index 視圖函數中使用 render 函數。但這個過程 ListView 已經幫我們做了,我們只需指定渲染哪個模板即可。
- 配置url,函數視圖的url跟類視圖url有所區別 , IndexView 是一個類,不能直接替代 index 函數需要先將類視圖轉換成函數視圖,調用類視圖的 as_view() 方法.
1.index-detail
1.編寫視圖函數
class IndexView(ListView):model = Posttemplate_name = 'index1.html'context_object_name = 'post_list'class DetailView(IndexView):model = Posttemplate_name = 'detail1.html'context_object_name = 'post'def get_queryset(self):print(self.args) #self.args用于接收路由中的動態參數return self.model.post_object.get(id=self.args[0])
因為后面的detailview是繼承于indexview的,而indexview是Post.objects.all(),所以要進行父類方法重寫
2.編寫HTML文件
index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p>內容</p>
{% for post in post_list %}<a href="{% url 'myday:detail1' post.id %}"><p style="color: red">題目:{{ post.title }}</p></a><p>內容:{{ post.content }}</p><p>作者:{{ post.author }}</p>
{% endfor %}</body>
</html>
detail.html?
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p>題目{{ post.title }}</p>
<p>標簽{{ post.tag }}</p>
<p>分類{{ post.category }}</p>
</body>
</html>
3.配置路由
from django.contrib import admin
from django.urls import path,include,re_path
from .views import *
app_name = 'myday'
urlpatterns = [path("index1",IndexView.as_view(),name="index1"),re_path("detail1/(\d+)",DetailView.as_view(),name="detail1"),
]
2.添加上下文?
通常來說,get_context_data會將當前類中的上下文數據,合并父類中的上下文數據,傳給模板。但前提是你調用了父類的get_context_data方法。
class IndexView(ListView):model = Posttemplate_name = 'index1.html'context_object_name = 'post_list'class DetailView(IndexView):model = Posttemplate_name = 'detail1.html'context_object_name = 'post'def get_queryset(self):print(self.args) #self.args用于接收路由中的動態參數return self.model.post_object.get(id=self.args[0])def get_context_data(self, **kwargs):context = super().get_context_data(**kwargs) #注意:要調用父類context ["dt"]= "獨特" #增加字段return context
3.登錄類視圖
1.視圖函數
class LoginView(View):name = "admin"pwd = "123"template_name = "login.html"def get(self, request, *args, **kwargs):return render(request, self.template_name)def post(self, request):list = request.POSTuname = list.get('uname')upwd = list.get('upwd')if uname == self.name and upwd == self.pwd:# 如果接收到的帳號密碼與初始設在的一樣那么表示登錄成功需要跳轉到首頁# 重定向# 效果同樣# return redirect('/myweb/index/')# return HttpResponseRedirect(reverse("myday:index"))return redirect("myday:index")else:# 帳號或密碼錯誤,重新返回登錄模板,并將用戶輸入的賬號密碼當參數傳遞回去。context = {'iserror': '帳號或密碼錯誤','uname': uname,'upwd': upwd,"no_login": 1}return render(request, self.template_name, context=context)
2.HTML頁面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form method="post" action="{% url 'myday:login_class' %}">用戶名:<input name="uname" value="{{ uname }}">密碼: <input name="upwd" value="{{ upwd }}"><input type="submit" value="登錄">
</form>
{% if no_login %}<p>{{ iserror }}</p><p>{{ uname }}</p>
{% endif %}
<span style="color: red"></span></body>
</html>
3.配置路由
from django.contrib import admin
from django.urls import path,include,re_path
from .views import *
app_name = 'myday'
urlpatterns = [path("login_class",LoginView.as_view(),name="login_class"),
]
總結(如何選擇)
類視圖 | 用途 | 對應 HTTP 方法 |
---|---|---|
ListView | 顯示多條數據(列表) | GET |
DetailView | 顯示單條數據(詳情) | GET |
CreateView | 創建新數據(表單提交) | GET (表單) +?POST (提交) |
UpdateView | 修改已有數據(表單提交) | GET (表單) +?POST (提交) |
DeleteView | 刪除數據(確認刪除) | GET (確認頁) +?POST (執行刪除) |