Appearance
上一章中我们已经将用户以外键的形式关联到文章中了,但是由于 author
字段是允许为空的,所以理论上还是可以发表没有作者的文章:
python
> http -a dusai:admin123456 POST http://127.0.0.1:8000/api/article/ title="title 1" body="body 1"
...
{
"author": null,
...
}
虽然你可以直接指定作者的 id
值来对外键赋值,但是这种方法不但没有必要,甚至还可以伪造一个错误的用户 id
:
python
> http -a dusai:admin123456 POST ... author=9999
...
{
"author": [
"Invalid pk \"9999\" - object does not exist."
]
}
解决方法如下:既然请求体中已经包含用户信息了,那就可以从 Request
中提取用户信息,并把额外的用户信息注入到已有的数据中。
修改视图:
python
# article/views.py
class ArticleList(generics.ListCreateAPIView):
...
# 新增代码
def perform_create(self, serializer):
serializer.save(author=self.request.user)
- 新增的这个
perform_create()
从父类ListCreateAPIView
继承而来,它在序列化数据真正保存之前调用,因此可以在这里添加额外的数据(即用户对象)。 serializer
参数是ArticleListSerializer
序列化器实例,并且已经携带着验证后的数据。它的save()
方法可以接收关键字参数作为额外的需要保存的数据。
在命令行重新测试:
python
> 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
:
python
> http -a dusai:admin123456 POST ... author=9999
...
{
"author": [
"Invalid pk \"9999\" - object does not exist."
]
}
好在序列化器允许你指定只读字段。修改 ArticleListSerializer
:
python
# article/serializers.py
...
class ArticleListSerializer(serializers.ModelSerializer):
class Meta:
...
# 新增代码
read_only_fields = ['author']
此时在接收 POST 请求时,序列化器就不再理会请求中附带的 author
数据了:
python
> 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:
python
(env) > python manage.py startapp user_info
并将新 app 添加到注册列表:
python
# drf_vue_blog/settings.py
INSTALLED_APPS = [
...
'user_info',
]
新建 user_info/serializers.py
文件,写入:
python
# 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'
]
序列化类我们已经比较熟悉了,这个序列化器专门用在文章列表中,展示用户的基本信息。
最后修改文章列表的序列化器,把它们嵌套到一起:
python
# 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 了,在命令行测试一下:
python
> 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"
}
}
]
任务完成。