【问题标题】:Django Rest Framework viewset - ForeignKey filtering filter based on Username IssueDjango Rest Framework 视图集 - 基于用户名问题的 ForeignKey 过滤器
【发布时间】:2019-01-23 03:32:50
【问题描述】:

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

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 中设置了一个用户名参数。我不确定如何将其集成到视图集中。

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

我正在尝试更改我的代码,以便我希望使用一个通用视图集,而不是每个视图都分开,它是一个包含所有视图的单点。

当我在网络浏览器中使用查看 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

【问题讨论】:

  • self.queryset.get(user__username=self.kwargs.get('username')) 中的问题。此错误告诉我们无法与此查询匹配。你能在self.kwargs.get('username') 中显示价值吗?

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


【解决方案1】:

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

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

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

请记住,这意味着您无法再通过他们的id 检索个人资料。

要允许使用多个查找字段进行更复杂的查找,您必须重写 get_object() 方法并在那里实现逻辑。

为此,您可以使用我在项目中构建和使用的这个 mixin。

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

如何使用:

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

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

因此,在您的情况下,使用 mixin 将如下所示:

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

【讨论】:

  • 我是否需要对视图集的 url 进行任何更改,以确保它知道正在传递用户名。
  • 不,视图集已经为 pk 定义了一个允许的 kwarg 正则表达式。它接受范围广泛的值,包括用户名
  • 这是一个小问题。所以我有一个具有用户外键的模型。每个用户可以有多个帐户模型记录。当我在 url 末尾添加用户名时,我希望它过滤掉具有该用户外键的帐户。多于 ! RECORD RETURNED 正在抛出错误。我使用了`def get_object(self): return self.queryset.get(user__username=self.kwargs.get('username'))`来过滤它并且不得不使用第二个列表视图,
  • 如果您确实阅读了,那是我添加到答案中的警告。永远不要使用非唯一字段进行这样的查找。如果多个帐户可以连接到同一个用户,则不要使用用户的用户名进行查找。您需要使用一个确保每次只返回一个对象的字段
  • 您想要对非唯一字段执行的操作是过滤而不是查找。这意味着您只需使用列表视图过滤连接到该用户的所有帐户。查看 django-filter 和 DRF 文档以了解如何实现过滤。如果您遇到另一个问题,请为它发布一个不同的问题,因为它们是不同的东西。由于这回答了您最初的问题,因此请不要忘记接受并投票。祝你好运!
猜你喜欢
  • 2019-06-22
  • 1970-01-01
  • 1970-01-01
  • 2021-06-17
  • 2019-07-11
  • 2018-11-19
  • 2015-11-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多