Django Rest Framework视图集-基于用户名问题的ForeignKey过滤器

时间:2019-01-23 03:32:51

标签: django django-rest-framework django-views

我有一个django项目,并且已经将Django Rest Framework集成到了后端项目中。我有一个Profile模型。在概要文件模型中,我有一个具有用户名字段的用户外键。用户名是我当前用于过滤配置文件的名称。

ListAPIViewRetrieveAPIView分开时,我一切正常。

class ProfileListView(ListAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer

class ProfileDetailView(RetrieveAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer

    def get_object(self):
        return self.queryset.get(user__username=self.kwargs.get('username')) 

另一件事是,当视图分开时,我在传递到视图的url中设置了一个username参数。我不确定如何将其集成到视图集中。

path('users/', UserListView.as_view()),
path('users/<username>', UserDetailView.as_view()),

这就是我现在拥有的

router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'profiles', ProfileViewSet, basename='profile')
urlpatterns = router.urls

我正在尝试更改代码,以使每个视图都无法使用一个通用视图集,而不是每个视图都要单独使用。

使用Web浏览器中的API时,列表视图有效,但是当我尝试搜索特定的用户名时,出现错误。

DoesNotExist at /api/users/profiles/omarjandali/
Profile matching query does not exist.
Request Method: GET
Request URL:    http://localhost:8000/api/users/profiles/omarjandali/
Django Version: 2.1.5
Exception Type: DoesNotExist
Exception Value:    
Profile matching query does not exist.
Exception Location: /Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7/site-packages/django/db/models/query.py in get, line 399
Python Executable:  /Users/omarjandali/anaconda3/envs/splittapp/bin/python
Python Version: 3.7.2
Python Path:    
['/Users/omarjandali/Documents/splittapp/backend/src',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python37.zip',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7/lib-dynload',
 '/Users/omarjandali/anaconda3/envs/splittapp/lib/python3.7/site-packages']
Server time:    Wed, 23 Jan 2019 03:21:44 +0000

1 个答案:

答案 0 :(得分:1)

问题是视图集具有从lookup_field继承的GenericAPIView。它本质上是一个用于在视图集的详细信息端点中检索单个对象的字段。默认情况下,它通常是id字段,因此它尝试使用id=omarjandali搜索配置文件,但由于它是用户名,因此当然不存在。

因此,如果要使用用户名而不是ID进行查找,则必须指定lookup_field作为用户名。所以你应该有这样的东西:

class MyViewSet(vieswts.ModelViewSet):
    ...
    lookup_field = 'user__username'
    ...

请记住,这意味着您不能再通过其id来检索个人资料。

要允许使用多个查询字段进行更复杂的查询,您必须覆盖get_object()方法并在其中实现逻辑。

为此,您可以使用我构建并在项目中使用的混入。

class AlternateLookupFieldsMixin(object):
"""
Looks up objects for detail endpoints using alternate
lookup fields assigned in `alternate_lookup_fields` apart
from the default lookup_field. Only unique fields should be used
else Http404 is raised if multiple objects are found
"""

alternate_lookup_fields = []

def get_object(self):
    try:
        return super().get_object()
    except Http404:
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
        queryset = self.filter_queryset(self.get_queryset())
        obj = None
        for lookup_field in self.alternate_lookup_fields:
            filter_kwargs = {lookup_field: self.kwargs[lookup_url_kwarg]}
            try:
                obj = get_object_or_404(queryset, **filter_kwargs)
            except Http404:
                pass
        if obj:
            self.check_object_permissions(self.request, obj)
            return obj
        raise Http404

使用方法:

将混合添加到视图集中,并在alternate_lookup_fields列表中指定用于查找的其他字段。这意味着您可以(很可能应该)允许将原始lookup_field作为ID并在列表中添加额外的查找字段。

请记住,只应使用唯一字段进行查找,否则会遇到发现多个对象的问题。

因此,在您的情况下,使用mixin看起来像这样:

class MyViewSet(AlternateLookupFieldsMixin, vieswts.ModelViewSet):
    ...
    alternate_lookup_fields = ['user__username']
    ...