您现在的位置是: 网站首页 >Django >Vue+Django REST framework前后端分离生鲜电商 Django

【Vue+DRF生鲜电商】21.用户中心个人资料的展示,并在Vue中实现个人资料更新

admin2019年5月31日 12:45 Django | Vue 315人已围观

Vue+Django REST framework前后端分离生鲜电商简介 Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。 Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket ; Django版本:2.2、djangorestframework:3.9.2。 前端Vue模板可以直接联系我拿。

## 用户个人资料的显示 ![BLOG_20190531_125022_59](/media/blog/images/2019/05/BLOG_20190531_125022_59.png "博客图集BLOG_20190531_125022_59.png") 这儿需要序列化:姓名、出生日期、性别、邮箱、手机,这些信息。 这儿就需要一个接口,用来请求用户信息的。在之前用户注册的ViewSet总,已经继承了`UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet)`,现在只需要再继承一个`mixins.RetrieveModelMixin`,原则上就可以获取用户详情了。那么在router注册的时候就会增加一个获取用户详情`users/id/`的url。 ```python # apps/users/views.py class UserViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """ create: 创建用户 retrieve: 显示用户详情 """ ``` 现在来访问 http://127.0.0.1:8000/users/1/ ![BLOG_20190531_125011_70](/media/blog/images/2019/05/BLOG_20190531_125011_70.png "博客图集BLOG_20190531_125011_70.png") 只会序列化`username`、`mobile`,因为在`UserSerializer(serializers.ModelSerializer)`序列化类中虽然定义了`('username', 'mobile', 'code', 'password')`多个序列化字段,但其中有两个配置了`write_only=True`属性,序列化将不会显示该字段。 但是用户在进个人中心的时候,用户并不知道这个`id`,也就无法拼凑`users/id/`的url。 ### 使用get_object()获取当前登录用户 可以重写`get_object()`方法(它是在`viewsets.GenericViewSet -> generics.GenericAPIView`中) 这个方法返回视图显示的对象。 如果需要提供非标准的查询集查找,则可能需要重写此项。例如,如果在url conf中使用多个关键字参数引用对象。使用如下 ```python # apps/users/views.py class UserViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): # ...... def get_object(self): # 获取操作的对象,在RetrieveModelMixin和DestroyModelMixin都需要用到 return self.request.user ``` 现在不管传递什么`id`,都只返回当前登录用户。 ### 要求用户为登录状态permissions 要能获取到当前用户,就必须要要求为登录状态,所以就需要设定权限。 #### 直接使用permissions属性 ```python # apps/users/views.py from rest_framework import permissions class UserViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): # ...... permissions = (permissions.IsAuthenticated,) # 用户登录后才能获取详情 # ...... ``` 因为获取用户详情,更新用户资料都要求已登录。但是如果按照上面这样配置,用户~~在注册(用一个ViewSet)的时候也要求登录~~这显然是不对的。所以用户注册的时候就不需要`IsAuthenticated`权限。 #### 用户ViewSet中动态permissions 查看 `permissions` 的配置,来自于`viewsets.GenericViewSet -> generics.GenericAPIView -> views.APIView`,这里面配置了权限类列表`permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES`,而下面有一个`get_permissions()`方法,用来**遍历此视图所需的权限列表并实例化返回**。现在我们就可以重载该方法,来动态给定**不同的动作**拥有不同的权限。 ```python # 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] ``` 现在就需要判断当前的动作是什么,例如`POST`、`GET`等。这里面的`action`的值对应`mixins`中的各种类方法名称,比如`ListModelMixin`对应的action为`list`,`RetrieveModelMixin`对应的action为`retrieve`。注意`action`只有使用了`viewsets`才有这个好处。那么`get_permissions()`就可以这样完成 ```python # apps/users/views.py from rest_framework import permissions class UserViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): # ...... # permissions = (permissions.IsAuthenticated,) # 用户登录后才能获取详情,但用户注册也要求该权限,不可行 def get_permissions(self): """ 动态设置不同action不同的权限类列表 """ if self.action == 'retrieve': return [permissions.IsAuthenticated()] # 一定要加()表明返回它的实例 elif self.action == 'create': return [] else: return [] # ...... ``` 测试下是否正常,在未登录的情况下打开 http://127.0.0.1:8000/users/ ![BLOG_20190531_124951_59](/media/blog/images/2019/05/BLOG_20190531_124951_59.png "博客图集BLOG_20190531_124951_59.png") 然后再请求 http://127.0.0.1:8000/users/99/ ,id可以随意输入,后端调试可以看到当请求某个id是,action为`retrieve` ![BLOG_20190531_124941_23](/media/blog/images/2019/05/BLOG_20190531_124941_23.png "博客图集BLOG_20190531_124941_23.png") ![BLOG_20190531_124933_39](/media/blog/images/2019/05/BLOG_20190531_124933_39.png "博客图集BLOG_20190531_124933_39.png") 输入帐密即可显示当前登录用户信息。弹出这个输入框是因为在DRF全局中配置过`'rest_framework.authentication.BasicAuthentication',`,这是一种基本的认证方式,让弹出输入用户名密码。 ![BLOG_20190531_124926_34](/media/blog/images/2019/05/BLOG_20190531_124926_34.png "博客图集BLOG_20190531_124926_34.png") ### 自定义认证方式authentication_classes 这里面需要用户认证,这个ViewSet可以自己指定认证方式`authentication_classes`,不使用全局的认证方式。 ```python # apps/users/views.py from rest_framework.authentication import SessionAuthentication from rest_framework_simplejwt.authentication import JWTAuthentication class UserViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): # ...... authentication_classes = (SessionAuthentication, JWTAuthentication) # 自定义VieSet认证方式:JWT用于前端登录认证,Session用于方便在DRF中调试使用 # ...... ``` 现在退出登录,刷新 http://127.0.0.1:8000/users/99/ 就不会弹出帐密输入框了,因为自定义了认证类。 ![BLOG_20190531_124917_80](/media/blog/images/2019/05/BLOG_20190531_124917_80.png "博客图集BLOG_20190531_124917_80.png") #### 使用Session认证登录测试 如果要使用Session认证,直接右上角的Log in 即可。 ![BLOG_20190531_124909_64](/media/blog/images/2019/05/BLOG_20190531_124909_64.png "博客图集BLOG_20190531_124909_64.png") #### 使用JWT认证登录测试 这儿直接使用DRF文档功能完成。首先获取JWT Token 访问 http://127.0.0.1:8000/docs/#login-create 提供用户名密码 ![BLOG_20190531_124901_80](/media/blog/images/2019/05/BLOG_20190531_124901_80.png "博客图集BLOG_20190531_124901_80.png") 得到的结果 ```json { "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU1ODY4OTkwNiwianRpIjoiZTkzY2NiZGI1NzNiNGFhZWFlYWIzMjI4YzlkZWVhMjYiLCJ1c2VyX2lkIjoxfQ.280vC8bTbFGF8D7ypKx0NgXlOcn_gI51N6IG4kaCMIM", "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTU3OTk4NzA2LCJqdGkiOiI2N2RjNGJiZGYyZjI0NGYzYjQ4YjUxNmUzYTAwODkyZSIsInVzZXJfaWQiOjF9.O54g23VZykww724fsn23gnaF205uGvd_MjUfXslW_k0" } ``` 然后使用token完成登录 ![BLOG_20190531_124852_39](/media/blog/images/2019/05/BLOG_20190531_124852_39.png "博客图集BLOG_20190531_124852_39.png") 最后在 http://127.0.0.1:8000/docs/#users-read 请求,也可以得到当前登录用户信息 ![BLOG_20190531_124843_11](/media/blog/images/2019/05/BLOG_20190531_124843_11.png "博客图集BLOG_20190531_124843_11.png") #### 使用token认证不成功测试 先在 http://127.0.0.1:8000/docs/#api-token-auth-create 获取DRF的token ![BLOG_20190531_124837_83](/media/blog/images/2019/05/BLOG_20190531_124837_83.png "博客图集BLOG_20190531_124837_83.png") 然后使用token认证 ![BLOG_20190531_124828_76](/media/blog/images/2019/05/BLOG_20190531_124828_76.png "博客图集BLOG_20190531_124828_76.png") 访问 http://127.0.0.1:8000/docs/#users-read 获取用户信息 ![BLOG_20190531_124821_43](/media/blog/images/2019/05/BLOG_20190531_124821_43.png "博客图集BLOG_20190531_124821_43.png") 且在后台会报`[09/May/2019 17:33:43] "GET /users/123/ HTTP/1.1" 403 43 Forbidden: /users/123/`错误,表明认证不通过,因为在这个ViewSet中要求用户只能有Session和JWT认证才有效。 ### 序列化用户详情数据 之前也写过,到目前为止,序列化只会显示`username`和`mobile`,其他不会显示,它使用的`serializer_class`为`UserSerializer`。所以现在要创建一个专用于序列化用户详情的`serializer_class`。 #### 创建用户详情序列化类 在当前应用 serializers.py 下添加下面的类 ```python # apps/users/serializers.py class UserDetailSerializer(serializers.ModelSerializer): """ 用户详情序列化类 """ class Meta: model = User fields = ('username', 'name', 'birthday', 'mobile', 'gender') ``` #### 用户ViewSet动态使用serializer_class 在`GenericAPIView`类中有一个`get_serializer_class(self)`方法 返回用于序列化的类。默认使用`self.serializer_class`。如果需要根据传入请求提供不同的序列化,则可能需要重写此项。 在当前应用的 serializers.py 添加以下代码,重载`get_serializer_class(self)`函数。可以参考动态permissions配置。 ```python # apps/users/serializers.py from .serializers import VerifyCodeSerializer, UserSerializer, UserDetailSerializer class UserViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): # ...... # serializer_class = UserSerializer def get_serializer_class(self): """ 不同的action使用不同的序列化 :return: """ if self.action == 'retrieve': return UserDetailSerializer # 使用显示用户详情的序列化类。这儿就直接返回类名,不需要实例化 elif self.action == 'create': return UserSerializer # 使用原来的序列化类,创建用户专用 else: return UserDetailSerializer # ...... ``` 现在再请求用户详情数据 http://127.0.0.1:8000/docs/#users-read ![BLOG_20190531_124810_88](/media/blog/images/2019/05/BLOG_20190531_124810_88.png "博客图集BLOG_20190531_124810_88.png") ### Vue和用户接口信息联调 ![BLOG_20190531_124804_73](/media/blog/images/2019/05/BLOG_20190531_124804_73.png "博客图集BLOG_20190531_124804_73.png") 用户中心的组件都在 src/views/member/userinfo.vue 中 ```JavaScript created () { this.getUserInfo(); }, ``` 数据加载是在组件`created()`之后,首先会调用`getUserInfo()`获取`getUserDetail()`获取用户详情的接口 ```JavaScript getUserInfo() { //请求用户信息 getUserDetail().then((response) => { this.userInfo = response.data; }).catch(function (error) { console.log(error); }); }, ``` 这个接口就是 src/api/api.js 中的 ```JavaScript //获取用户信息 export const getUserDetail = () => { return axios.get(`${local_host}/users/1/`) }; ``` 其中这个1是任意写的,但只会获取当前登录用户的详细信息,拿到数据之后,赋值给`userInfo`,拿到html里面做映射 序列化的时候少了`email`字段,将其加上 ```python # apps/users/serializers.py class UserDetailSerializer(serializers.ModelSerializer): """ 用户详情序列化类 """ class Meta: model = User fields = ('username', 'name', 'email', 'birthday', 'mobile', 'gender') ``` 现在刷新 http://127.0.0.1:8080/#/app/home/member/userinfo 就可以正常显示用户信息了 ![BLOG_20190531_124752_36](/media/blog/images/2019/05/BLOG_20190531_124752_36.png "博客图集BLOG_20190531_124752_36.png") ### 用户个人信息修改 可以选择性继承某些mixins,想要实现信息修改,只需要再继承`mixins.UpdateModelMixin,` #### DRF实现更新功能 ```python # apps/users/views.py class UserViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): # 其他代码 ``` 在这个`UpdateModelMixin`中拥有了更新和部分更新的操作,url也不需要去配置,接受`PUT`(更新)、`PATCH`(部分更新)操作。 查看DRF文档,访问 http://127.0.0.1:8000/docs/#users-update 可以看到用户更新功能。 ![BLOG_20190531_124743_25](/media/blog/images/2019/05/BLOG_20190531_124743_25.png "博客图集BLOG_20190531_124743_25.png") `update`为更新,`partial_update`为部分更新。 ![BLOG_20190531_124737_83](/media/blog/images/2019/05/BLOG_20190531_124737_83.png "博客图集BLOG_20190531_124737_83.png") 更新好后会返回已更新的数据 ```json { "username": "admin", "name": null, "email": "admin@admin.com", "birthday": "2000-01-01", "mobile": "15818181888", "gender": "female" } ``` 刷新 http://127.0.0.1:8080/#/app/home/member/userinfo 可以看到已修改好的内容 ![BLOG_20190531_124730_72](/media/blog/images/2019/05/BLOG_20190531_124730_72.png "博客图集BLOG_20190531_124730_72.png") #### Vue中更新实现 当点击“确认修改”按钮 ```html <td width="72%" align="left" bgcolor="#FFFFFF"><input name="username" type="text" placeholder="" size="25" class="inputBg" v-model="userInfo.name"></td> <button class="btn_blue_1" type="button" style="border:none;" @click="confirmModify">确认修改</button> ``` 部分代码,其中修改的姓名,一定要修改`name`字段,不能是修改`username` 传递`userInfo`参数调用`updateUserInfo`接口实现更新 ```JavaScript // src/views/member/userinfo.vue confirmModify() { // 确认修改 updateUserInfo(this.userInfo).then((response) => { alert('修改成功'); }).catch(function (error) { console.log(error); }); } ``` 直接采用部分更新方式,调用`patch`方法 ```JavaScript // src/api/api.js //修改用户信息 export const updateUserInfo = params => { return axios.patch(`${local_host}/users/1/`, params) }; ``` 在 http://127.0.0.1:8080/#/app/home/member/userinfo 修改一些信息,点击确认修改 ![BLOG_20190531_124720_66](/media/blog/images/2019/05/BLOG_20190531_124720_66.png "博客图集BLOG_20190531_124720_66.png") ![BLOG_20190531_124714_71](/media/blog/images/2019/05/BLOG_20190531_124714_71.png "博客图集BLOG_20190531_124714_71.png") 刷新下该页面,显示的是更新后的内容了。

很赞哦! (1)

文章交流

  • emoji
0人参与,0条评论

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行655天
  • 系统信息:Linux
  • 后台程序:Python: 3.6.6
  • 网站框架:Django: 2.2.12
  • 文章统计:235 篇
  • 文章评论:40 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站