Django Rest Framework - 添加JOIN端点

时间:2016-07-28 02:08:54

标签: python django rest

我正在使用具有以下端点的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,并使用ViewSet而不是像这样编写一次性函数视图。

有什么想法吗?

1 个答案:

答案 0 :(得分: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/<pk>/,具有为每个商家设置的权限,具体取决于他们是谁,通过创建自定义权限,例如:

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/<pk>/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/<pk>/xyz。还有列表路线(@list_route);名为xyz的{​​{1}}对应user/xyz(例如user/add_item)。

以上结构将为您提供:/user/user/<pk>user/<pk>/items/items/items/<pk>,但 (正如我错误地试图实现的那样)user/<user_pk>/items/<items_pk>。相反,user/<pk>/items会为您提供用户列表,但只能通过/items/<pk>访问其各自的属性。

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

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