【问题标题】:Django Rest Framework - adding JOIN endpointsDjango Rest Framework - 添加 JOIN 端点
【发布时间】:2016-07-28 02:08:54
【问题描述】:

我正在使用具有以下端点的 Django Rest Framework (DRF) 创建 REST API:

/users/
/users/<pk>/
/items/
/items/<pk>/

但我想添加端点:

/users/<pk>/items/

这当然会将属于(具有外键)该用户的项目返回。

目前我的代码是:

#########################
##### myapp/urls.py #####
#########################

from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from rest_framework.decorators import api_view, renderer_classes
from rest_framework import response, schemas
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer

from myapp.views import ItemViewSet, UserViewSet

# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'items', ItemViewSet)

@api_view()
@renderer_classes([OpenAPIRenderer, SwaggerUIRenderer])
def schema_view(request):
    generator = schemas.SchemaGenerator(title='My API')
    return response.Response(generator.get_schema(request=request))

urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]

##########################
##### myapp/views.py #####
##########################

from django.contrib.auth import get_user_model
from rest_framework import viewsets, permissions 

from myapp.serializers import MyUserSerializer, ItemSerializer
from myapp.models import Item 


class UserViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = get_user_model().objects.all()
    serializer_class = MyUserSerializer
    permission_classes = (permissions.IsAuthenticated,)

class ItemViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Item.objects.all()
    serializer_class = ItemSerializer
    permission_classes = (permissions.IsAuthenticated,)

################################
##### myapp/serializers.py #####
################################

from rest_framework import serializers
from django.contrib.auth import get_user_model

from myapp.models import Item

class MyUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = ('pk', 'email',)

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ('pk', 'name',)

鉴于我使用 DRF 的方式,有没有一种在 DRF 中添加此端点的好方法?

我可以像这样在urls.py 中添加一个函数视图:

from myapp.views import items_for_user

urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^users/(?P<pk>[0-9]+)/items/$', items_for_user),
]

但我想利用 DRF,获取可浏览的 API,并使用 ViewSets,而不是像这样编写一次性函数视图。

有什么想法吗?

【问题讨论】:

    标签: python django rest


    【解决方案1】:

    我花了一段时间才弄清楚这一点。我一直在使用视图集,所以我会在该设置中给出这个答案。

    首先,URLConf和注册的路由保持不变,即,

    router = DefaultRouter()
    router.register(r'users', UserViewSet)
    router.register(r'items', ItemViewSet)
    
    urlpatterns = [
        url(r'^', include(router.urls)),
        url(r'^api-auth/',
            include('rest_framework.urls', namespace='rest_framework')
        ),
    ]
    

    您的项目仍将位于/items/&lt;pk&gt;/,并根据谁请求它们为每个项目创建权限,例如通过创建自定义权限:

    class IsItemOwnerPermissions(permissions.DjangoObjectPermissions):
        """
        The current user is the owner of the item.
        """
        def has_object_permission(self, request, view, obj):
            # A superuser?
            if request.user.is_superuser:
                return True
            # Owner
            if obj.owner.pk == request.user.pk:
                return True
            return False
    

    接下来,对于/user/&lt;pk&gt;/items/,您需要定义@detail_route,如下所示:

    class UserViewSet(viewsets.ReadOnlyModelViewSet):
        # Your view set properties and methods
        @detail_route(
            methods=['GET', 'POST'],
            permission_classes=[IsItemOwnerPermissions],
        )
        def items(self, request, pk=None):
            """
            Returns a list of all the items belonging to `/user/<pk>`.
            """
            user = get_user_model().objects.get(pk=pk)
            items = user.items.all()
            page = self.paginate_queryset(items)
            if page is None:
                serializer = ItemSerializer(
                    objs, context={'request': request}, many=True
                )
                return Response(serializer.data)
            else:
                serializer = ItemSerializer(
                    page, context={'request': request}, many=True
                )
                return self.get_paginated_response(serializer.data)
    

    名为xyz 的详细路由对应于路由user/&lt;pk&gt;/xyz。还有列表路线(@list_route);一个名为xyz 将对应于user/xyz(例如user/add_item)。

    上面的结构会给你:/user/user/&lt;pk&gt;user/&lt;pk&gt;/items/items,和/items/&lt;pk&gt;,但是不是(因为我错误地试图实现)@987654340 @。相反,user/&lt;pk&gt;/items 将为您提供用户项目的列表,但它们的各个属性仍只能通过 /items/&lt;pk&gt; 访问。

    我刚刚将它用于我的项目,上面的代码可以快速适应您的情况。希望对您有所帮助,但那里可能仍然存在问题。

    更新:您可以使用custom hyperlinked fields 完成您想要的操作。我(还)没有尝试过,所以我不能说如何使用这些,但是那个链接上有很好的例子。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-25
      • 2015-08-31
      相关资源
      最近更新 更多