【问题标题】:Filtering by Foreign Key in ViewSet, django-rest-framework在 ViewSet 中按外键过滤,django-rest-framework
【发布时间】:2022-01-31 10:07:59
【问题描述】:

我希望我的 api 根据从 url 路径检索到的外键从数据库中返回某些对象。如果我的网址看起来像api/get-club-players/1,我希望每个玩家对象都具有匹配的俱乐部 ID(在本例中为 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)),
]

编辑: 现在 views.py 看起来像这样:

class GetClubPlayersViewSet(viewsets.ViewSet):
    queryset = Player.objects.all()

    def list(self, request):
        serializer = PlayerSerializer(self.queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, *args, **kwargs):
        clubId = kwargs['get-club-players']
        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)

http://127.0.0.1:8000/api/get-club-players/ 返回所有玩家对象,但是当我在 url 中添加一个 clubId 时,我得到了这个错误:

编辑 2

class GetClubPlayersViewSet(viewsets.ViewSet):
    queryset = Player.objects.all()
    
    def retrieve(self, request, *args, **kwargs):
        queryParams = self.request.GET.get('abc')
        if queryParams is None:
            queryset = Player.objects.none()
        else:
            queryset = Player.objects.filter(club = queryParams)
            serializer = PlayerSerializer(queryset)
        return Response(serializer.data)
    
    def list(self, request):
        serializer = PlayerSerializer(self.queryset, many=True)
        return Response(serializer.data)

【问题讨论】:

    标签: python django django-rest-framework django-rest-viewsets


    【解决方案1】:

    您可以使用 kwargs 属性获取 url 参数。您需要为它修改您的检索方法的签名。

    def retrieve(self, request, *args, **kwargs):
        clubId = kwargs['get-club-players']
        players = Player.objects.filter(club=clubId, many=True)
        ....
    

    编辑

    对于查询集错误,这是由于 DRF 需要 queryset 类属性或实现 get_queryset() 函数。在您的情况下,您可以像这样解决它:

    class GetClubPlayersViewSet(viewsets.ViewSet):    
        queryset = Player.objects.all()
    
        def list(self, request):
            serializer = PlayerSerializer(self.queryset, many=True)
    

    【讨论】:

    • 我收到“无法在未设置 .queryset 或具有 .get_queryset() 方法的视图上应用 DjangoModelPermissionsOrAnonReadOnly。”现在出错。你知道我该如何解决吗?
    • 检查我更新的答案。
    • 我遇到了另一个错误,这次是在我尝试输入此网址时clubId = self.kwargs['get-club-players'] 出现 KeyError http://127.0.0.1:8000/api/get-club-players/10/
    • 对不起,我的错误。试试这个新的。我不小心留下了 self.kwargs 而不是 kwargs
    • 同样的错误我上传了这个错误的截图和我改变的代码
    【解决方案2】:

    所以你可以定义你的查询集 -

    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
    

    你的网址会像 -

    api/get-club-players/?abc=1
    

    这个 abc 可以是 id 或模型中的任何其他属性。

    在您的检索方法中使用此 get_queryset 逻辑。

    【讨论】:

    • 它返回了所有的对象
    • request.GET 是一种非常糟糕的检索 URL 参数的做法。特别是在创建、更新或删除视图时更是如此。 @mateuszlaczynski 如果您正在使用这种 URL 参数检索方法,请确保 read through this article。对于仅获取模型对象的视图,它应该可以正常工作。
    • 好吧,仅在获取请求时使用它,如果您正确配置它,它将返回过滤后的查询集。显示您的检索方法。
    • 查看编辑 2
    • 但是你为什么要返回查询集,不要返回查询集。定义 serializer = PlayerSerializer(quesryset) 并返回 Response(serializer.data) 就像你在列表中所做的那样。
    【解决方案3】:

    rest_framework.viewsets.ViewSet 有一个名为 lookup_field 的属性,您可以覆盖该属性。默认情况下lookup_field 的值为id

    在路由器中添加视图集时,lookup_field 作为参数名称添加到 url(例如 /api/get-club-players/:id/)。

    您可以覆盖 GetClubPlayersViewSetlookup_field 或通过更改访问正确的 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)
    

    【讨论】:

      猜你喜欢
      • 2021-06-25
      • 2019-06-22
      • 2019-02-12
      • 2021-06-20
      • 1970-01-01
      • 1970-01-01
      • 2014-05-29
      • 2014-02-26
      • 1970-01-01
      相关资源
      最近更新 更多