20-Django之CBV、序列化、Form表單

一、FBV & CBV

CBV定義

Djanggo中的請(qǐng)求處理方式
FBV: Function Based View
/index/ func(request)

CBV: Class Based View
/index/ class(object)--get方法/post方法

View方法

  • View方法支持的http請(qǐng)求:
    ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
  • form表單提交請(qǐng)求:
    'get', 'post'
  • ajax提交請(qǐng)求:
    ['get'獲取, 'post'發(fā)送, 'put'增加, 'patch'增加, 'delete'刪除, 'head', 'options', 'trace']
  • 請(qǐng)求數(shù)據(jù):用url去路由系統(tǒng)中找到對(duì)應(yīng)的類(lèi)/函數(shù)
    • 函數(shù):直接執(zhí)行函數(shù)
    • 類(lèi):類(lèi)名()執(zhí)行構(gòu)造方法初始化-->執(zhí)行類(lèi)中的dispatch()方法-->dispatch()中拿到請(qǐng)求的method進(jìn)行反射

dispatch方法

class A:
    def f1(self):
        self.f2()

class B(A):
    def f2(self):
        print("-->a.f2")
#關(guān)鍵點(diǎn):找到self到底是誰(shuí),就從哪個(gè)self這個(gè)類(lèi)開(kāi)始找,沒(méi)有就往上找
obj = B()
obj.f1()

#報(bào)錯(cuò):A類(lèi)中沒(méi)有f2()方法
obj = A()
obj.f1()
========================
class View:
        def dispatch(self):
            func = getattr(self,'get')
            return func(...)

        def get()
            print(1)
    
class LoginView(View):
        def dispatch(self,request, *args, **kwargs):
            # 可以在執(zhí)行dispatch方法前后統(tǒng)一執(zhí)行一些代碼,起到裝飾器的作用
                print("before")
                response = super(LoginView, self).dispatch(request, *args, **kwargs)
                print("after")
            return response
        
        def get():
            print(2)
#打印結(jié)果為2
obj = LoginView()
obj.dispatch()

對(duì)于super(B, self).dispatch()可以這樣理解:super(B, self)首先找到B的父類(lèi)(就是類(lèi)A),然后把類(lèi)B的對(duì)象self轉(zhuǎn)換為類(lèi)A的對(duì)象,然后“被轉(zhuǎn)換”的類(lèi)A對(duì)象,先調(diào)用類(lèi)B中的方法,如果沒(méi)有,就向上調(diào)用類(lèi)A中的方法。

應(yīng)用場(chǎng)景:登錄驗(yàn)證

  • 繼承
    • 單繼承
    • 多繼承
  • 裝飾器:@method_decorator(auth, name='post')
    #單繼承方式
    class BaseView(View):
        def dispatch(self, request, *args, **kwargs):
            if request.session.get('username'):
                response = super(BaseView,self).dispatch(request, *args, **kwargs)
                return response
            else:
                return redirect('/login.html')

    class IndexView(BaseView):
        def get(self, request, *args, **kwargs):
            return HttpResponse('Welcome! '+request.session.get('username'))

    #多繼承方式
    class BaseView(object):
        def dispatch(self, request, *args, **kwargs):
            if request.session.get('username'):
                response = super(BaseView,self).dispatch(request, *args, **kwargs)
                return response
            else:
                return redirect('/login.html')
    
    class IndexView(BaseView, View):
        def get(self, request, *args, **kwargs):
            return HttpResponse('Welcome! '+request.session.get('username'))
    
    #裝飾器
    from django.utils.decorators import method_decorator

    def auth(func):
        def inner(request, *args, **kwargs):
            if request.session.get('username'):
                obj = func(request, *args, **kwargs)
                return obj
            else:
                return redirect('/login.html')
        return inner
    
    @method_decorator(auth,name='get')
    class IndexView(View):
        # @method_decorator(auth)
        def get(self, request, *args, **kwargs):
            return HttpResponse('Welcome! ' + request.session.get('username'))

CBV中csrf的一個(gè)小bug

@method_decorator(csrf_exempt/protect)放在類(lèi)或者對(duì)應(yīng)方法上面時(shí),不起作用,目前只能放在dispatch方法上面(對(duì)該類(lèi)下的所有方法起作用)

from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.utils.decorators import method_decorator
class LoginView(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        response = super(LoginView, self).dispatch(request, *args, **kwargs)
        return response

    def get(self, request, *args, **kwargs):    
        return render(request, 'login.html')

    def post(self, request, *args, **kwargs):
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'amy' and pwd == '123':
            request.session['username']=user
            return redirect('/index.html')
        return render(request, 'login.html', {'msg':'輸入錯(cuò)誤,請(qǐng)重新輸入!'})

二、序列化

后端向前端傳送數(shù)據(jù)

傳統(tǒng)方式

def users(request):
    user_list = models.UserInfo.objects.all()
    return render(request, 'users.html', {'user_list':user_list})

方式1:序列化

serializers.serialize(),不推薦使用(數(shù)據(jù)過(guò)多)
def users(request):
return render(request, 'users.html')

from django.core import serializers
def get_users(request):
    user_list = models.UserInfo.objects.all()
    data = serializers.serialize('json',user_list) #將queryset格式轉(zhuǎn)化為json
    return HttpResponse(data)

方式2:json.dumps()

def get_users(request):
    user_list = models.UserInfo.objects.values()
    user_list = list(user_list)
    data = json.dumps(user_list)
    return HttpResponse(data)

問(wèn)題:對(duì)json.dumps做定制化

json.dumps()的局限性:只能處理python的數(shù)據(jù)類(lèi)型,而對(duì)于date/datetime等類(lèi)型就會(huì)報(bào)錯(cuò)
解決辦法:

前提:取數(shù)據(jù)時(shí)用values()或者values_list(),保證取到的數(shù)據(jù)是json認(rèn)識(shí)的類(lèi)型

from django.test import TestCase
import json
from datetime import date
from datetime import datetime

class JsonCustomEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return o.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(o, date):
            return o.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, o)

user_list = [
    {'id':1,'user':'amy','ctime':datetime.now()},
    {'id':1,'user':'bulk','ctime':datetime.now()},
]

data = json.dumps(user_list,cls=JsonCustomEncoder)

前端展示

<body>
<ul id="user_list_id">
</ul>

<script src="/static/jquery-3.2.1.js"></script>
<script>
function initData() {
    $.ajax({
        url:'/get_users.html',
        type:'GET', // get大小寫(xiě)都可以
        dataType:'JSON', // 等價(jià)于arg = JSON.parse(arg)
        sucess:function (arg) {
            if (arg.status){
                /*
                    arg.data = [
                        {id: xxx,username: xxx}
                    ]
                */
                $.each(arg.data, function (index,row) {
                    // row = {id: xxx,username: xxx}
                    var tag = '<li>' +row.username + '</li>';
                    $('#user_list_id').append(tag);
                })

            }else {
                alert(arg.msg);
            }
        }
    })
}
</body>

三、Form表單驗(yàn)證

功能:用戶(hù)請(qǐng)求驗(yàn)證+生成html標(biāo)簽

示例:用戶(hù)管理

添加用戶(hù)頁(yè)面

  • 顯示HTML標(biāo)簽
  • 提交:數(shù)據(jù)驗(yàn)證
  • 成功之后保存
  • 錯(cuò)誤顯示錯(cuò)誤信息

步驟:

  1. 創(chuàng)建Form類(lèi)(本質(zhì)就是正則表達(dá)式的集合)

     class MyForm(Form)
         username = fields.CharField()
         email = fields.EmailField()
         ut_id = fields.ChoicesField()
         role_id = fields.MultipleChoiceField()
             ip = fields.GenericIPAddressField(protocol='ipv4')
         
     #用戶(hù)表
     class UserForm(Form):
         #Form第一次加載的時(shí)候,會(huì)深拷貝所有字段放到fields中,方便以后調(diào)用
         username = fields.CharField(
             required=True,
             error_messages={'required':'用戶(hù)名不能為空!'},
             widget = widgets.TextInput(attrs={'class':'form-control'})
         )
         password = fields.CharField(
             required=True,
             error_messages={'required':'密碼不能為空!'},
             widget= widgets.TextInput(attrs={'class':'form-control'})
         )
         ut_id = fields.ChoiceField(
             choices = [],
             widget= widgets.Select(attrs={'class':'form-control'})
         )
         role_id = fields.MultipleChoiceField(
             choices= [],
             widget = widgets.SelectMultiple(attrs={'class':'form-control'})
         )
     
         def __init__(self, *args, **kwargs):
             super(UserForm,self).__init__(*args, **kwargs)
             #self.fields中已經(jīng)有所有拷貝的字段,每次實(shí)例化時(shí)重新賦值
             self.fields['ut_id'].choices = models.UserType.objects.values_list('id','title')
             self.fields['role_id'].choices = models.UserRole.objects.values_list('id', 'caption')
    
  2. 只是生成HTML標(biāo)簽:添加頁(yè)面

     form = MyForm()
     {{form.字段名}}
    
  3. 帶默認(rèn)值的HTML標(biāo)簽:編輯頁(yè)面

     form = MyForm(initial={'Form字段名':obj.數(shù)據(jù)庫(kù)字段名)
     {{form.字段名}}
    
  4. 提交數(shù)據(jù)

     form = MyForm(data=request.POST)
     if form.is_valid():
         print(form.cleaned_data)
     else:
         print(form.errors)
    
  5. 數(shù)據(jù)驗(yàn)證

    1. 錯(cuò)誤提示信息在UserForm類(lèi)中設(shè)置
    2. cleaned_data格式:{'ut_id': '1', 'username': 'd', 'email': 'd@123.com', 'ip': '1.1.1.1'},來(lái)源于前端對(duì)應(yīng)標(biāo)簽中的name屬性-->進(jìn)一步來(lái)源于UserForm類(lèi)
    3. 可以通過(guò)models.UserInfo.objects.create(**form.cleaned_data)將POST數(shù)據(jù)保存到數(shù)據(jù)庫(kù),前提是:創(chuàng)建Form表單類(lèi)的字段與數(shù)據(jù)庫(kù)中的對(duì)應(yīng)字段保持一致
  6. 問(wèn)題:下拉框數(shù)據(jù)無(wú)法實(shí)時(shí)更新-->重寫(xiě)Form表單的init方法(見(jiàn)步驟1)

    • new方法中,循環(huán)出所有的Form表單字段。
    • init方法中,通過(guò)self.fields = copy.deepcopy(self.base_fields)將Form表單深拷貝放在fields中

    靜態(tài)字段只執(zhí)行一次,__init(self)中的數(shù)據(jù)在每次實(shí)例化對(duì)象的時(shí)候執(zhí)行一次。
    NewForm()先執(zhí)行new(),再執(zhí)行init()

一個(gè)bug

ut_id = fields.ChoiceField(choices=models.UserType.objects.values_list('id','title'))
取值為空
進(jìn)一步:
user_type = models.UserType.objects.values_list('id','title')
print(user_type) #

四、Form多對(duì)多操作

添加頁(yè)面

注意:POST操作中,要先把m2m字段pop出來(lái),再通過(guò)m2m字段關(guān)聯(lián)到第三張表進(jìn)行添加

class AddUserView(AuthView, View):
    def get(self, request, *args, **kwargs):
        form = UserForm()
        return render(request, 'adduser.html',{'form':form})

    def post(self, request, *args, **kwargs):
        form = UserForm(data=request.POST)
        #將用戶(hù)提交的數(shù)據(jù)和UserForm中定義的規(guī)則進(jìn)行匹配
        if form.is_valid():
            # {'role_id': ['1', '4'], 'password': '123', 'ut_id': '1', 'username': 'frank'}
            role_id_list = form.cleaned_data.pop('role_id') #['1', '4']
            obj = models.UserInfo.objects.create(**form.cleaned_data)
            obj.role.add(*role_id_list)
            return redirect('/users.html')
        else:
            print(form.errors)
            return render(request, 'adduser.html', {'form':form})

編輯頁(yè)面,顯示默認(rèn)值

注意:有的用戶(hù)沒(méi)有角色,需要通過(guò)三元表達(dá)式判斷后再賦值

class EditUserView(AuthView, View):
    def get(self, request, pk):
        # 獲取當(dāng)前編輯對(duì)象
        obj = models.UserInfo.objects.filter(id=pk).first()
        # <QuerySet [(1,), (4,)]>。沒(méi)有設(shè)置角色的用戶(hù)<QuerySet []>
        role_list = obj.role.values_list('id')

        #為真時(shí)的結(jié)果 if 判定條件 else 為假時(shí)的結(jié)果
        # v = list(zip(*role_list))[0] if role_list else []
        #判定條件 and 為真時(shí)的結(jié)果 or 為假時(shí)的結(jié)果
        v = role_list and list(zip(*role_list))[0] or []

        #獲取Form表單的默認(rèn)值
        form = UserForm(initial={'username':obj.username, 'password':obj.password, 'ut_id':obj.ut_id, 'role_id':v})
        return render(request, 'edituser.html',{'form':form})

    def post(self, request, pk):
        form = UserForm(data=request.POST)
        if form.is_valid():
            #用戶(hù)表更新
            # {'role_id': ['1', '4'], 'password': '123', 'ut_id': '1', 'username': 'frank'}
            role_id = form.cleaned_data.pop('role_id')
            query = models.UserInfo.objects.filter(id=pk)
            query.update(**form.cleaned_data)
            obj = query.first()
            obj.role.set(role_id)
            return redirect('/users.html')
        else:
            print(form.errors)
            return render(request, 'edituser.html', {'form':form})

widgets插件,自定義組件樣式

四、Form組件和Ajax組合示例

應(yīng)用場(chǎng)景

  • 頁(yè)面模態(tài)對(duì)話(huà)框:添加/刪除/編輯 -->適用:個(gè)數(shù)少、內(nèi)容少

    • 前端用ajax提交數(shù)據(jù)(無(wú)刷新)+后端用Django的Form組件進(jìn)行驗(yàn)證
    • Form組件驗(yàn)證功能必用,生成HTML可不用
    • Ajax頁(yè)面不刷新,可以手寫(xiě)input框
  • 跳轉(zhuǎn)到新URL的方式:添加/刪除/編輯 --> 適用:數(shù)據(jù)個(gè)數(shù)多、博客

    • Form標(biāo)簽提交(頁(yè)面刷新)+后端用Django的Form組件進(jìn)行驗(yàn)證
    • Form驗(yàn)證功能必用,生成HTML可不用
    • 用Form表單提交數(shù)據(jù)時(shí),input框不能手寫(xiě),否則不能保留已填寫(xiě)的值
  • 個(gè)人使用習(xí)慣

    • 頁(yè)面上的刪除用模態(tài)對(duì)話(huà)框:【是否確認(rèn)刪除?】
    • 添加/修改:用新URL方式
  • 注意:

    • 頁(yè)面只在第一次請(qǐng)求時(shí)渲染一次。
    • Ajax不能識(shí)別redirect,只能接收字符串。只有Form提交時(shí)redirect可用
    • 注冊(cè)成功,通過(guò)JS的location.href進(jìn)行跳轉(zhuǎn)
    • 注冊(cè)失敗,標(biāo)簽后提示錯(cuò)誤信息:each循環(huán)出錯(cuò)誤信息-->創(chuàng)建錯(cuò)誤信息標(biāo)簽放在對(duì)應(yīng)標(biāo)簽后面-->保證每點(diǎn)一次,錯(cuò)誤信息清空

一個(gè)Bug

  • 報(bào)錯(cuò):Uncaught ReferenceError: $ is not defined
  • 報(bào)錯(cuò)語(yǔ)句:$('#register_form .error').remove();
  • 解決:
    1. 靜態(tài)文件設(shè)置寫(xiě)錯(cuò)為STATICFILE_DIRS
      STATIC_URL = '/static/'
      STATICFILES_DIRS = (
      os.path.join(BASE_DIR,'static'),
      )
    2. 要先引用JQuery,再引用javascript
  • 補(bǔ)充
    • STATICFILE_DIRS:自己寫(xiě)代碼時(shí)用,
    • STATIC_ROOT:線上部署時(shí)用,nginx做靜態(tài)文件處理,這時(shí)用STATIC_ROOT指向靜態(tài)文件地址
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • HTML表單 在HTML中,表單是 ... 之間元素的集合,它們?cè)试S訪問(wèn)者輸入文本、選擇選項(xiàng)、操作對(duì)象等等,然后將...
    蘭山小亭閱讀 3,507評(píng)論 2 14
  • 在上一個(gè)章節(jié),我們已經(jīng)創(chuàng)建了一個(gè)基礎(chǔ)的Blog程序。現(xiàn)在我們將使用一些Dajngo高級(jí)功能,去實(shí)現(xiàn)一個(gè)完整的blo...
    金金剛狼閱讀 3,757評(píng)論 1 12
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,555評(píng)論 19 139
  • 你把它放在桌上置于眼前 我將它夾在書(shū)中藏于心底 世上的人差異萬(wàn)萬(wàn)千 不過(guò)用各自的方式做著同一件事情
    星斗思天明閱讀 121評(píng)論 0 1
  • / 有位明迷說(shuō)過(guò),當(dāng)你見(jiàn)過(guò)了無(wú)數(shù)的帥哥之后,你會(huì)發(fā)現(xiàn),原來(lái)心底里最帥的人,還是他。咦~我覺(jué)得這句話(huà)就把黎明的魅力表...
    番薯葉閱讀 295評(píng)論 0 0

友情鏈接更多精彩內(nèi)容