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)訪問地址:http://127.0.0.1:8000/userfavs/

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

(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_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)- 返回分頁樣式Response對(duì)象慷暂。
  • paginate_queryset(self, queryset)- 如果需要,可以分頁查詢集晨雳,返回頁面對(duì)象行瑞,或者None如果沒有為此視圖配置分頁。
  • filter_queryset(self, queryset) - 給定一個(gè)查詢集餐禁,使用正在使用的過濾后端進(jìn)行過濾血久,返回一個(gè)新的查詢集。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帮非,一起剝皮案震驚了整個(gè)濱河市氧吐,隨后出現(xiàn)的幾起案子讹蘑,更是在濱河造成了極大的恐慌,老刑警劉巖筑舅,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件座慰,死亡現(xiàn)場離奇詭異,居然都是意外死亡翠拣,警方通過查閱死者的電腦和手機(jī)版仔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來误墓,“玉大人蛮粮,你說我怎么就攤上這事∶栈牛” “怎么了然想?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長欣范。 經(jīng)常有香客問我又沾,道長,這世上最難降的妖魔是什么熙卡? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任杖刷,我火速辦了婚禮,結(jié)果婚禮上驳癌,老公的妹妹穿的比我還像新娘滑燃。我一直安慰自己,他們只是感情好颓鲜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布表窘。 她就那樣靜靜地躺著,像睡著了一般甜滨。 火紅的嫁衣襯著肌膚如雪乐严。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天衣摩,我揣著相機(jī)與錄音昂验,去河邊找鬼。 笑死艾扮,一個(gè)胖子當(dāng)著我的面吹牛既琴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泡嘴,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼甫恩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酌予?” 一聲冷哼從身側(cè)響起磺箕,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤奖慌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后松靡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體简僧,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年击困,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涎劈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片广凸。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阅茶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谅海,到底是詐尸還是另有隱情脸哀,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布扭吁,位于F島的核電站撞蜂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侥袜。R本人自食惡果不足惜蝌诡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枫吧。 院中可真熱鬧浦旱,春花似錦、人聲如沸九杂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽例隆。三九已至甥捺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镀层,已是汗流浹背镰禾。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唱逢,地道東北人羡微。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像惶我,于是被迫代替她去往敵國和親妈倔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容