我有以下模型和序列化程序,目标是在序列化程序运行时只有一个查询:
型号:
class MongoUserSerializer(DocumentSerializer):
assignees = AssigneeSerializer(many=True)
class Meta:
model = MongoUser
fields = ('id', 'email', 'first_name', 'last_name', 'assignees')
depth = 2
class AssigneeSerializer(EmbeddedDocumentSerializer):
class Meta:
model = Assignee
fields = ('assignee_first_name', 'assignee_last_name', 'user')
depth = 0
串行器:
class AssigneeSerializer(EmbeddedDocumentSerializer):
class Meta:
model = Assignee
fields = ('assignee_first_name', 'assignee_last_name', 'user_id')
depth = 0
当检查mongo分析器时,我有2个查询MongoUser文档。如果我从MongoUserSerializer中删除了assignees字段,那么只有一个查询。
作为一种解决方法,我尝试使用user_id字段仅存储ObjectId并将AssigneeSerializer更改为:
fields = ('assignee_first_name', 'assignee_last_name', 'user_id')
但是又有2个查询。我认为序列化程序EmbeddedDocumentSerializer获取ReferenceField和
的所有字段和查询{{1}}
在查询完成后起作用。 如何使用ReferenceField,而不是在序列化时为每个引用运行单独的查询?
答案 0 :(得分:2)
我最终得到了一种解决方法,而不是使用ReferenceField。相反,我使用的是ObjectIdField:
#user = ReferenceField("MongoUser", required=True) # Removed now
user = ObjectIdField(required=True)
更改了值分配如下:
- if assignee.user == MongoUser:
+ if assignee.user == MongoUser.id:
这不是最好的方式 - 我们没有使用ReferenceField功能,但它比在序列化器中创建30个查询更好。
最诚挚的问候, 克里斯蒂安
答案 1 :(得分:1)
这是一个非常有趣的问题,我认为它与Mongoengine的DeReference政策有关:https://github.com/MongoEngine/mongoengine/blob/master/mongoengine/dereference.py。
即,你的mongoengine文档有一个方法MongoUser.objects.select_related()
max_depth
参数应该足够大,以至于Mongoengine遍历3个深度级别:MongoUser
- > assignees
- > Assignee
- > user
并缓存当前MongoUser
实例的所有相关MongoUser
个对象。也许,我们应该在DRF-Mongoengine的DocumentSerializers中的某个地方调用这个方法来预取关系,但目前我们还没有。
请参阅this post关于经典DRF + Django ORM解释,如何通过在经典DRF中进行预取来解决N + 1请求问题。基本上,您需要覆盖get_queryset()
的{{1}}方法才能使用ModelViewSet
方法:
select_related()
不幸的是,我不认为DRF-Mongoengine中的from rest_framework_mongoengine.viewsets import ModelViewSet
class MongoUserViewSet(ModelViewSet):
def get_queryset(self):
queryset = MongoUser.objects.all()
# Set up eager loading to avoid N+1 selects
queryset.select_related(max_depth=3)
return queryset
current implementation足够聪明,可以恰当地处理这些查询集。可能ReferenceField
会有效吗?
尽管如此,我还没有使用过这个功能,也没有足够的时间自己玩这些设置,所以如果你分享了你的发现,我会感激你。