django rest框架的多个lookup_fields

Ale*_*Grs 9 python django rest django-rest-framework

我有多个API,历史上id用作查找字段:

/api/organization/10
Run Code Online (Sandbox Code Playgroud)

我有一个消耗这些api的前端.

我正在构建一个新的界面,出于某些原因,我想使用slug而不是id:

/api/organization/my-orga
Run Code Online (Sandbox Code Playgroud)

API是使用Django Rest Framework构建的.除了查找字段的更改外,api行为应该保持不变.

有没有一个解决方案允许我的API同时使用a slug和a pk?这两条路径应该给他们相同的结果:

/api/organization/10
/api/organization/my-orga
Run Code Online (Sandbox Code Playgroud)

这是我的API定义:

# urls.py
router = DefaultRouter()
router.register(r'organization', Organization)
urlpatterns = router.urls

#view.py
class Organization(viewsets.ModelViewSet):
    queryset = OrganisationGroup.objects.all()
    serializer_class = OrganizationSerializer

# serializer.py
class OrganizationSerializer(PermissionsSerializer):
    class Meta:
        model = Organization
Run Code Online (Sandbox Code Playgroud)

谢谢你的帮助.

itz*_*nTV 9

试试这个

from django.db.models import Q
import operator
class MultipleFieldLookupMixin(object):
    def get_object(self):
        queryset = self.get_queryset()             # Get the base queryset
        queryset = self.filter_queryset(queryset)  # Apply any filter backends
        filter = {}
        for field in self.lookup_fields:
            filter[field] = self.kwargs[field]
        q = reduce(operator.or_, (Q(x) for x in filter.items()))
        return get_object_or_404(queryset, q)
Run Code Online (Sandbox Code Playgroud)

然后在视图中

class Organization(MultipleFieldLookupMixin, viewsets.ModelViewSet):
    queryset = OrganisationGroup.objects.all()
    serializer_class = OrganizationSerializer
    lookup_fields = ('pk', 'another field')
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • 我认为它没有回答这个问题,因为在这种情况下,URL 必须同时包含 pk 和其他字段。 (4认同)

Moh*_*edi 9

我知道您很久以前就问过这个问题,但考虑到视图和网址,这是我从所有答案中得到的完整解决方案:

将其放入您的views.py中:(通过drf进行一些编辑)

class MultipleFieldLookupMixin(object):

def get_object(self):
    queryset = self.get_queryset()             
    queryset = self.filter_queryset(queryset)  
    filter = {}
    for field in self.lookup_fields:
        if self.kwargs.get(field, None):  
            filter[field] = self.kwargs[field]
    obj = get_object_or_404(queryset, **filter)  # Lookup the object
    self.check_object_permissions(self.request, obj)
    return obj
Run Code Online (Sandbox Code Playgroud)

然后从这个Mixin继承你的视图并添加你想要的字段到lookup_fields。像这样:

class YourDetailView(MultipleFieldLookupMixin, RetrieveUpdateAPIView):
    ...
    lookup_fields = ['pk', 'slug','code']
Run Code Online (Sandbox Code Playgroud)

在 urls.py 中:

re_path(r'^organization/(?P<pk>[0-9]+)/$',
        YourDetailView),
re_path(r'^organization/(?P<slug>[-a-zA-Z0-9_]+)/$',
        YourDetailView),
re_path(r'^organization/sth_else/(?P<code>[0-9]+)/$',
        YourDetailView),
Run Code Online (Sandbox Code Playgroud)


Hik*_* G. 5

我通过覆盖retrieve方法并pk根据任何模式检查字段的值来解决类似的问题。例如,如果它仅由数字组成。

def retrieve(self, request, *args, **kwargs):
    if kwargs['pk'].isdigit():
        return super(Organization, self).retrieve(request, *args, **kwargs)
    else:
        # get and return object however you want here.
Run Code Online (Sandbox Code Playgroud)

  • Afaik,这个解决方案完全绕过了检查对象权限的 GenericAPIView.get_object 。 (2认同)