Bob's Blog

Web开发、测试框架、自动化平台、APP开发、机器学习等

返回上页首页

Django restframework加Vue打造前后端分离的网站(十四)后端模块间数据引用和展示



前面已经建立了多个后端模块,url / model 等都在模块中形成独立业务相关内容,不过业务中难免就会有互相联系的情况,比如一个模块引用了其他模块,类似ForengnKey;比如get请求获取某个模块的数据,也希望把外键相连的模块数据也展示出来以避免多次请求。

场景1: model的外键涉及其他model时,在serializer中加载其他model的字段而不是只有id,加载其他model的部分字段

比如下面的例子,项目会限制到特定人员访问,于是project模块绑定了user模块

class Project(models.Model):

    PROJECT_TYPES = (
        ("internal", "internal"),
        ("public", "public")
    )
    name = models.CharField(max_length=100)
    project_type = models.CharField(choices=PROJECT_TYPES, default=PROJECT_TYPES[0][0], max_length=100)
    create_time = models.DateTimeField(default=timezone.now)
    update_time = models.DateTimeField(default=timezone.now)
    owner = models.ManyToManyField(User, blank=True)   # 涉及到user model

在serializer中展示所有字段

class ProjectSerializer(serializers.ModelSerializer):

    class Meta:
        model = Project
        fields = "__all__"

但是,api返回的数据中owner字段只展示了id

如果我需要获取该用户的信息比如username,则需要再次请求一次user api,造成了多余的请求。于是可以简化一下,在一个project api中就获得该用户的部分数据。原来的UserSerializer包含了详细的字段: ["id", "username", "email", "is_active", "is_staff", "is_superuser", "groups", "last_login", "user_permissions"],但这些不需要全部在project api中展示,只需要id / username / email。

于是我们用SerializerMethodField来指明owner字段,并将需要的字段数据获取出来再汇总。

class ProjectSerializer(serializers.ModelSerializer):
    owner = serializers.SerializerMethodField()

    def get_owner(self, instance):
        print(instance.owner.get_queryset())
        user_obj = instance.owner.get_queryset()
        owner_list = []
        if user_obj:
            for u in user_obj:
                owner_list.append({"id": u.id,
                                   "username": u.username,
                                   "email": u.email})
        return owner_list

    class Meta:
        model = Project
        # fields = "__all__"
        fields = ("id", "name", "project_type", "create_time", "update_time", "owner")

此时便展示了owner的部分数据,不需要再次请求一次user api了。

场景2: model的外键涉及其他model时,在serializer中加载其他model的字段而不是只有id,加载其他model的所有字段

这样的需求也可以使用场景1中的办法,但是当一个model的字段比较多时,则需要写较多代码来展示所有字段,这是没有必要的。

我们写一个指定User所有字段的Serializer

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = "__all__"

然后我们可以在ModelSerializer的to_representation方法里更改展示的owner字段的数据,令其展示所有,然后在project的serializer.py中添加

class ProjectSerializer(serializers.ModelSerializer):

    class Meta:
        model = Project
        fields = "__all__"

    def to_representation(self, instance):
        response = super().to_representation(instance)
        response['owner'] = UserSerializer(instance.owner, many=True).data
        return response

此时在get请求时便展示了User模块的详细信息,如下图

当然,场景1的情况时也可以用这个方法,只是对应的UserSerializer指定的字段不同,便可展示不同的内容。

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ["id", "username", "email"]

 

需要注意的:

下面这种写法也是可以将外键id展示为具体数据,但这样的会影响到创建新object时传递user ID。这种写法与在"Class Meta"中指明"depth = 1"等价。

(read_only代表只读,不可写;如果去掉read_only,可以写入了,但传入的值却不能是id了)

from users.serializers import OwnerSerializer

class ProjectSerializer(serializers.ModelSerializer):
    owner = OwnerSerializer(read_only=True, many=True)

    class Meta:
        model = Project
        fields = ("id", "name", "project_type", "create_time", "update_time", "owner")

 

参考: https://stackoverflow.com/questions/33182092/django-rest-framework-serializing-many-to-many-field

https://stackoverflow.com/questions/29950956/drf-simple-foreign-key-assignment-with-nested-serializers/

下一篇:  Mac上用openemu玩街机游戏
上一篇:  Python爬虫(六)模拟用户登录

共有0条评论

添加评论

暂无评论