1. 程式人生 > >django -- 錯誤跳轉&表單驗證&資料分頁

django -- 錯誤跳轉&表單驗證&資料分頁

## 重點:

1、錯誤跳轉

2、表單驗證

3、資料分頁

## 複習  

``` from django.views.decorators.http from require_http_methods,require_GET,require_POST @require_http_methods['GET','POST']  @require_GET def index(request)

301 永久重定向 nginx 和 apache 中 進行設定    location /{      }

302 臨時重定向     from django.shortcuts import redirect,reverse  redirect(reverse())

def index(request):     WSGIRequest     print(request.path)     print(request.method)     request.method == "GET"     return HttpResponse("") ```

## 返回  json 型別  

``` def jsonresponse_view(request):     person = {         'username':'kangbazi1806',         'age':18,         'height':'181cm'     }     person_str = json.dumps(person)  #將資料dump轉化為  json字串       response = HttpResponse(person_str,content_type='application/json') 封裝成 Response返回給瀏覽器 並告訴瀏覽器 這是 json 型別       return response 最後返回  response                以上的可以換成  

def jsonresponse_view(request):     person = {         'username':'kangbazi1806',         'age':18,         'height':'181cm'     }     response = JsonResponse(person,safe=False)   JsonResponse 只能轉化字典型別的  非字典型別  必須加上 safe=False     return response -------------------------------------------------------------------          完整程式碼例項 app:front.py       from django.shortcuts import render       import json       from django.http import HttpResponse,JsonResponse

      def jsonresponse_view(request):           #第一種方式           # person = {           #     'username':'kangbazi',           #     'age':22,           #     'height':'181cm'           # }           # person_str = json.dumps(person)           # response = HttpResponse(person_str,content_type='application/json')           # return response

          #第二種方式           person = {               '姓名':'kangbazi',               '年齡':22,               '身高':'180cm'           }           response = JsonResponse(person,safe=False)           return response ```

## 類檢視

在寫檢視的時候,`Django`除了使用函式作為檢視,也可以使用類作為檢視。使用類檢視可以使用類的一些特性,比如繼承等。

### View:

**django.views.generic.base.View**是主要的類檢視,所有的類檢視都是繼承自他。如果我們寫自己的類檢視,也可以繼承自他。然後再根據當前請求的`method`,來實現不同的方法。比如這個檢視只能使用`get`的方式來請求,那麼就可以在這個類中定義`get(self,request,*args,**kwargs)`方法。以此類推,如果只需要實現`post`方法,那麼就只需要在類中實現`post(self,request,*args,**kwargs)`。示例程式碼如下:

```python from django.views import View def index(request):     return HttpResponse("") class BookDetailView(View):     def get(self,request,*args,**kwargs):         return render(request,'detail.html') ```

類檢視寫完後,還應該在`urls.py`中進行對映,對映的時候就需要呼叫`View`的類方法`as_view()`來進行轉換。示例程式碼如下:

```python urlpatterns = [         path("",views.index)     path("detail/<book_id>/",views.BookDetailView.as_view(),name='detail') ] ```

除了`get`方法,`View`還支援以下方法`['get','post','put','patch','delete','head','options','trace']`。

如果使用者訪問了`View`中沒有定義的方法。比如你的類檢視只支援`get`方法,而出現了`post`方法,那麼就會把這個請求轉發給`http_method_not_allowed(request,*args,**kwargs)`。示例程式碼如下:

```python class AddBookView(View):     def post(self,request,*args,**kwargs):         return HttpResponse("書籍新增成功!")

    def http_method_not_allowed(self, request, *args, **kwargs):         return HttpResponse("您當前採用的method是:%s,本檢視只支援使用post請求!" % request.method) ```

`urls.py`中的對映如下:

```python path("addbook/",views.AddBookView.as_view(),name='add_book') ```

如果你在瀏覽器中訪問`addbook/`,因為瀏覽器訪問採用的是`get`方法,而`addbook`只支援`post`方法,因此以上檢視會返回您當前採用的`method`是:`GET`,本檢視只支援使用`post`請求!。

其實不管是`get`請求還是`post`請求,都會走`dispatch(request,*args,**kwargs)`方法,所以如果實現這個方法,將能夠對所有請求都處理到。

### TemplateView:

**django.views.generic.base.TemplateView**,這個類檢視是專門用來返回模版的。在這個類中,有兩個屬性是經常需要用到的,一個是`template_name`,這個屬性是用來儲存模版的路徑,`TemplateView`會自動的渲染這個變數指向的模版。另外一個是`get_context_data`,這個方法是用來返回上下文資料的,也就是在給模版傳的引數的。示例程式碼如下:

```python from django.views.generic.base import TemplateView

class HomePageView(TemplateView):

    template_name = "home.html"

    def get_context_data(self, **kwargs):         context = {             "phone":"123456"         }                    return context          {{phone}} ```

在`urls.py`中的對映程式碼如下:

```python from django.urls import path

from myapp.views import HomePageView

urlpatterns = [     path('', HomePageView.as_view(), name='home'), ] ```

如果在模版中不需要傳遞任何引數,那麼可以直接只在`urls.py`中使用`TemplateView`來渲染模版。示例程式碼如下:

```python from django.urls import path from django.views.generic import TemplateView

urlpatterns = [     path('about/', TemplateView.as_view(template_name="about.html")), ] ```

### ListView:

在網站開發中,經常會出現需要列出某個表中的一些資料作為列表展示出來。比如文章列表,圖書列表等等。在`Django`中可以使用`ListView`來幫我們快速實現這種需求。示例程式碼如下:

```python from django.views.generic import TemplateView,ListView class ArticleListView(ListView):     model = Article   #你的模型       template_name = 'article_list.html' #渲染的頁面      paginate_by = 10  #每頁顯示多少條        context_object_name = 'articles' #在頁面上 進行  遍歷 for article in articles     ordering = 'id' #結果根據什麼進行排序       page_kwarg = 'p'   #http://127.0.0.1:9000/article/?p=2

    def get_context_data(self, **kwargs):#獲取上下文的資訊    分頁的相關資訊           context = super(ArticleListView, self).get_context_data(**kwargs)         print(context.count)         print(context)                  """         {'paginator': <django.core.paginator.Paginator object at 0x00000277042B40F0>,          'page_obj': <Page 2 of 8>, 'is_paginated': True,         """                           return context

    def get_queryset(self):         return Article.objects.filter(id__lte=89)  ```

對以上程式碼進行解釋:

1. 首先`ArticleListView`是繼承自`ListView`。 2. `model`:重寫`model`類屬性,指定這個列表是給哪個模型的。 3. `template_name`:指定這個列表的模板。 4. `paginate_by`:指定這個列表一頁中展示多少條資料。 5. `context_object_name`:指定這個列表模型在模板中的引數名稱。 6. `ordering`:指定這個列表的排序方式。 7. `page_kwarg`:獲取第幾頁的資料的引數名稱。預設是`page`。 8. `get_context_data`:獲取上下文的資料。 9. `get_queryset`:如果你提取資料的時候,並不是要把所有資料都返回,那麼你可以重寫這個方法。將一些不需要展示的資料給過濾掉。

## Paginator和Page類(page_obj):

`Paginator`和`Page`類都是用來做分頁的。他們在`Django`中的路徑為`django.core.paginator.Paginator`和`django.core.paginator.Page`。以下對這兩個類的常用屬性和方法做解釋:

``` class ArticleListView(ListView):     model = Article     template_name = 'article_list.html'     paginate_by = 20     context_object_name = 'articles'     ordering = 'create_time'     page_kwarg = 'p'     def get_context_data(self,**kwargs):         #要獲取上下文的資料 需要繼承於父類         context = super(ArticleListView,self).get_context_data(*kwargs)         print("="*30)         print(context)         print("=" * 30)         paginator = context.get('paginator') #獲取context中的 paginator         print(paginator.count) #151條資料         print(paginator.num_pages) #8 分 8頁         print(paginator.page_range) #range(1, 9) 包含第一頁 不包含第九頁 ```

### Paginator常用屬性和方法:

1. `count`:總共有多少條資料。 2. `num_pages`:總共有多少頁。 3. `page_range`:頁面的區間。比如有三頁,那麼就`range(1,4)`。

``` class ArticleListView(ListView):     model = Article     template_name = 'article_list.html'     paginate_by = 20     context_object_name = 'articles'     ordering = 'create_time'     page_kwarg = 'p'     def get_context_data(self,**kwargs):         #要獲取上下文的資料 需要繼承於父類         context = super(ArticleListView,self).get_context_data(*kwargs)          page_obj = context.get('page_obj')  從 context 中 獲取 page_obj          #http://127.0.0.1:9000/article/list/?p=7         print(page_obj.has_next()) 是否還有下一頁  True         print(page_obj.has_previous()) 是否還有上一頁 True         print(page_obj.next_page_number()) 下一頁的頁碼  8          

```

### Page常用屬性和方法:

1. `has_next`:是否還有下一頁。 2. `has_previous`:是否還有上一頁。 3. `next_page_number`:下一頁的頁碼。 4. `previous_page_number`:上一頁的頁碼。 5. `number`:當前頁。 6. `start_index`:當前這一頁的第一條資料的索引值。 7. `end_index`:當前這一頁的最後一條資料的索引值。

## django錯誤處理   

### 常見的錯誤  

#### 2開頭  一般是成功的  

#### 3 開頭的 重定向     

​    301

​    302

#### 4開頭的   一般是客戶端的錯誤   

​    404 找不到目標url 

​    403 你沒有許可權訪問相關的資料

​    405 請求方法不允許 限制請求的過程中 只允許你 get請求  但是 你很調皮  非得 post傳過去  這個時候報405錯誤

​    400 請求的引數有錯誤  

#### 5開頭的 一般是伺服器的錯誤

​    500 伺服器內部錯誤  程式碼有 bug  

​    502    一般是 伺服器部署錯誤  比如  nginx啟動 但是  uwsgi 有無 沒法完成正常的請求    

> 生產環境  也就是 線上  上線以後 會把debug 關閉 settings.py  > > ``` > DEBUG = False > > ALLOWED_HOSTS = ['127.0.0.1'] > ```

常見的錯誤  比如 404 500 直接在  templates 下面 新建  404.html 500.html如果出現 404 500 錯誤 會自動的顯示這個頁面的內容 

其它錯誤 比如  400 403  

專門 定義一個 app  名字叫  errors  

在errors 下面 新建一個  templates  下面再建一個 errors  裡邊 建立  頁面   400.html或者 403.html 502.html

在errors 應用下面  

vim views.py

``` from django.shortcuts import render def view_400(request):     return render(request,'errors/400.html') # Create your views here. def view_403(request):     return render(request,'errors/403.html') def view_502(request):     return render(request,'errors/502.html') ```

vim urls.py 

``` from django.urls import path from . import  views app_name = 'errors' urlpatterns = [     path('400.html',views.view_400,name='400'),     path('403.html',views.view_403,name='403'),     path('502.html',views.view_502,name='502'), ] ```

在 專案總的  urls.py 下面  

```  path('errors/',include('errors.urls')) ```

其它 應用  比如  front  或者  book  或者其它   通過redirect 重定向   

``` def index(request):     if not request.GET.get('username'):         #400請求引數有錯誤         return redirect(reverse('errors:502'))     return HttpResponse("首頁") ```

# 表單:

## HTML中的表單:

單純從前端的`html`來說,表單是用來提交資料給伺服器的,不管後臺的伺服器用的是`Django`還是`PHP`語言還是其他語言。只要把`input`標籤放在`form`標籤中,然後再新增一個提交按鈕,那麼以後點選提交按鈕,就可以將`input`標籤中對應的值提交給伺服器了。

## Django中的表單:

`Django`中的表單豐富了傳統的`HTML`語言中的表單。在`Django`中的表單,主要做以下兩件事:

1. 渲染表單模板。 2. 表單驗證資料是否合法。

## Django中表單使用流程:

在講解`Django`表單的具體每部分的細節之前。我們首先先來看下整體的使用流程。這裡以一個做一個留言板為例。首先我們在後臺伺服器定義一個表單類,繼承自`django.forms.Form`。示例程式碼如下:

```python # forms.py class MessageBoardForm(forms.Form):     title = forms.CharField(max_length=3,label='標題',min_length=2,error_messages={"min_length":'標題字元段不符合要求!'})     content = forms.CharField(widget=forms.Textarea,label='內容')     email = forms.EmailField(label='郵箱')     reply = forms.BooleanField(required=False,label='回覆') ```

然後在檢視中,根據是`GET`還是`POST`請求來做相應的操作。如果是`GET`請求,那麼返回一個空的表單,如果是`POST`請求,那麼將提交上來的資料進行校驗。示例程式碼如下:

```python # views.py class IndexView(View):     def get(self,request):         form = MessageBoardForm() #例項化一個表單物件           return render(request,'index.html',{'form':form})

    def post(self,request):         form = MessageBoardForm(request.POST)         if form.is_valid(): #這裡是為了驗證 froms.py中的規則              title = form.cleaned_data.get('title')             content = form.cleaned_data.get('content')             email = form.cleaned_data.get('email')             reply = form.cleaned_data.get('reply')             return HttpResponse('success')         else:             print(form.errors)             return HttpResponse('fail') ```

在使用`GET`請求的時候,我們傳了一個`form`給模板,那麼以後模板就可以使用`form`來生成一個表單的`html`程式碼。在使用`POST`請求的時候,我們根據前端上傳上來的資料,構建一個新的表單,這個表單是用來驗證資料是否合法的,如果資料都驗證通過了,那麼我們可以通過`cleaned_data`來獲取相應的資料。在模板中渲染表單的`HTML`程式碼如下:

```html <form action="" method="post">     <table>         {{form.as_table}}         <tr>             <td></td>             <td><input type="submit" value="提交"></td>         </tr>     </table> </form> ```

我們在最外面給了一個`form`標籤,然後在裡面使用了`table`標籤來進行美化,在使用`form`物件渲染的時候,使用的是`table`的方式,當然還可以使用`ul`的方式(`as_ul`),也可以使用`p`標籤的方式(`as_p`),並且在後面我們還加上了一個提交按鈕。這樣就可以生成一個表單了

# 用表單驗證資料

## 常用的Field:

使用`Field`可以是對資料驗證的第一步。你期望這個提交上來的資料是什麼型別,那麼就使用什麼型別的`Field`。

### CharField:

用來接收文字。 引數:

- max_length:這個欄位值的最大長度。 - min_length:這個欄位值的最小長度。 - required:這個欄位是否是必須的。預設是必須的。 - error_messages:在某個條件驗證失敗的時候,給出錯誤資訊。

### EmailField:

用來接收郵件,會自動驗證郵件是否合法。 錯誤資訊的key:`required`、`invalid`。

### FloatField:

用來接收浮點型別,並且如果驗證通過後,會將這個欄位的值轉換為浮點型別。 引數:

- max_value:最大的值。 - min_value:最小的值。

錯誤資訊的key:`required`、`invalid`、`max_value`、`min_value`。

### IntegerField:

用來接收整形,並且驗證通過後,會將這個欄位的值轉換為整形。 引數:

- max_value:最大的值。 - min_value:最小的值。

錯誤資訊的key:`required`、`invalid`、`max_value`、`min_value`。

### URLField:

用來接收`url`格式的字串。 錯誤資訊的key:`required`、`invalid`。

------

## 常用驗證器:

在驗證某個欄位的時候,可以傳遞一個`validators`引數用來指定驗證器,進一步對資料進行過濾。驗證器有很多,但是很多驗證器我們其實已經通過這個`Field`或者一些引數就可以指定了。比如`EmailValidator`,我們可以通過`EmailField`來指定,比如`MaxValueValidator`,我們可以通過`max_value`引數來指定。以下是一些常用的驗證器:

1. `MaxValueValidator`:驗證最大值。

2. `MinValueValidator`:驗證最小值。

3. `MinLengthValidator`:驗證最小長度。

4. `MaxLengthValidator`:驗證最大長度。

5. `EmailValidator`:驗證是否是郵箱格式。

6. `URLValidator`:驗證是否是`URL`格式。

7. ```    RegexValidator    ```

   :如果還需要更加複雜的驗證,那麼我們可以通過正則表示式的驗證器:

   ```    RegexValidator    ```

   。比如現在要驗證手機號碼是否合格,那麼我們可以通過以下程式碼實現:

   ```python     class MyForm(forms.Form):         telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='請輸入正確格式的手機號碼!')])    ```

注意變數的統一  

``` forms.py      telephone = forms.CharField(validators=[validators.RegexValidator(r'1[3456789]\d{9}',message="請輸入正確的手機號")],error_messages={"required":"請輸入您的手機號"})      views.py      def post(self,request):         form = MyForm(request.POST)         if form.is_valid():             telephone = form.cleaned_data.get('telephone')             return HttpResponse("成功")         else:             print(form.errors.get_json_data())             return HttpResponse('fail')  index.html         <input type="text" name="telephone">                  三個telephone 必須要 統一    ```

## 表單初步實現示例程式碼

~~~ app:form views.py       from django.shortcuts import render       from django.views.generic import View       from .forms import MessageBoardForm       from django.http import HttpResponse       from django.forms.utils import ErrorDict       class IndexView(View):           def get(self,request):               form = MessageBoardForm( )               return render(request,'index.html',context={"form":form})           def post(self,request):               form = MessageBoardForm(request.POST)               if form.is_valid():                   title = form.cleaned_data.get('title')                   content = form.cleaned_data.get('content')                   email = form.cleaned_data.get('email')                   reply = form.cleaned_data.get('reply')                   print('-------------------------')                   print(title)                   print(content)                   print(email)                   print(reply)                   print('-------------------------')                   return HttpResponse("提交成功")               else:                   print(form.errors.get_json_data())                   return HttpResponse("提交失敗") forms.py     from django import forms     from django.core import validators

    class MessageBoardForm(forms.Form):         title = forms.CharField(max_length=100,min_length=2,label="標題",error_messages={"min_length":"至少寫入兩個字元"})         content = forms.CharField(widget=forms.Textarea,label="內容",error_messages={"required":"必須寫入內容"})         email = forms.EmailField(label="郵箱",error_messages={"invalid":"清時輸入正確的郵箱"})         reply = forms.BooleanField(required=False,label="是否回覆")

index.html     <form action="" method="post">         <table>             {{ form.as_table }}             <tr>                 <td><input type="submit" value="提交"></td>             </tr>         </table>     </form>

~~~

## 表單驗證示例程式碼

~~~ app:front views.py     from django.shortcuts import render     from django.views.generic import View     from .forms import MyForm     from .models import User     from django.http import HttpResponse     class IndexView(View):         def get(self,request):             return render(request,'index.html')         def post(self,request):             form = MyForm(request.POST)             if form.is_valid():                 telephone = form.cleaned_data.get('telephone')                 return HttpResponse("成功")             else:                 print(form.errors.get_json_data())                 return HttpResponse("fail") forms.py from django import forms from django.core import validators class MyForm(forms.Form):     # email = forms.EmailField(error_messages={"required":"請輸入正確的郵箱"})     # price = forms.FloatField(error_messages={"required":"請輸入浮點型別"})     # urls = forms.URLField(error_messages={"required":"請輸入正確的urls"})     telephone = forms.CharField(validators=[validators.RegexValidator(r'1[3456789]\d{9}',message="請輸入正確的手機號")],error_messages={"required":"請輸入正確的手機號"})

index.html     <form action="" method="post">         <input type="text" name="'telephone">         <input type="submit" value="提交">     </form>      models.py     from django.db import models     class User(models.Model):         name = models.CharField(max_length=100)         phone = models.CharField(max_length=11) ~~~

## 自定義驗證:

有時候對一個欄位驗證,不是一個長度,一個正則表示式能夠寫清楚的,還需要一些其他複雜的邏輯,那麼我們可以對某個欄位,進行自定義的驗證。比如在註冊的表單驗證中,我們想要驗證手機號碼是否已經被註冊過了,那麼這時候就需要在資料庫中進行判斷才知道。對某個欄位進行自定義的驗證方式是,定義一個方法,這個方法的名字定義規則是:`clean_fieldname`。如果驗證失敗,那麼就丟擲一個驗證錯誤。比如要驗證使用者表中手機號碼之前是否在資料庫中存在,那麼可以通過以下程式碼實現:

```python class MyForm(forms.Form):     telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='請輸入正確格式的手機號碼!')])

    def clean_telephone(self):         telephone = self.cleaned_data.get('telephone')         exists = User.objects.filter(telephone=telephone).exists()         if exists:             raise forms.ValidationError("手機號碼已經存在!")         return telephone ```

以上是對某個欄位進行驗證,如果驗證資料的時候,需要針對多個欄位進行驗證,那麼可以重寫`clean`方法。比如要在註冊的時候,要判斷提交的兩個密碼是否相等。那麼可以使用以下程式碼來完成:

```python class MyForm(forms.Form):     telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='請輸入正確格式的手機號碼!')])     pwd1 = forms.CharField(max_length=12)     pwd2 = forms.CharField(max_length=12)

    def clean(self):         cleaned_data = super().clean()         pwd1 = cleaned_data.get('pwd1')         pwd2 = cleaned_data.get('pwd2')         if pwd1 != pwd2:             raise forms.ValidationError('兩個密碼不一致!') ```