drf学习

概述

真是非常优秀的一个框架,能想到的都提供了,啥都不用写,代码很简洁

Serialization

Creating a Serializer class and Using ModelSerializers

# class SnippetSerializer(serializers.Serializer):
#     id = serializers.IntegerField(read_only=True)
#     title = serializers.CharField(required=False, allow_blank=True, max_length=100)
#     code = serializers.CharField(style={'base_template': 'textarea.html'})
#     linenos = serializers.BooleanField(required=False)
#     language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
#     style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
#
#
#     # 需要手动实现create和update方法
#     def create(self, validated_data):
#         return Snippet.objects.create(**validated_data)
#
#     def update(self, instance, validated_data):
#         instance.title = validated_data.get('title', instance.title)
#         instance.code = validated_data.get('code', instance.code)
#         instance.linenos = validated_data.get('linenos', instance.linenos)
#         instance.language = validated_data.get('language', instance.language)
#         instance.style = validated_data.get('style', instance.style)
#         instance.save()
#         return instance

# 利用ModelSerializer简化
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']

Writing regular Django views using our Serializer

@csrf_exempt # 暂时取消csrf
def snippet_list(request):
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        # many=True序列化多个实例
        serializer = SnippetSerializer(snippets, many=True)
        # .data为一个orderdict对象列表,rest_framework.parsers中自带JSONParser解析器
        return JsonResponse(serializer.data, safe=False)
    elif request.method == 'POST':
        data = JSONParser().parse(request) # 这里把整个request传了
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse(serializer.errors, status=400)


@csrf_exempt
def snippet_detail(request, pk):
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)
    if request.method == 'GET':
        serializer = SnippetSerializer(snippet) # 把对象序列化直接返回,不用构造返回参数
        return JsonResponse(serializer.data)
    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data)
    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)

Requests and Responses

Request objects

REST framework 引入了一个Request扩展常规的对象HttpRequest,并提供更灵活的请求解析。Request对象的核心功能是request.data属性,它类似于request.POST,但对于使用 Web API 更有用。

request.POST  # Only handles form data.  Only works for 'POST' method.
request.data  # 处理任意数据。适用于“POST”、“PUT”和“PATCH”方法。

Response objects

REST 框架还引入了一个Response对象,它是一种TemplateResponse接受未渲染内容并使用内容协商来确定返回给客户端的正确内容的类型。

return Response(data)  # 呈现为客户端请求的内容类型。

Status codes

REST framework 为每个状态代码提供了更明确的标识符,例如HTTP_400_BAD_REQUEST在status模块中。

Wrapping API views

REST framework provides two wrappers you can use to write API views.

  1. The @api_view decorator for working with function based views.
  2. The APIView class for working with class-based views.

These wrappers provide a few bits of functionality such as making sure you receive Request instances in your view, and adding context to Response objects so that content negotiation can be performed.

The wrappers also provide behaviour such as returning 405 Method Not Allowed responses when appropriate, and handling any ParseError exceptions that occur when accessing request.data with malformed input.

e.g.

@api_view(['GET', 'PUT', 'DELETE'])  # there
def snippet_detail(request, pk, format=None):
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)  # there

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)  # there

    elif request.method == 'PUT':
        serializer = SnippetSerializer(snippet, data=request.data)  # there
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)  # there
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)  # there

    elif request.method == 'DELETE':
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)  # there

请注意,我们不再明确地将我们的请求或响应绑定到给定的内容类型。 request.data可以处理传入json的请求,但它也可以处理其他格式。同样,我们正在返回带有数据的响应对象,但允许 REST 框架将响应呈现为我们正确的内容类型(http/json)。

Adding optional format suffixes to our URLs

为了利用我们的响应不再硬连线到单一内容类型这一事实,让我们为我们的 API 端点添加对格式后缀的支持。使用格式后缀为我们提供了明确引用给定格式的 URL,这意味着我们的 API 将能够处理诸如http://example.com/api/items/4.json之类的 URL 。

# urls.py
from rest_framework.urlpatterns import format_suffix_patterns  # 可选格式后缀支持
urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>/', views.snippet_detail)
]

urlpatterns = format_suffix_patterns(urlpatterns)

这样用,注意最后的"/"

http http://127.0.0.1:8000/snippets/ Accept:application/json  # Request JSON
http http://127.0.0.1:8000/snippets/ Accept:text/html         # Request HTML
http http://127.0.0.1:8000/snippets.json  # JSON suffix
http http://127.0.0.1:8000/snippets.api   # Browsable API suffix
http --form POST http://127.0.0.1:8000/snippets/ code="print(123)" # form data
http --json POST http://127.0.0.1:8000/snippets/ code="print(456)" # json data

Browsability

由于 API 根据客户端请求选择响应的内容类型,因此默认情况下,当 Web 浏览器请求该资源时,它将返回资源的 HTML 格式表示。这允许 API 返回完全可通过 Web 浏览的 HTML 表示。

拥有一个可浏览网页的 API 是一个巨大的可用性胜利,并使开发和使用你的 API 变得更加容易。它还显着降低了其他想要检查和使用您的 API 的开发人员的进入门槛。

Class-based Views

我们也可以使用基于类的视图而不是基于函数的视图来编写我们的 API 视图。正如我们将看到的,这是一个强大的模式,它允许我们重用通用功能,并帮助我们保持代码DRY。

Rewriting

class SnippetDetail(APIView):
    # crud
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Using mixins

使用基于类的视图的一大优势是它允许我们轻松地组合可重用的行为部分。

到目前为止,我们一直在使用的创建/检索/更新/删除操作对于我们创建的任何模型支持的 API 视图都将非常相似。这些常见行为在 REST 框架的 mixin 类中实现。

class SnippetDetail(mixins.RetrieveModelMixin,  # 查
                    mixins.UpdateModelMixin,    # 改
                    mixins.DestroyModelMixin,   # 删
                    generics.GenericAPIView):   # 构建视图
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

Using generic class-based views

使用 mixin 类,我们重写了视图以使用比以前略少的代码,但我们可以更进一步。 REST 框架提供了一组已经混合的通用视图,我们可以使用它们来进一步精简我们的 views.py 模块。

class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

Authentication & Permissions

目前我们的 API 对谁可以编辑或删除代码片段没有任何限制。我们希望有一些更高级的行为,以确保:

  • 代码片段始终与创建者相关联。
  • 只有经过身份验证的用户才能创建片段。
  • 只有片段的创建者可以更新或删除它。
  • 未经身份验证的请求应具有完全只读访问权限。

Adding information to our model

# 模型类中添加
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)

Adding endpoints for our User models

# 序列化类中添加
class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all()) # endpoints

Associating Snippets with Users

关联用户,重写.perform_create()

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

Updating our serializer

# serializers.py/SnippetSerializer
owner = serializers.ReadOnlyField(source='owner.username')

Adding required permissions to views

view中添加验证类

# 这将确保经过身份验证的请求获得读写访问权限,而未经身份验证的请求获得只读访问权限。
permission_classes = [permissions.IsAuthenticatedOrReadOnly]

Adding login to the Browsable API

urlpatterns += [
    path('api-auth/', include('rest_framework.urls')),
]
# 'api-auth/'模式部分实际上可以是您想要使用的任何登录URL 。

Object level permissions

我们希望任何人都可以看到所有代码片段,但还要确保只有创建代码片段的用户才能更新或删除它。

为此,我们需要创建一个自定义权限。

# permissions.py
from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user  # 只有创建代码片段的用户才能更新或删除它

Authenticating with the API

因为我们现在对 API 拥有一组权限,所以如果我们想编辑任何片段,我们需要验证我们对它的请求。我们尚未设置任何身份验证类,因此当前应用默认值,即SessionAuthentication和BasicAuthentication。

当我们通过 Web 浏览器与 API 交互时,我们可以登录,然后浏览器会话将为请求提供所需的身份验证。

如果我们以编程方式与 API 交互,我们需要在每个请求上显式提供身份验证凭据。

Relationships & Hyperlinked APIs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值