如何在SerializerMethodFields之间共享一个查询集?

时间:2017-02-24 19:57:13

标签: django django-rest-framework

我有一个序列化程序,用于显示团队在体育联盟中的表现细节。

class TeamLeagueSerializer(ModelSerializer):
    class Meta:
        model = Team
        fields = [...]
    games_played = fields.SerializerMethodField()
    games_won = fields.SerializerMethodField()
    games_lost = fields.SerializerMethodField()
    points_for = fields.SerializerMethodField()
    ...etc...

    def fixtures(self, team):
        # Retrieves all relevant fixtures for a given season and a given team
        return Fixture.objects.filter(Q(home_team=team) | Q(away_team=team),  season=self.season)

    def get_games_won(self, obj):
        home_results = Q(home_team=obj, result__home_team_score__gt=F('result__away_team_score'))
        away_results = Q(away_team=obj, result__away_team_score__gt=F('result__home_team_score'))
        results = self.fixtures(obj).filter(home_results | away_results)
        return results.count()

    def get_games_lost(self, obj):
        home_results = Q(home_team=obj, result__home_team_score__lt=F('result__away_team_score'))
        away_results = Q(away_team=obj, result__away_team_score__lt=F('result__home_team_score'))
        results = self.fixtures(obj).filter(home_results | away_results)
        return results.count()

这一切都正常,但是对于一个由8个团队组成的联盟,由于每个SerializerMethodField是一个单独的查询,所以这可能需要大约5秒才能完成。

我的非api版本的代码使用单个查询集更加简单,该查询集注释了此序列化程序尝试创建的每个属性,而不是单个查询。

因此,对于给定的序列化程序,是否可以执行相同的操作并从单个查询而不是多个查询构造序列化程序数据?

2 个答案:

答案 0 :(得分:1)

几种可能的解决方案:

1)将它们组合成一个字段

class TeamLeagueSerializer(ModelSerializer):
    class Meta:
        model = Team
        fields = [...]
    details = fields.SerializerMethodField()

    def get_details(self, team):
        #your non-api version of the code
        return {games_played: fixtures, games_won: won, games_lost: lost}

2)或者如果你真的想保持API相同,另一种可能性是将每个属性设置为模型上的属性,并有一个额外的缓存属性,例如self._team_info来存储查询的结果供将来使用:

class Team(models.Model):
     #your fields

    @property
    def team_info(self):
        if not hasattr(self, "_team_info"):
               #Your non-api version of the code
               self._team_info = #result of your query containing fixture info, games won, games lost, etc, .e.g. in a dictionary
        return self._team_info

    @property
    def fixtures(self):
        return self.team_info['fixtures']

    @property
    def games_won(self):
        return self.team_info['games_won']

    @property
    def games_lost(self):
        return self.team_info['games_won']

然后在序列化程序中你不需要设置任何SerializerMethodFields,你可以在你的fields属性中引用games_won,games_lost和fixtures:

class TeamLeagueSerializer(ModelSerializer):
    class Meta:
        model = Team
        fields = [..., 'fixtures', 'games_won', 'games_lost']

答案 1 :(得分:1)

您可以通过设置queryset属性或覆盖get_queryset在ViewSet级别指定自定义查询。

http://www.django-rest-framework.org/api-guide/generic-views/#get_querysetself

该查询可能与您在非api版本中使用的查询相同。

该方法的结果将直接提供给Serializer,因此每个额外的注释都可用。您可能需要在序列化程序中使用read_only=True标志定义带注释的字段。