I'm trying to serialize some objects whose data is stored in 2 databases, linked by common UUIDs. The second database DB2 stores personal data, so it is run as a segregated microservice to comply with various privacy laws. I receive the data as a decoded list of dicts (rather than an actual queryset of model instances). How can I adapt the ModelSerializer
to serialize this data?
我正在尝试序列化一些对象,这些对象的数据存储在两个数据库中,通过公共uuid进行链接。第二个数据库DB2存储个人数据,因此它作为独立的微服务运行,以遵守各种隐私法律。我接收的数据是经过解码的dicts列表(而不是实际的模型实例查询集)。如何调整ModelSerializer来序列化此数据?
Here's a minimal example of interacting with DB2 to get the personal data:
这里有一个与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)
And here's a minimal way of serializing it, including the date of birth:
这里有一个最小的序列化方式,包括出生日期:
class PersonalDataSerializer(serializers.Serializer):
uuid = serializers.UUIDField() # common UUID in DB1 and DB2
dob = serializers.DateField() # personal, so can't be stored in DB1
In my application, I need to serialize the Person queryset, and related personal_data
, into one JSON array.
在我的应用程序中,我需要将Person queryset和相关的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
I don't know if there's a nice DRF way to link the two. I definitely don't want to be sending (potentially thousands of) individual requests to the microservice by including a single request in get_dob
. The actual view just looks like this:
我不知道是否有一种很好的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)
Where should the logic go to link the microservice data into the serializer, and what should it look like?
将微服务数据链接到序列化器的逻辑应该放在哪里,它应该是什么样子?
2 个解决方案
#1
5
I suggest you to override the serializer and your list method.
我建议您重写序列化器和列表方法。
Serializer:
序列化器:
class PersonSerializer(models.Serializer):
personal_data = serializers.DictField()
class Meta:
model = Person
make a function to add personal_data dictionary to persons object. Use this method before giving the list of person objects to the serializer.
创建一个向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)
#2
4
Because you want to only hit your database one time, a good way to add your extra data to your queryset is by adding a custom version of ListModelMixin
to your ViewSet that includes extra context:
因为您只希望一次访问数据库,所以向queryset添加额外数据的一个好方法是在您的ViewSet中添加一个自定义版本的ListModelMixin,其中包含额外的上下文:
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)
Then, access the extra context in your serializer by overriding the to_representation
method:
然后,通过覆盖to_representation方法访问序列化器中的额外上下文:
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
#1
5
I suggest you to override the serializer and your list method.
我建议您重写序列化器和列表方法。
Serializer:
序列化器:
class PersonSerializer(models.Serializer):
personal_data = serializers.DictField()
class Meta:
model = Person
make a function to add personal_data dictionary to persons object. Use this method before giving the list of person objects to the serializer.
创建一个向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)
#2
4
Because you want to only hit your database one time, a good way to add your extra data to your queryset is by adding a custom version of ListModelMixin
to your ViewSet that includes extra context:
因为您只希望一次访问数据库,所以向queryset添加额外数据的一个好方法是在您的ViewSet中添加一个自定义版本的ListModelMixin,其中包含额外的上下文:
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)
Then, access the extra context in your serializer by overriding the to_representation
method:
然后,通过覆盖to_representation方法访问序列化器中的额外上下文:
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