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