【问题标题】:Reducing queries on serialization with SerializerMethodField使用 SerializerMethodField 减少对序列化的查询
【发布时间】:2020-07-27 10:57:58
【问题描述】:

我目前有一个序列化程序,它使用两个 SerializerMethodField 访问同一个嵌套对象,导致两个数据库调用:

# models.py
class Onboarding(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    is_retailer_created = models.BooleanField(default=False)
    is_complete = models.BooleanField(default=False)

# views.py
class StateView(RetrieveAPIView):
    serializer_class = serializers.UserSerialiser

    def get_object(self):
        return self.request.user

# serializers.py
class UserSerialiser(serializers.ModelSerializer):
    is_onboarded = serializers.SerializerMethodField()
    is_loyalty_onboarded = serializers.SerializerMethodField()

    class Meta:
        model = models.User
        fields = ('is_onboarded', 'is_loyalty_onboarded')

    def get_is_onboarded(self, obj):
        onboarding = obj.onboarding_set.first()
        if onboarding:
            return onboarding.is_retailer_created
        return False

    def get_is_loyalty_onboarded(self, obj):
        onboarding = obj.onboarding_set.first()
        if onboarding:
            return onboarding.is_complete
        return False

如果可能的话,我想把它整合到一个电话中。通常可以只使用prefetch_related,但由于get_object 返回的是特定用户(而不是queryset),我认为该解决方案在这里不起作用。

有没有办法让用户预取Onboarding 模型?还是只调用一次Onboarding 而不是两次?

【问题讨论】:

  • .first() 看起来很奇怪。在以随机顺序检索所有记录之后。所以想象is_retailer_created 对于某些人来说是True 而对于其他人来说是False。那么这意味着结果将是随机的。
  • 通常您可以使用.annotate(..) 对每个组执行聚合/子查询:docs.djangoproject.com/en/dev/ref/models/querysets/#annotate
  • @WillemVanOnsem - 这是预期行为 - 目前,每个用户只有一个入职模型,所以现在 .first() 可以(尽管它是错误代码),稍后会更新 -就annotate 而言,我看不出在这种情况下它是如何工作的,因为没有查询集可以操作。我仍然会为每个SerializerMethodField 调用两次,不是吗?
  • 那么应该使用OneToOneField,因为这是一个具有唯一性约束的ForeignKey,因此这意味着每个User 都有一个Onboarding。至于get_object。诀窍是在get_queryset 中进行注释,因为get_object 采用查询集,然后获取一条记录(例如pk 匹配的位置)。因此,即使是提供get_object 方法的SingleObjectMixin,也会在后台使用get_queryset。
  • 您可以分享您正在使用的视图/视图集吗?

标签: django django-models serialization django-rest-framework


【解决方案1】:

事实证明这比预期的要容易一些。只需要将Onboarding 对象保存在__init__ 中即可。

# serializers.py
class UserSerialiser(serializers.ModelSerializer):
    is_onboarded = serializers.SerializerMethodField()
    is_loyalty_onboarded = serializers.SerializerMethodField()

    class Meta:
        model = models.User
        fields = ('is_onboarded', 'is_loyalty_onboarded')

    def __init__(self, *args, **kwargs):
        super(UserSerialiser, self).__init__(*args, **kwargs)
        self.onboarding = self.instance.onboarding_set.first()

    def get_is_onboarded(self, obj):
        if self.onboarding:
            return self.onboarding.is_retailer_created
        return False

    def get_is_loyalty_onboarded(self, obj):
        if self.onboarding:
            return self.onboarding.is_complete
        return False

【讨论】:

    猜你喜欢
    • 2023-01-07
    • 2018-02-28
    • 2019-11-03
    • 2021-08-01
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 2016-07-21
    • 2019-04-28
    相关资源
    最近更新 更多