致虚极 守静笃
视图
2020-04-21发布 371

基础视图

现在来写一个简单的函数视图:

# article/views.py
from article.serializers import ArticleSerializer
from article.models import Article
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser


@csrf_exempt
def article_list(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = ArticleSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)

        return JsonResponse(serializer.errors, status=400)

以上视图函数将列出所有已存在的Article对象,并且接受POST请求来新增文章。这里使用了装饰器csrf_exempt,因为Django为了防止CSRF攻击要求POST请求需要带上CSRF token,但是我们这里要用工具来简单测试一下,所以加上装饰器用于忽略。

除了list视图,我们再添加一个detail视图,一遍通过id来访问具体的文章:

@csrf_exempt
def article_detail(request, pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return JsonResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = ArticleSerializer(article, data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data)
        return JsonResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        article.delete()
        return HttpResponse(status=204)

别忘了修改article/urls.py

urlpatterns = [
    path('articles/', views.article_list),
    path('articles/<int:pk>/', views.article_detail),
]

现在可以使用一些工具来测试一下我们的API了,如果你熟练使用Postman,那么下面的内容可以忽略了。

Linux各个发行版基本都默认安装了curl,不过这里我推荐使用httpie这款工具,使用pip install httpie安装工具。

$ http http://127.0.0.1:8000/articles/
HTTP/1.1 200 OK
......

[
    {
        "body": "React is good",
        "created": "2020-03-21T21:22:55.553124",
        "id": 3,
        "title": "React",
        "updated": "2020-03-21T21:22:55.553182"
    },
    ......
]

获取第一篇文章:

$ http http://127.0.0.1:8000/articles/1/
HTTP/1.1 200 OK
......

{
    "body": "React is good",
    "created": "2020-03-21T21:10:53.922033",
    "id": 1,
    "title": "React",
    "updated": "2020-03-21T21:10:53.922128"
}

http命令后跟POSTPUT等,可执行相应操作,如

http POST http://127.0.0.1:8000/articles/ title='hello' body='一起来写 代码吧'

详情请查看官方文档。或者你更喜欢图形界面工具,可以去试试功能强大的Postman

API view

虽然现在我们的API已经能工作了,但是使用原生的视图有点麻烦,REST framework为我们提供了一些方便的封装:

# 改写article/views.py
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from article.serializers import ArticleSerializer
from article.models import Article


@api_view(['GET', 'POST'])
def article_list(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PUT', 'DELETE'])
def article_detail(request, pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

现在代码量相比之前减少了一些,REST framework为我们封装了RequestResponse类,详情请浏览官网

类视图

聪明的你想必已经想到了,REST framework也如Django一样为我们提供了类视图:

# 导入相关类
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from django.http import Http404
from article.serializers import ArticleSerializer
from article.models import Article

类视图:

class ArticleList(APIView):
    def get(self, request, format=None):
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

详情视图:

class ArticleDetail(APIView):
    def get_object(self, pk):
        try:
            return Article.objects.get(pk=pk)
        except Article.DoesNotExist:
            raise Http404

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

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = ArticleSerializer(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)

通过实现诸如getput等方法,实现对不同HTTP请求的处理。对于类视图,我们需要修改一下article/urls.py

urlpatterns = [
    path('articles/', views.ArticleList.as_view()),
    path('articles/<int:pk>/', views.ArticleDetail.as_view()),
]

现在使用工具测试,API又能正常工作啦。

viewsets

REST framework为我们提供了一个更强大的视图集合,这里包括了一些常用的视图,直接上代码:

from rest_framework import viewsets
from article.serializers import ArticleSerializer
from article.models import Article


class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

我们写的ArticleViewSet类只有两行代码,但是却涵盖了之前所有的功能。viewsets可以与官方提供的路由系统routers搭配使用,修改article/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from article import views


router = DefaultRouter()
router.register(r'articles', views.ArticleViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

注意:确保你的项目urls.py里包含了article的urls。

# 项目级urls
urlpatterns = [
    path('', include('article.urls')),
]

我们准备做前后端分离开发,那么其实这里并不需要再去写模板了,但是为了方便调试,REST framework为了我提供了默认的一套模板,所以如果你打开浏览器访问默认的地址127.0.0.1:8000,是可以看到页面的。另外,强大的路由系统还贴心地为我们提供了首页

这里只是简略介绍一下几种编写视图的方法,要深入了解还是要去看官方文档,viewsets非常强大,但是当你需要高度定制化的时候缺失了一些灵活性,后续会根据需要选择使用,简单功能一律直接使用viewsets,等需要定制内容时再详细介绍。

版权声明