Как эффективно поразить базу данных для связанного поля в сериализаторе REST? - PullRequest
0 голосов
/ 26 августа 2018

У меня сложная архитектура для базы данных, и у меня возникают проблемы при написании соответствующего REST API.

Контекст: я каждый час запрашиваю различную информацию об общедоступном API, включая данные, которые я хочу сохранить в историиfor.

Вот модели, которые я использую

class Player(models.Model):
    name = models.CharField(max_length=255)

class PlayerStatsHistory(models.Model):
    player = models.ForeignKey('Player', null=True, on_delete=models.CASCADE)
    last_refresh = models.DateTimeField(null=True)
    ...

Таким образом, я могу сохранять каждое изменение в статистике каждого пользователя.Я написал 2 сериализатора, один для Player и один для PlayerStatsHistory.Самый простой работает нормально

class PlayerStatsSerializer(HyperlinkedModelSerializer):
    class Meta:
        model = PlayerStatsHistory
        fields = ('last_refresh', ...)

Но когда мне нужно запросить последнюю статистику для одного игрока, я запутываюсь:

class PlayerSerializer(HyperlinkedModelSerializer):
    details = SerializerMethodField()

    def get_details(self, obj):
        return PlayerStatsSerializer(PlayerStatsHistory.objects.filter(player=obj).order_by('-last_refresh').first()).data

    class Meta:
        model = Player
        fields = ('name', 'details')

Это работает нормально, но попадет в базу данных накаждый игрок предоставил PlayerSerializer, и я чувствую, что делаю это неправильно.

Как я могу улучшить это решение?

1 Ответ

0 голосов
/ 26 августа 2018

Я думаю, что лучшее, что вы можете сделать, это сначала получить идентификатор последнего PlayerStatsHistory каждого игрока (используя group_by):

latest_stats_history_pks = PlayerStatsHistory.objects.values('player').annotate(max_id=models.Max('id')).values_list('max_id', flat=True)

(у него есть проблема, он использует всех игроковесли вы используете разбиение на страницы, вам не нужны все из них, в этом случае предварительная выборка без фильтрации набора запросов должна быть в порядке)

, тогда только предварительную выборку этих значений следует выполнять в вашем Player наборе запросов, поэтому:

       queryset = Player.objects.all().prefetch_related(models.Prefetch(
        'playerstatshistory_set',
        queryset=PlayerStatsHistory.objects.filter(pk__in=latest_stats_history_pks), to_attr='last_stat_list'))

поэтому, наконец, ваш get_queryset метод в вашем представлении должен выглядеть следующим образом:

def get_queryset(self):

    latest_stats_history_pks = PlayerStatsHistory.objects.values('player').annotate(max_id=models.Max('id')).values_list('max_id', flat=True)

    queryset = Player.objects.all().prefetch_related(models.Prefetch(
        'playerstatshistory_set',
        queryset=PlayerStatsHistory.objects.filter(pk__in=latest_stats_history_pks), to_attr='last_stat_list'))

    return queryset

, и если вы используете FBV, сделайте что-то вроде этого:

from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['GET'])
def player_list(request, format=None):
    if request.method == 'GET':
        latest_stats_history_pks = PlayerStatsHistory.objects.values('player').annotate(max_id=models.Max('id')).values_list('max_id', flat=True)

        players = Player.objects.all().prefetch_related(models.Prefetch(
        'playerstatshistory_set',
        queryset=PlayerStatsHistory.objects.filter(pk__in=latest_stats_history_pks), to_attr='last_stat_list'))

        serializer = PlayerSerializer(players, many=True)
        return Response(serializer.data)

также в вашем сериализаторе, измените это поле, как показано ниже:

class PlayerSerializer(HyperlinkedModelSerializer):
    details = SerializerMethodField()

    def get_details(self, obj):
        return {} if not obj.last_stat_list else PlayerStatsSerializer(obj.last_stat_list[-1]).data

    class Meta:
        model = Player
        fields = ('name', 'details')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...