Django-Vue搭建个人博客:文章关联用户
10303 views, 2021/03/07 updated Go to Comments
上一章中我们已经将用户以外键的形式关联到文章中了,但是由于 author
字段是允许为空的,所以理论上还是可以发表没有作者的文章:
> http -a dusai:admin123456 POST http://127.0.0.1:8000/api/article/ title="title 1" body="body 1" ... { "author": null, ... }
虽然你可以直接指定作者的 id
值来对外键赋值,但是这种方法不但没有必要,甚至还可以伪造一个错误的用户 id
:
> http -a dusai:admin123456 POST ... author=9999 ... { "author": [ "Invalid pk \"9999\" - object does not exist." ] }
解决方法如下:既然请求体中已经包含用户信息了,那就可以从 Request
中提取用户信息,并把额外的用户信息注入到已有的数据中。
修改视图:
# article/views.py class ArticleList(generics.ListCreateAPIView): ... # 新增代码 def perform_create(self, serializer): serializer.save(author=self.request.user)
- 新增的这个
perform_create()
从父类ListCreateAPIView
继承而来,它在序列化数据真正保存之前调用,因此可以在这里添加额外的数据(即用户对象)。 serializer
参数是ArticleListSerializer
序列化器实例,并且已经携带着验证后的数据。它的save()
方法可以接收关键字参数作为额外的需要保存的数据。
在命令行重新测试:
> http -a dusai:admin123456 POST http://127.0.0.1:8000/api/article/ title="post with user" body="new test again" ... { "author": 1, "created": "2020-07-03T07:21:49.865414Z", "id": 6, "title": "post with user" }
很好,但是用户依然可以手动传入一个错误的 author
:
> http -a dusai:admin123456 POST ... author=9999 ... { "author": [ "Invalid pk \"9999\" - object does not exist." ] }
好在序列化器允许你指定只读字段。修改 ArticleListSerializer
:
# article/serializers.py ... class ArticleListSerializer(serializers.ModelSerializer): class Meta: ... # 新增代码 read_only_fields = ['author']
此时在接收 POST 请求时,序列化器就不再理会请求中附带的 author
数据了:
> http -a dusai:admin123456 POST ... author=9999 HTTP/1.1 201 Created ... { "author": 1, "created": "2020-07-03T07:30:25.061543Z", "id": 8, "title": "new post" }
显示用户信息
虽然作者外键已经出现在序列化数据中了,但是仅仅显示作者的 id 不太有用,我们更想要的是比如名字、性别等更具体的结构化信息。所以就需要将序列化数据嵌套起来。
新创建一个用户 app:
(env) > python manage.py startapp user_info
并将新 app 添加到注册列表:
# drf_vue_blog/settings.py INSTALLED_APPS = [ ... 'user_info', ]
新建 user_info/serializers.py
文件,写入:
# user_info/serializers.py from django.contrib.auth.models import User from rest_framework import serializers class UserDescSerializer(serializers.ModelSerializer): """于文章列表中引用的嵌套序列化器""" class Meta: model = User fields = [ 'id', 'username', 'last_login', 'date_joined' ]
序列化类我们已经比较熟悉了,这个序列化器专门用在文章列表中,展示用户的基本信息。
最后修改文章列表的序列化器,把它们嵌套到一起:
# article/serializers.py from user_info.serializers import UserDescSerializer class ArticleListSerializer(serializers.ModelSerializer): # read_only 参数设置为只读 author = UserDescSerializer(read_only=True) class Meta: model = Article fields = [ 'id', 'title', 'created', 'author', ] # 嵌套序列化器已经设置了只读,所以这个就不要了 # read_only_fields = ['author']
这就 OK 了,在命令行测试一下:
> http http://127.0.0.1:8000/api/article/ ... [ { "id": 6, "title": "post with user", "created": "2020-07-03T07:21:49.865414Z", "author": { "id": 1, "username": "dusai", "last_login": "2020-07-03T03:25:25.554972Z", "date_joined": "2020-06-15T09:23:27.417440Z" } }, { "id": 10, "title": "new test hello!", "created": "2020-07-12T09:11:25.688996Z", "author": { "id": 1, "username": "dusai", "last_login": "2020-07-03T03:25:25.554972Z", "date_joined": "2020-06-15T09:23:27.417440Z" } } ]
任务完成。