一、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ò)誤信息
步驟:
-
創(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') -
只是生成HTML標(biāo)簽:添加頁(yè)面
form = MyForm() {{form.字段名}} -
帶默認(rèn)值的HTML標(biāo)簽:編輯頁(yè)面
form = MyForm(initial={'Form字段名':obj.數(shù)據(jù)庫(kù)字段名) {{form.字段名}} -
提交數(shù)據(jù)
form = MyForm(data=request.POST) if form.is_valid(): print(form.cleaned_data) else: print(form.errors) -
數(shù)據(jù)驗(yàn)證
- 錯(cuò)誤提示信息在UserForm類(lèi)中設(shè)置
- 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)
- 可以通過(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)字段保持一致
-
問(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();
- 解決:
- 靜態(tài)文件設(shè)置寫(xiě)錯(cuò)為STATICFILE_DIRS
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
) - 要先引用JQuery,再引用javascript
- 靜態(tài)文件設(shè)置寫(xiě)錯(cuò)為STATICFILE_DIRS
- 補(bǔ)充
- STATICFILE_DIRS:自己寫(xiě)代碼時(shí)用,
- STATIC_ROOT:線上部署時(shí)用,nginx做靜態(tài)文件處理,這時(shí)用STATIC_ROOT指向靜態(tài)文件地址