3.用戶收藏接口實(shí)現(xiàn)

用戶收藏屬于用戶操作的功能,所以我們將在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)訪問(wèn)地址:http://127.0.0.1:8000/userfavs/

未登錄截圖
image.png
登陸收藏三個(gè)商品,查看已收藏列表截圖
image.png
重復(fù)收藏提示“已經(jīng)收藏”
image.png

(5)drf的權(quán)限認(rèn)證
這樣看起來(lái)已經(jīng)完成了用戶添加、刪除收藏的功能。但還需要保證用戶只能刪除自己的收藏。
“auth” 和 “permission”是兩種東西。auth是用來(lái)做用戶驗(yàn)證的,permission是用來(lái)做權(quán)限判斷的。

AllowAny 將允許不受限制的訪問(wèn)
IsAuthenticated 將允許登陸用戶進(jìn)行訪問(wèn)
IsAdminUser 將允許管理員進(jìn)行訪問(wè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ù)庫(kù)中的model,這里要把owner改為我們數(shù)據(jù)庫(kù)中的user
        return obj.user == request.user

自定義的permission類IsOwnerOrReadOnly繼承了我們的BasePermission。
方法has_object_permission,是否有對(duì)象權(quán)限。會(huì)檢測(cè)我們從數(shù)據(jù)庫(kù)中拿出來(lái)的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過(guò)期了,當(dāng)用戶訪問(wèn)goods列表頁(yè)等不需要token認(rèn)證的頁(yè)面也會(huì)拿不到數(shù)據(jù)。

將setting中的 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',刪除掉。

然后在具體的view中來(lái)import以及進(jìn)行配置

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
authentication_classes = (JSONWebTokenAuthentication, )

此時(shí)在我們的api控制臺(tái)以及無(wú)法使用登錄進(jìn)入userfav了,是因?yàn)槲覀兊念悆?nèi)auth并不包含session auth

from rest_framework.authentication import SessionAuthentication
    #auth使用來(lái)做用戶認(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是用來(lái)做權(quán)限判斷的
    # IsAuthenticated:必須登錄用戶;IsOwnerOrReadOnly:必須是當(dāng)前登錄的用戶
    permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)
#auth使用來(lái)做用戶認(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)
說(shuō)明:
<1>只有登錄用戶才可以收藏
<2>用戶只能獲取自己的收藏,不能獲取所有用戶的收藏
<3>JSONWebTokenAuthentication認(rèn)證不應(yīng)該全局配置,因?yàn)橛脩臬@取商品信息或者其它頁(yè)面的時(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è)具體通用視圖是通過(guò)GenericAPIView與一個(gè)或多個(gè)mixin類組合而構(gòu)建的。

屬性

基本設(shè)置

以下屬性控制基本視圖行為。

  • queryset - 應(yīng)該用于從此視圖返回對(duì)象的查詢集。通常,您必須設(shè)置此屬性,或覆蓋該get_queryset()方法。如果要覆蓋視圖方法,則必須調(diào)用get_queryset()而不是直接訪問(wèn)此屬性,因?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意見(jiàn)串行類設(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。

分頁(yè)

與列表視圖一起使用時(shí),以下屬性用于控制分頁(yè)。

  • pagination_class - 分頁(yè)列表結(jié)果時(shí)應(yīng)使用的分頁(yè)類。默認(rèn)為與DEFAULT_PAGINATION_CLASS設(shè)置相同的值,即'rest_framework.pagination.PageNumberPagination'。設(shè)置pagination_class=None將禁用此視圖上的分頁(yè)。

過(guò)濾

  • filter_backends - 應(yīng)該用于過(guò)濾查詢集的過(guò)濾器后端類列表。默認(rèn)值與DEFAULT_FILTER_BACKENDS設(shè)置相同。

方法

基本方法

get_queryset(self)

返回應(yīng)該用于列表視圖的查詢集,該查詢集應(yīng)該用作詳細(xì)視圖中查找的基礎(chǔ)。默認(rèn)返回queryset屬性指定的查詢集。

應(yīng)始終使用此方法而不是self.queryset直接訪問(wèn),因?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ù)來(lái)過(guò)濾基本查詢集。

可以重寫以提供更復(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,并簡(jiǎn)單地從get_object_or_404查找中返回該對(duì)象。

filter_queryset(self, queryset)

給定一個(gè)查詢集,使用正在使用的任何過(guò)濾后端過(guò)濾它,返回一個(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ā)生的行為(例如通過(guò)電子郵件發(fā)送確認(rèn)或記錄更新)也特別有用。

def perform_update(self, serializer):
    instance = serializer.save()
    send_email_confirmation(user=self.request.user, modified=instance)

你也可以使用這些鉤子來(lái)提供額外的驗(yàn)證,通過(guò)提高ValidationError()。如果您需要在數(shù)據(jù)庫(kù)保存點(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_savepre_deletepost_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)- 返回分頁(yè)樣式Response對(duì)象。
  • paginate_queryset(self, queryset)- 如果需要,可以分頁(yè)查詢集,返回頁(yè)面對(duì)象,或者None如果沒(méi)有為此視圖配置分頁(yè)。
  • filter_queryset(self, queryset) - 給定一個(gè)查詢集,使用正在使用的過(guò)濾后端進(jìn)行過(guò)濾,返回一個(gè)新的查詢集。
最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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