我正在尝试序列化一些对象,这些对象的数据存储在2个数据库中,由普通的UUID链接。第二个数据库DB2存储个人数据,因此它作为隔离的微服务运行,以符合各种隐私法。我将数据作为解码的dicts列表(而不是模型实例的实际查询集)接收。如何调整ModelSerializer
以序列化此数据?
这是与DB2交互以获取个人数据的最小示例:
# returns a list of dict objects, approx representing PersonalData.__dict__
# `custom_filter` is a wrapper for the Microservice API using `requests`
personal_data = Microservice.objects.custom_filter(uuid__in=uuids)
这是一种序列化的最小方式,包括出生日期:
class PersonalDataSerializer(serializers.Serializer):
uuid = serializers.UUIDField() # common UUID in DB1 and DB2
dob = serializers.DateField() # personal, so can't be stored in DB1
在我的应用程序中,我需要将Person查询集和相关的personal_data
序列化为一个JSON数组。
class PersonSerializer(serializers.ModelSerializer):
dob = serializers.SerializerMethodField()
# can't use RelatedField for `dob` because the relationship isn't
# codified in the RDBMS, due to it being a separate Microservice.
class Meta:
model = Person
# A Person object has `uuid` and `date_joined` fields.
# The `dob` comes from the personal_data, fetched from the Microservice
fields = ('uuid', 'date_joined', 'dob',)
def get_dob(self):
raise NotImplementedError # for the moment
我不知道是否有一个很好的DRF方式来链接这两者。我绝对不希望通过在get_dob
中包含单个请求来向微服务发送(可能有数千个)个别请求。实际视图看起来像这样:
class PersonList(generics.ListAPIView):
model = Person
serializer_class = PersonSerializer
def get_queryset(self):
self.kwargs.get('some_filter_criteria')
return Person.objects.filter(some_filter_criteria)
逻辑应该将微服务数据链接到串行器中,它应该是什么样的?
答案 0 :(得分:5)
我建议您覆盖序列化程序和列表方法。
串行:
class PersonSerializer(models.Serializer):
personal_data = serializers.DictField()
class Meta:
model = Person
创建一个函数,将personal_data字典添加到person对象。在将人物对象列表提供给序列化程序之前使用此方法。
def prepare_persons(persons):
person_ids = [p.uuid for p in persons]
personal_data_list = Microservice.objects.custom_filter(uuid__in=person_ids)
personal_data_dict = {pd['uuid']: pd for pd in personal_data_list}
for p in persons:
p.personal_data = personal_data_dict[p.id]
return persons
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
page = prepare_persons(page)
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
else:
persons = prepare_persons(queryset)
serializer = self.get_serializer(persons, many=True)
return Response(serializer.data)
答案 1 :(得分:4)
因为您只希望一次点击数据库,所以将额外数据添加到查询集的好方法是向ViewSet添加ListModelMixin
的自定义版本includes extra context:
class PersonList(generics.ListAPIView):
...
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
# Pseudo-code for filtering, adjust to work for your use case
filter_criteria = self.kwargs.get('some_filter_criteria')
personal_data = Microservice.objects.custom_filter(filter_criteria)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(
page,
many=True,
context={'personal_data': personal_data}
)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(
queryset,
many=True,
context={'personal_data': personal_data}
)
return Response(serializer.data)
然后,通过overriding the to_representation
method访问序列化程序中的额外上下文:
def to_representation(self, instance):
"""Add `personal_data` to the object from the Microservice"""
ret = super().to_representation(instance)
personal_data = self.context['personal_data']
ret['personal_data'] = personal_data[instance.uuid]
return ret