Предотвращение необходимости использования одного и того же предложения select_related для нескольких представлений в DRF

Учитывая следующие модели...

class Player(models.Model):
    user = models.ForeignKey(User)


class Activity(models.Model):
    player = models.ForeignKey(Player)

и эти сериализаторы...

class PlayerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Player
        fields = ['user']


class ActivitySerializer(serializers.ModelSerializer):
    player = PlayerSerializer()

    class Meta:
        model = Activity
        fields = ['player']

если я хочу использовать django-rest-framework для составления списка всех действий, мне нужно сделать что-то вроде этого...

class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Activity.objects.select_related("player__user") <--- this is needed because the activity serializer is going to serialize the player which has a user
    serializer_class = ActivitySerializer

Все это хорошо. Но тогда каждый раз, когда я буду писать новое представление, которое на каком-то уровне использует PlayerSerializer, мне придется не забывать делать правильный select_related clause, чтобы сохранить низкое количество обращений к БД.

В итоге я собираюсь написать множество представлений, которые имеют одинаковые select_related clauses (мои на самом деле намного сложнее, чем этот пример), и, если PlayerSerializer когда-нибудь изменится, мне нужно будет не забыть изменить все представления.

Мне это кажется не очень DRY, и я чувствую, что должен быть лучший способ сделать это. Может быть, я упустил что-то очевидное?

Можно иметь базовый родительский класс, например:

class BaseViewset(viewsets.ReadOnlyModelViewSet):
    SELECT_RELATED_FIELD = None  # for providing select related field in your queryset or maybe have a default value for this one (based on your use case)
    
    def get_queryset(self):
        if not self.SELECT_RELATED_FIELD or not isinstance(self.SELECT_RELATED_FIELD, str):
            raise NotImplementedError("Ensure 'SELECT_RELATED_FIELD' is not empty/ Is instance of string")
        return super().get_queryset().select_related(self.SELECT_RELATED_FIELD)

А затем используйте этот родительский класс во всех ваших представлениях вместо ReadOnlyModelViewSet:

class ActivityViewSet(BaseViewset):
    SELECT_RELATED_FIELD = "player__user"
    queryset = Activity.objects.all()
    serializer_class = ActivitySerializer

было бы хорошим началом (во всех представлениях, которые у вас есть select_related часть).

Вернуться на верх