(生鲜项目_用户板块)21. 用户信息+我的收藏+用户留言+收货地址

第一步: 用户信息

1.前端在进入到更改用户信息的界面, 会向后端索取用户的各种信息, 但是前端并不知道用户当前的id是多少, 而且后端之前也没配置get请求的接口, 所以配置如下

重写get_object后, 无论前端发过来什么数据 例如  /users/999/  , 后端都可以返回给前端当前用户的信息

class UserViewset(CreateModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
    ...
    # 给retrieve用的, 目的是给前端返回当前登录用户信息
    def get_object(self):
        return self.request.user
    ...

2. 既然前端要获取用户登录的信息, 那么肯定前端发过来的请求要携带登录的信息,要么是session,要么是JWT token,  所以后端应该设置

permission_classes = (permissions.IsAuthenticated,)

但这么配置会导致一个问题, 会使我们在创建用户的时候也被迫要求处于登录状态, 这不是自相矛盾吗? 所以我们要动态的配置permission_classes, 不同的请求方式用不同的permission_classes

办法就是重写 APIView中的 get_permissions 方法(该方法原本的目的是遍历permission_classes, 并一个个返回) 源码如下

  这里, 我们重写它, 当然配套的你还得指定认证方式, 这里设置JWT和session两种

class UserViewset(...)
  ...
  # 登录的方式,session是为了浏览器验证方便 authentication_classes = (JSONWebTokenAuthentication,authentication.SessionAuthentication) # 动态配置permission_classes, 如果是create就不需要登录, 否则就要求登录 def get_permissions(self): if self.action == "retrieve": # 只有viewSet才能使用action的属性 return [permissions.IsAuthenticated()] # 返回实例 elif self.action == "create": return [] return []

  3. 使用文档测试一下接口

  4. 很明显, 因为我们之前的设置, (下图), 所以只返回这两个字段, 如果要返回其它字段信息, 我们不得不重写一个serializer,

并且在viewSet里面动态使用serializer, 即重写GenericAPIView的get_serializer_class, 和重写get_permissions逻辑差不多

  users.serializers.py

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

 views.py

    # 动态配置serializer_class, 如果是create就用UserRegSerializer, 否则就用UserDetailSerializer
    def get_serializer_class(self):
        if self.action == "retrieve":  # 只有viewSet才能使用action的属性
            return UserDetailSerializer  
        elif self.action == "create":
            return UserRegSerializer
        return UserDetailSerializer

 使用文档去测试接口, 成功返回了想要的字段

 

 第二步: Vue联调

1.前端源码

  2.前端测试, 邮箱已经正常显示, 说明没问题了

 

 

第三步: 个人信息修改

1.前面我们已经完成了很多技术难点, 现在只需要新增 mixins.UpdateModelMixin 即可, update插件里面其实可以实现全部更新(put请求方式)和部分更新(patch请求方式)

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

 2. 去文档测试, 可发现多出来两项设置, 重新填了token后(刷新页面, 文档的token就会丢失), 

 

 

 3.去前端页面测试修改, 首先更改Vue源码, 这里就能看到是patch请求方式

 

 

 

  

第四步: 我的收藏

1.目前, 我们对userFav接口的返回数据见下, 只有goods_id, 这是不够的

 

 2. 为了返回good的全部信息, 我们就需要向前面返回用户信息一样, 单独设计一个 UserDetailSerializer , 并给其增加goods字段(继承自GoodsSerializer)

user_operation.serializers.py

# 返回用户收藏中的good的详细信息
class UserFavDetailSerializer(serializers.ModelSerializer):
    goods = GoodsSerializer()  # 新增goods字段,并继承goods的serializer

    class Meta:
        model = UserFav
        fields = ("goods", "id")

 

 views.py

class UserFavViewset(...)
    ...
    # 动态配置serializer_class, 如果是list就用UserFavDetailSerializer, 否则就用UserFavSerializer
    def get_serializer_class(self):
        if self.action == "list":  # 只有viewSet才能使用action的属性
            return UserFavDetailSerializer
        elif self.action == "create":
            return UserFavSerializer
        return UserFavSerializer
    ...

 

 3. 去API界面测试, 可看到goods的字段全部都返回了

 

 4. 前端Vue源码解读

 

前端测试, 返回成功, 测试删除功能后刷新网页, 一切正常. 点击进入商品详情页, 访问正常

 

 第五步: 用户留言

1.models在最开始已经设置好了, 这里需要些LeavingMessageViewSet, 逻辑里面应该包含list, create, destroy功能, 要求和user_fav几乎一致, 例如, 需要登录认证, 只返回自己的留言信息(重写query_set)

serializer的model = UserLeavingMessage, 返回的字段除了留言的字段外, 应该注意user的取法serializers.CurrentUserDefault(), 以及必须返回id字段, 方便前端删除

代码如下: 

class LeavingMessageSerializer(serializers.ModelSerializer):
    # 获取当前用户,并隐藏user字段
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    add_time = serializers.DateTimeField(read_only=True)  # 知识点: read_only=True, 只返回, 不用提交

    class Meta:
        model = UserLeavingMessage
        fields = ("user", "message_type", "subject", "message", "file", "id", "add_time")  # 设置id字段是为了方便前端删除收藏

 

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

    serializer_class = LeavingMessageSerializer

    # IsAuthenticated 用于验证用户是否登录
    # IsOwnerOrReadOnly 用户只能删除自己的收藏信息
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)

    # 可以通过 JWT的token认证登录, 也可以通过session登录
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)

    # 只返回当前登录用户的留言
    def get_queryset(self):
        return UserLeavingMessage.objects.filter(user=self.request.user)
# 用户留言接口(包含删除留言,新增留言)
router.register(r'messages', LeavingMessageViewSet, basename="messages")

 

 2. 先去添加一些留言信息

 

 

 

 3. serializer知识点, 设置某一个返回的字段为read_only=True, 即只返回, 不用提交

 

 4. serializer知识点, 设置serializers.DateTimeField的时间格式 add_time = serializers.DateTimeField(read_only=True, format="%Y-%m-%d %H:%M:%S")

 

 5.Vue联调

 

 

 

  

6. 前端源码

 

 

 7. 上传文件, 为何我们什么都没做, 但是上传文件如此简单, 这归因于 REST的 MultiPartParser 模块

 

 

 

 

 

第六步: 收货地址

1.有了前面的基础, 收货地址几乎可以直接copy过来. 新知识点: viewsets.ModelViewSet  包含了mixin的增删改查和GenericViewSet

2.由于前段的地址使用插件实现的: 省份+城市+区域, 所以后端为了方便, 这里我们创建三个字段来完成这个地址

class UserAddress(models.Model):
    province = models.CharField(max_length=100, default="", verbose_name="省份")
    city = models.CharField(max_length=100, default="", verbose_name="城市")
    district = models.CharField(max_length=100, default="", verbose_name="区域")

 

3. 代码如下,

viewset

# 收货地址, ModelViewSet包括了前面的mixin的增删改查所有功能
class AddressViewSet(viewsets.ModelViewSet):
    """
    收货地址管理
    list:
        获取收货地址
    create:
        添加收货地址
    update:
        修改收货地址
    destroy:
        删除收货地址
    """
    serializer_class = AddressSerializer
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    def get_queryset(self):
        return UserAddress.objects.filter(user=self.request.user)

 

 serializers, 需要自己去回忆局部钩子validators属性

# 收货地址
class AddressSerializer(serializers.ModelSerializer):
    # 获取当前用户,并隐藏user字段
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    add_time = serializers.DateTimeField(read_only=True, format="%Y-%m-%d %H:%M:%S")
    signer_mobile = serializers.CharField(min_length=11, max_length=11,
                                          error_messages={
                                              "min_length": "手机号码长度不对",
                                              "max_length": "手机号码长度不对",
                                              "required": "未收到手机号码字段",
                                              "blank": "请输入手机号码"
                                          },
                                          # validators=[RegexValidator(REGEX_MOBILE, "手机号码非法")],
                                          )

    # 用局部钩子来验证, 和上面单独加validator属性一样
    def validate_signer_mobile(self, signer_mobile):
        if UserAddress.objects.filter(signer_mobile=signer_mobile):
            raise serializers.ValidationError("手机号码已存在")
        if not re.match(REGEX_MOBILE, signer_mobile):
            raise serializers.ValidationError("手机号码非法")
        return signer_mobile

    class Meta:
        model = UserAddress
        fields = ("id", "user", "province", "city", "district", "address", "signer_name", "add_time",
                  "signer_mobile")  # 设置id字段是为了方便前端删除收藏

 

 

 urls

# 用户留言接口(包含删除留言,新增留言)
router.register(r'address', AddressViewSet, basename="address")

 

4. 用doc文档功能来测试(主要看手机号的过滤功能)

 

5. 前端Vue联调

 

 

 

 

 

 

 

 

 

 

 

 

---  君子处其实,不处其华;治其内,不治其外   张居正  ----

posted @ 2019-12-30 15:35  渱尘  阅读(335)  评论(0编辑  收藏  举报