Skip to content

Latest commit

 

History

History
195 lines (153 loc) · 4.78 KB

70-文章关联用户.md

File metadata and controls

195 lines (153 loc) · 4.78 KB

上一章中我们已经将用户以外键的形式关联到文章中了,但是由于 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"
        }
    }
]

任务完成。