Фильтрация по внешнему ключу в ViewSet, django-rest-framework
Я хочу, чтобы мой api возвращал определенные объекты из базы данных на основе внешнего ключа, полученного из пути url. Если мой url выглядит как api/get-club-players/1, я хочу получить каждый объект игрока с соответствующим идентификатором клуба (в данном случае club.id == 1). Я вставляю свой код ниже:
models.py
class Club(models.Model):
name = models.CharField(max_length=25)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
class Player(models.Model):
name = models.CharField(max_length=30)
club = models.ForeignKey(Club, on_delete=models.SET_NULL, blank=True, null=True)
def __str__(self):
return self.name
serialziers.py
class ClubSerializer(serializers.ModelSerializer):
class Meta:
model = Club
fields = 'id', 'owner', 'name'
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = 'id', 'name', 'offense', 'defence', 'club', 'position'
views.py, Это та часть, с которой у меня больше всего проблем:
class ClubViewSet(viewsets.ModelViewSet):
queryset = Club.objects.all()
serializer_class = ClubSerializer
class PlayerViewSet(viewsets.ModelViewSet):
queryset = Player.objects.all()
serializer_class = PlayerSerializer
class GetClubPlayersViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Player.objects.all()
serializer = PlayerSerializer(queryset, many=True)
def retrieve(self,request, clubId):
players = Player.objects.filter(club=clubId, many=True)
if not players:
return JsonResponse({'error': "No query found!"})
else:
serializer = PlayerSerializer(players)
return Response(serializer.data)
urls.py
from rest_framework import routers
from django.urls import path, include
from .views import (GameViewSet, PlayerViewSet, ClubViewSet,
GetClubPlayersViewSet, create_club, set_roster)
router = routers.DefaultRouter()
router.register(r'clubs', ClubViewSet, basename="clubs")
router.register(r'players', PlayerViewSet, basename="players")
router.register(r'get-club-players', GetClubPlayersViewSet, basename="club-players")
urlpatterns = [
path('', include(router.urls)),
]
Вы можете получить параметры url, используя атрибут kwargs. Для этого вам потребуется изменить сигнатуру вашего метода retrieve.
def retrieve(self, request, *args, **kwargs):
clubId = kwargs['get-club-players']
players = Player.objects.filter(club=clubId, many=True)
....
EDIT
Что касается ошибки queryset, то она связана с тем, что DRF требует либо атрибут класса queryset, либо реализацию функции get_queryset(). В вашем случае вы можете обойти это следующим образом:
class GetClubPlayersViewSet(viewsets.ViewSet):
queryset = Player.objects.all()
def list(self, request):
serializer = PlayerSerializer(self.queryset, many=True)
Так что вы можете определить свой кверисет как -
def get_queryset(self):
queryParams == self.request.GET.get('abc') # get queryparameter from url
if queryParams is None:
#queryset = anyModel.objects.all()
queryset = anyModel.objects.none()
else:
queryset = anyModel.objects.filter(anyProperty = queryParams)
return queryset
и ваш url будет выглядеть так -
api/get-club-players/?abc=1
это abc может быть id или любое другое свойство из модели.
Используйте эту логику get_queryset в своем методе retrieve.
rest_framework.viewsets.ViewSet имеет атрибут lookup_field, который можно переопределить. По умолчанию значение lookup_field равно id.
При добавлении набора представлений в маршрутизатор, lookup_field добавляется в качестве имени аргумента в url (например, /api/get-club-players/:id/).
Вы можете либо переопределить lookup_field из GetClubPlayersViewSet, либо получить доступ к правильному ключу kwargs, изменив
clubId = kwargs['get-club-players'] на clubId = kwargs['id']
Или немного и того, и другого:
class GetClubPlayersViewSet(viewsets.ViewSet):
lookup_field = "clubId"
queryset = Player.objects.all()
# ....
def retrieve(self, request, *args, **kwargs):
clubId = kwargs[self.lookup_field]
players = Player.objects.filter(club=clubId, many=True)
if not players:
return JsonResponse({'error': "No query found!"})
else:
serializer = PlayerSerializer(players)
return Response(serializer.data)