用戶收藏屬于用戶操作的功能揽碘,所以我們將在user_operation中進(jìn)行操作
(1)新建一個(gè)配套的Serializers.py(對(duì)UserFav進(jìn)行序列化操作)
#user_operation/serializers.py
from rest_framework import serializers
from .models import UserFav
from rest_framework.validators import UniqueTogetherValidator
class UserFavSerializers(serializers.ModelSerializer):
# 獲取當(dāng)前登錄的用戶
user = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
class Meta:
model = UserFav
# validate實(shí)現(xiàn)唯一聯(lián)合念恍,一個(gè)商品只能收藏一次
validators = [
UniqueTogetherValidator(
queryset=UserFav.objects.all(),
fields=('user', 'goods'),
# message的信息可以自定義
message = "已經(jīng)收藏"
)
]
# 收藏的時(shí)候需要返回商品的id已卸,因?yàn)槿∠詹氐臅r(shí)候必須知道商品的id是多少
fields = ('user','goods','id')
(2)收藏的接口既要繼承viewset囚霸,添加收藏create,刪除收藏destroy巢音,收藏列表list
#user_operation/views.py
from rest_framework import mixins,viewsets
from .models import UserFav
from .serializers import UserFavSerializers
from rest_framework.permissions import IsAuthenticated
class UserFavViewSet(mixins.CreateModelMixin,mixins.ListModelMixin,mixins.DestroyModelMixin, viewsets.GenericViewSet):
"""
用戶收藏
"""
queryset = UserFav.objects.all()
permission_classes = (IsAuthenticated,)
serializer_class = UserFavSerializers
(3)配置用戶收藏的URL
#urls.py
router.register(r'userfavs', UserFavViewSet, base_name="userfavs")
(4)訪問地址:http://127.0.0.1:8000/userfavs/
未登錄截圖
登陸收藏三個(gè)商品遵倦,查看已收藏列表截圖
重復(fù)收藏提示“已經(jīng)收藏”
(5)drf的權(quán)限認(rèn)證
這樣看起來已經(jīng)完成了用戶添加、刪除收藏的功能官撼。但還需要保證用戶只能刪除自己的收藏梧躺。
“auth” 和 “permission”是兩種東西。auth是用來做用戶驗(yàn)證的歧寺,permission是用來做權(quán)限判斷的燥狰。
AllowAny 將允許不受限制的訪問
IsAuthenticated 將允許登陸用戶進(jìn)行訪問
IsAdminUser 將允許管理員進(jìn)行訪問(user.is_staff是True)
IsAuthenticatedOrReadOnly將允許匿名用戶具有讀取權(quán)限棘脐,身份驗(yàn)證的用戶具有寫入權(quán)限
首先斜筐,判斷是否登陸(在#user_operation/views.py已體現(xiàn))
from rest_framework.permissions import IsAuthenticated
permission_classes = (IsAuthenticated,)
其次,判斷是否IsOwnerOrReadOnly
django rest framwork官網(wǎng)已給出例子
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
對(duì)象級(jí)別權(quán)限只允許對(duì)象的所有者進(jìn)行編輯
Assumes the model instance has an `owner` attribute.
假設(shè)模型實(shí)例有一個(gè)“owner”屬性蛀缝。
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
#對(duì)任何請(qǐng)求都允許讀取權(quán)限
# so we'll always allow GET, HEAD or OPTIONS requests.
#所以我們總是允許GET, HEAD or OPTIONS請(qǐng)求顷链。
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
#實(shí)例必須有一個(gè)名為“owner”的屬性。
return obj.owner == request.user
在utils中新建permissions屈梁,然后粘貼上面的IsOwnerOrReadOnly,并將“owner”改成“user”嗤练,這是我們自定義的permissions榛了。
# utils/permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
#obj相當(dāng)于數(shù)據(jù)庫中的model,這里要把owner改為我們數(shù)據(jù)庫中的user
return obj.user == request.user
自定義的permission類IsOwnerOrReadOnly繼承了我們的BasePermission煞抬。
方法has_object_permission霜大,是否有對(duì)象權(quán)限。會(huì)檢測我們從數(shù)據(jù)庫中拿出來的obj的user是否等于request.user
views中添加該自定義的權(quán)限認(rèn)證類
permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)
這樣在做刪除的時(shí)候就會(huì)驗(yàn)證權(quán)限革答。
重載get_queryset方法
只能查看當(dāng)前登錄用戶的收藏战坤,不會(huì)獲取所有用戶的收藏。因此我們要重載get_queryset方法
def get_queryset(self):
#只能查看當(dāng)前登錄用戶的收藏残拐,不會(huì)獲取所有用戶的收藏
return UserFav.objects.filter(user=self.request.user)
token認(rèn)證配置到view中
使用其他工具時(shí)(postman)輸入用戶名密碼也可以進(jìn)行登錄途茫,是因?yàn)槲覀兣渲昧硕喾Nauth類。
token的認(rèn)證一般配置到view中溪食,而不是配置到全局中囊卜。
前端的每一個(gè)request請(qǐng)求都加入我們的token的話,token過期了错沃,當(dāng)用戶訪問goods列表頁等不需要token認(rèn)證的頁面也會(huì)拿不到數(shù)據(jù)栅组。
將setting中的 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',刪除掉。
然后在具體的view中來import以及進(jìn)行配置
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
authentication_classes = (JSONWebTokenAuthentication, )
此時(shí)在我們的api控制臺(tái)以及無法使用登錄進(jìn)入userfav了枢析,是因?yàn)槲覀兊念悆?nèi)auth并不包含session auth
from rest_framework.authentication import SessionAuthentication
#auth使用來做用戶認(rèn)證的
authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication)
user_operaton/views.py中的代碼
# user_operaton/views.py
from rest_framework import viewsets
from rest_framework import mixins
from .models import UserFav
from .serializers import UserFavSerializer
from rest_framework.permissions import IsAuthenticated
from utils.permissions import IsOwnerOrReadOnly
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.authentication import SessionAuthentication
class UserFavViewset(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin):
'''
用戶收藏
'''
serializer_class = UserFavSerializer
#permission是用來做權(quán)限判斷的
# IsAuthenticated:必須登錄用戶笑窜;IsOwnerOrReadOnly:必須是當(dāng)前登錄的用戶
permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)
#auth使用來做用戶認(rèn)證的
authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication)
#搜索的字段
lookup_field = 'goods_id'
def get_queryset(self):
#只能查看當(dāng)前登錄用戶的收藏,不會(huì)獲取所有用戶的收藏
return UserFav.objects.filter(user=self.request.user)
說明:
<1>只有登錄用戶才可以收藏
<2>用戶只能獲取自己的收藏登疗,不能獲取所有用戶的收藏
<3>JSONWebTokenAuthentication認(rèn)證不應(yīng)該全局配置排截,因?yàn)橛脩臬@取商品信息或者其它頁面的時(shí)候并不需要此認(rèn)證,所以這個(gè)認(rèn)證只要局部中添加就可以
<4>刪除settings中的
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
(5)API參考
GenericAPIView
此類擴(kuò)展了REST框架的APIView
類辐益,為標(biāo)準(zhǔn)列表和詳細(xì)信息視圖添加了常用的行為断傲。
提供的每個(gè)具體通用視圖是通過GenericAPIView
與一個(gè)或多個(gè)mixin類組合而構(gòu)建的。
屬性
基本設(shè)置:
以下屬性控制基本視圖行為智政。
-
queryset
- 應(yīng)該用于從此視圖返回對(duì)象的查詢集认罩。通常,您必須設(shè)置此屬性续捂,或覆蓋該get_queryset()
方法垦垂。如果要覆蓋視圖方法,則必須調(diào)用get_queryset()
而不是直接訪問此屬性牙瓢,因?yàn)?code>queryset將進(jìn)行一次評(píng)估劫拗,并且將為所有后續(xù)請(qǐng)求緩存這些結(jié)果。 -
serializer_class
- 應(yīng)該用于驗(yàn)證和反序列化輸入以及序列化輸出的序列化程序類矾克。通常页慷,您必須設(shè)置此屬性,或覆蓋該get_serializer_class()
方法。 -
lookup_field
- 應(yīng)用于執(zhí)行單個(gè)模型實(shí)例的對(duì)象查找的模型字段酒繁。默認(rèn)為'pk'
滓彰。請(qǐng)注意,使用超鏈接的API時(shí)州袒,您需要確保雙方的API意見和串行類設(shè)置查找字段揭绑,如果你需要使用一個(gè)自定義值。 -
lookup_url_kwarg
- 應(yīng)該用于對(duì)象查找的URL關(guān)鍵字參數(shù)郎哭。URL conf應(yīng)包含與此值對(duì)應(yīng)的關(guān)鍵字參數(shù)洗做。如果未設(shè)置,則默認(rèn)使用相同的值lookup_field
彰居。
分頁:
與列表視圖一起使用時(shí)诚纸,以下屬性用于控制分頁。
-
pagination_class
- 分頁列表結(jié)果時(shí)應(yīng)使用的分頁類陈惰。默認(rèn)為與DEFAULT_PAGINATION_CLASS
設(shè)置相同的值畦徘,即'rest_framework.pagination.PageNumberPagination'
。設(shè)置pagination_class=None
將禁用此視圖上的分頁抬闯。
過濾:
-
filter_backends
- 應(yīng)該用于過濾查詢集的過濾器后端類列表井辆。默認(rèn)值與DEFAULT_FILTER_BACKENDS
設(shè)置相同。
方法
基本方法:
get_queryset(self)
返回應(yīng)該用于列表視圖的查詢集溶握,該查詢集應(yīng)該用作詳細(xì)視圖中查找的基礎(chǔ)杯缺。默認(rèn)返回queryset
屬性指定的查詢集。
應(yīng)始終使用此方法而不是self.queryset
直接訪問睡榆,因?yàn)?code>self.queryset只進(jìn)行一次評(píng)估萍肆,并為所有后續(xù)請(qǐng)求緩存這些結(jié)果。
可以重寫以提供動(dòng)態(tài)行為胀屿,例如返回查詢集塘揣,該查詢集特定于發(fā)出請(qǐng)求的用戶。
例如:
def get_queryset(self):
user = self.request.user
return user.accounts.all()
get_object(self)
返回應(yīng)該用于詳細(xì)視圖的對(duì)象實(shí)例宿崭。默認(rèn)使用lookup_field
參數(shù)來過濾基本查詢集亲铡。
可以重寫以提供更復(fù)雜的行為,例如基于多個(gè)URL kwarg的對(duì)象查找葡兑。
例如:
def get_object(self):
queryset = self.get_queryset()
filter = {}
for field in self.multiple_lookup_fields:
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter)
self.check_object_permissions(self.request, obj)
return obj
請(qǐng)注意奖蔓,如果您的API不包含任何對(duì)象級(jí)別權(quán)限,您可以選擇性地排除self.check_object_permissions
讹堤,并簡單地從get_object_or_404
查找中返回該對(duì)象吆鹤。
filter_queryset(self, queryset)
給定一個(gè)查詢集,使用正在使用的任何過濾后端過濾它蜕劝,返回一個(gè)新的查詢集檀头。
例如:
def filter_queryset(self, queryset):
filter_backends = (CategoryFilter,)
if 'geo_route' in self.request.query_params:
filter_backends = (GeoRouteFilter, CategoryFilter)
elif 'geo_point' in self.request.query_params:
filter_backends = (GeoPointFilter, CategoryFilter)
for backend in list(filter_backends):
queryset = backend().filter_queryset(self.request, queryset, view=self)
return queryset
get_serializer_class(self)
返回應(yīng)該用于序列化程序的類。默認(rèn)返回serializer_class
屬性岖沛。
可以重寫以提供動(dòng)態(tài)行為暑始,例如使用不同的序列化程序進(jìn)行讀寫操作,或者為不同類型的用戶提供不同的序列化程序婴削。
例如:
def get_serializer_class(self):
if self.request.user.is_staff:
return FullAccountSerializer
return BasicAccountSerializer
保存和刪除掛鉤:
mixin類提供了以下方法廊镜,并提供了對(duì)象保存或刪除行為的輕松覆蓋。
-
perform_create(self, serializer)
-CreateModelMixin
保存新對(duì)象實(shí)例時(shí)調(diào)用唉俗。 -
perform_update(self, serializer)
-UpdateModelMixin
保存現(xiàn)有對(duì)象實(shí)例時(shí)調(diào)用嗤朴。 -
perform_destroy(self, instance)
-DestroyModelMixin
刪除對(duì)象實(shí)例時(shí)調(diào)用。
這些掛鉤對(duì)于設(shè)置請(qǐng)求中隱含的屬性特別有用虫溜,但不是請(qǐng)求數(shù)據(jù)的一部分雹姊。例如,您可以根據(jù)請(qǐng)求用戶或基于URL關(guān)鍵字參數(shù)在對(duì)象上設(shè)置屬性衡楞。
def perform_create(self, serializer):
serializer.save(user=self.request.user)
這些覆蓋點(diǎn)對(duì)于添加在保存對(duì)象之前或之后發(fā)生的行為(例如通過電子郵件發(fā)送確認(rèn)或記錄更新)也特別有用吱雏。
def perform_update(self, serializer):
instance = serializer.save()
send_email_confirmation(user=self.request.user, modified=instance)
你也可以使用這些鉤子來提供額外的驗(yàn)證,通過提高ValidationError()
瘾境。如果您需要在數(shù)據(jù)庫保存點(diǎn)應(yīng)用某些驗(yàn)證邏輯歧杏,這可能很有用。例如:
def perform_create(self, serializer):
queryset = SignupRequest.objects.filter(user=self.request.user)
if queryset.exists():
raise ValidationError('You have already signed up')
serializer.save(user=self.request.user)
注意:這些方法取代舊式的2.x版pre_save
迷守,post_save
犬绒,pre_delete
和post_delete
方法,這將不再可用兑凿。
其他方法:
您通常不需要覆蓋以下方法凯力,但如果您正在使用編寫自定義視圖,則可能需要調(diào)用它們GenericAPIView
礼华。
-
get_serializer_context(self)
- 返回包含應(yīng)提供給序列化程序的任何額外上下文的字典沮协。默認(rèn)為包括'request'
,'view'
和'format'
鑰匙卓嫂。 -
get_serializer(self, instance=None, data=None, many=False, partial=False)
- 返回一個(gè)序列化程序?qū)嵗?/li> -
get_paginated_response(self, data)
- 返回分頁樣式Response
對(duì)象慷暂。 -
paginate_queryset(self, queryset)
- 如果需要,可以分頁查詢集晨雳,返回頁面對(duì)象行瑞,或者None
如果沒有為此視圖配置分頁。 -
filter_queryset(self, queryset)
- 給定一個(gè)查詢集餐禁,使用正在使用的過濾后端進(jìn)行過濾血久,返回一個(gè)新的查詢集。