【Django REST framework电商项目笔记】第09章 用户操作功能开发

drf的权限验证

看起来已经完成了用户添加收藏,删除收藏的功能。但是正常的业务逻辑应该是用户只能删除自己的收藏。
http://www.django-rest-framework.org/api-guide/permissions/
auth 和 permission是两种东西。auth是用来做用户验证的,permission是用来做权限判断的。
AllowAny:不管有没有权限都可以访问。
IsAuthenticated:判断是否已经登录
IsAdminUser:判断用户是否是一个管理员。
user.is_staff:第一步判断用户是否登录了。

实现代码:

from rest_framework.permissions import IsAuthenticated
permission_classes = (IsAuthenticated,)

用户未登录访问 userfav 的 list 会给我们抛出401的错误。
官方例子,ip 是否在白名单中

from rest_framework import permissions

class BlacklistPermission(permissions.BasePermission):
    """
    Global permission check for blacklisted IPs.
    """

    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
        return not blacklisted

拿着model做一下过滤实现

官方例子:是否是所有者,否则仅仅可读

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`.
        return obj.owner == request.user

在utils中新建permissions,这是我们自定义的permissions,然后粘贴上面的IsOwnerOrReadOnly

这个自定义的 permission 类继承了我们的BasePermission。它有一个方法叫做has_object_permission,是否有对象权限。

会检测我们从数据库中拿出来的obj的owner是否等于request.user
这个obj是我们数据库中的表,所以这里的owner应该改为我们数据库中的外键user

安全的方法也就是不会对数据库进行变动的方法,总是可以让大家都有权限访问到。

views中添加该自定义的权限认证类

from utils.permissions import IsOwnerOrReadOnly

permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)

这样在做删除的时候就会验证权限。
不能让所有的收藏关系数据都被获取到。因此我们要重载get_queryset方法

    def get_queryset(self):
        return UserFav.objects.filter(user=self.request.user)

重载之后queryset的参数配置就可以注释掉了

在model设计中。我们的str方法返回值为name。这个name是有可能为null的。

在使用其他工具时输入用户名密码也可以进行登录,是因为我们配置了多种auth类。

token的认证最好是配置到view里面去,而不是配置到全局中。

前端的每一个request请求都加入我们的token的话,token过期了,当用户访问goods列表页等不需要token认证的页面也会拿不到数据。

将setting中的’rest_framework.authentication.SessionAuthentication’,删除掉。

然后在具体的view中来import以及进行配置

user_operation/views.py中实现代码:

from rest_framework_jwt.authentication import JSONWebTokenAuthentication

authentication_classes = (JSONWebTokenAuthentication, )

此时在我们的api控制台以及无法使用登录进入userfav了,是因为我们的类内auth并不包含session auth

from rest_framework.authentication import SessionAuthentication

authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)

drf的api文档自动生成

文档的功能其实我们之前已经配置过了。
文档功能的url配置后面是不能加$符号结尾的。

    # DRF自动文档, 方便前后端交互的文档
    url(r'docs/', include_docs_urls(title="DRF SHOP DOCS")),

优点: 全自动生成,文档里面做测试与交互。生成js shell代码段
左侧下方有个source code 可以选择shell JavaScript python
每一个接口会写好示例代码,把需要传递的参数说明清楚。

会将username和password这种参数写好。
根据我们在urls中配置的url自动生成文档。

动态设置Serializer和permission获取用户信息

首先完成的功能,用户个人信息的修改。手机号码是不可编辑的,我们在注册的时候对于手机号码进行了验证。

点击用户资料,首先要将已有资料进行显示,所以要有一个接口请求用户的个人资料

UserViewset 接收post方法时用于用户注册,这时候我们只需要重载retrieveModelMixin

from rest_framework import mixins

class UserViewset(CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):

users/views.py 中实现代码:

    # 重写该方法,不管传什么id,都只返回当前用户
    def get_object(self):
        return self.request.user

restful api 是对资源的操作,用户注册的时候post是对用户资源的一个操作
获取用户信息是getdetail 修改个人信息是update

直接使用UserViewset进行重用。
既然要获取到当前的用户,必须是一种登录的状态。

permission_classes = (permissions.IsAuthenticated, )

访问viewset的方法时,都必须用户登录才可以。
但是create方法用户注册时不可能放在permission验证通过之后的。

这样我们就希望这个permission能在不同的http请求方式面前进行动态的选择
用户注册的时候没有权限,用户在get时有权限验证。

为了解决问题研究源码

rest_framework/views.py

def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        return [permission() for permission in self.permission_classes]

在我们之前配置的permission class里面去遍历,返回permission class的一个实例也就是对象

实际上我们就可以重载这个函数进行返回。问题有来了,我们的post get请求对应的action是什么?

与函数名称是保持一致的。action放在self中,只有viewset是这样的,如果使用的是api view就不会这样了。

def get_permissions(self):
        if self.action == "retrieve":
            return [permissions.IsAuthenticated()]
        elif self.action == "create":
            return []

        return []

retrieve和create进行单独的处理。其他情况均返回空

既然要用户认证,此时我们的auth class被全局删除了之后,在用户注册中还没有配置

    authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication )

用户认证弹出框的模式是basic auth的模式,现在配置的这两种模式实际上是不需要用户输入用户名和密码的。

浏览器中添加session或head中添加token的。

刚才弹出是因为我们在setting中做过的设置

'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )

只返回了用户名和mobile,是因为我们之前设置了Serializer
fields = (“username”, “code”, “mobile”, “password”)

四个字段只有其中的username 和 mobile会显示,因为其他两个字段是writeonly
我们希望获取用户详情的时候,使用一个另外的Serializer

class UserDetailSerializer(serializers.ModelSerializer):
    """
    用户详情序列化
    """
    class Meta:
        model = User
        fields = ("username", "gender", "birthday", "email","mobile")

问题来了,注册时候如果使用userdetailSerializer会导致那些没有的字段验证失败。
我们如何跟permission一样,动态的使用Serializer呢?

Serializer class 位于genericAPIView中

Serializer_class 可以通过直接配置,也可以通过get_Serializer_class函数获取到

所以我们需要动态的选择Serializer就可以重载这个函数就可以了

users/views.py中实现代码:

def get_serializer_class(self):
        if self.action == "retrieve":
            return UserDetailSerializer
        elif self.action == "create":
            return UserRegSerializer

        return UserDetailSerializer

用户个人信息修改

为UserViewset配置update mixins

这里面承载了更新和部分更新的操作。

它接受put和patch请求,put实际是一种更新的操作,patch是部分更新。

用户收藏功能

我们之前在UserFavViewSet中的Listmodelmixin已经将goods的id 和 这条收藏关系的id返回了。但是我们需要的是商品的信息,所以在Serializer中

添加一个Serializers

class UserFavDetailSerializer(serializers.ModelSerializer):

    # 通过goods_id拿到商品信息。就需要嵌套的Serializer
    goods = GoodsSerializer()
    class Meta:
        model = UserFav
        fields = ("goods", "id")

收藏提交的时候只需要传递goods的id过来,userfavdetail获取的时候想要获取goods的详情。

动态的Serializer。

# 设置动态的Serializer
    def get_serializer_class(self):
        if self.action == "list":
            return UserFavDetailSerializer
        elif self.action == "create":
            return UserFavSerializer

        return UserFavSerializer

用户留言功能

删除,获取留言,添加用户留言。留言中可以上传文件。

后台接口viewset和配套的Serializer

user_operation/serializers.py:

class LeavingMessageSerializer(serializers.ModelSerializer):
    """
    用户留言序列化
    """
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    # 设置当天提交的时间 ready_only=True, 只返回不提交
    add_time = serializers.DateTimeField(read_only=True, format="%Y-%m-%d %H:%M:%S")

    class Meta:
        model = UserLeavingMessage

        fields = ("user", "message_type", "subject", "message", "add_time", "file", "id")

user直接获取当前的用户,add_time设置只可读取,格式化。

class LeavingMessageViewSet(mixins.ListModelMixin,
                            mixins.CreateModelMixin,
                            mixins.DestroyModelMixin,
                            viewsets.GenericViewSet):
    """
    list:       获取用户留言
    create:     添加留言
    delete:     删除留言
    """

    # 需要登录状态与用户验证
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)

    serializer_class = LeavingMessageSerializer

    def get_queryset(self):
        return UserLeavingMessage.objects.filter(user=self.request.user)
        

配置 url

# 配置用户留言的url
router.register(r'messages', LeavingMessageViewset, base_name="messages"),

我们在删除的时候需要服务器给我们返回的 id。前后端分离的系统,服务器返回完整地址可以直接被前端查看。

addtime 应该是只返回不提交。由获取的当前时间自动填充。

用户收货地址列表页接口开发

收货地址也是放在user_operation中的。获取到所有的收货地址,修改某一个收货地址。
删除某一个收货地址。

需要列表,添加,更新,删除:继承增删改查的mixin,但是如果都需要,那么有有一个viewset已经帮我们做到了。
我们只需要继承于ModelViewSet就行了。

添加一个 Serializer

class AddressSerializer(serializers.ModelSerializer):
    """
    收货地址序列化
    """
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault
    )
    add_time = serializers.DateTimeField(read_only=True, format="%Y-%m-%d %H:%M:%S")

    def validated_mobile(self, signer_mobile):
        if not re.match(REGEX_MOBILE, signer_mobile):
            raise serializers.ValidationError("手机号非法")
        return signer_mobile

    class Meta:
        model = UserAddress
        # fields = "__all__"
        fields = ("id", "add_time", "user", "province", "city", "district",
                  "address", "signer_name", "signer_mobile")

地址回填的时候,如果是一个地址要将它的省市区全部回填就要拆分出来,这样比较麻烦。所以我们设置三个字段进行存储。省 市 区域

viewset 中进行配置

class AddressViewSet(viewsets.ModelViewSet):
    """
    收获地址管理
    list:       获取收货地址
    create:     添加收货地址
    update:     更新收货地址
    delete:     删除收货地址
    """
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    serializer_class = AddressSerializer

    def get_queryset(self):
        return UserAddress.objects.filter(user=self.request.user)
        

在url中进行配置:

# 收货地址
router.register(r'address', , base_name="address"),
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值