Django REST Framework Quickstart 项目解读

初衷

在2018年5月份接触并搭建过基于Django的后端平台,由于当时时间紧任务单一,很多细节也没去深究。但是我清除地记着我是有去找过Django REST相关的解读的,但是无果。最近有些时间,开始搭建一个较完整的基于Django的平台,结果发现“毒”还在,看来只能自制“解药”了。因为离上一次写类似博客的东西已经很久了,所以语言组织已经退化,不求写清楚了,但求和我一样的入门者有一个可聊的话题。

创建工程,添加一个应用

  • 基于virtualenv创建工作环境,远离python版本管理的烦恼
virtualenv -p /usr/local/bin/python3 py3env
source py3env/bin/activate
pip install django
pip install djangorestframework
  • 创建工程和应用
django-admin.py startproject tutorial .
cd tutorial
django-admin.py startapp quickstart
  • 默认使用sql数据库初始化数据库设置
python manage.py migrate
  • 创建管理员账号
python manage.py createsuperuser

关于Quickstart

这是一个官方的demo:http://www.django-rest-framework.org/tutorial/quickstart/, 目的是为了向开发者展示该组件的易用性,但是立足点似乎有点高,作为刚刚开始接触Django并想要了解REST组件的同学来说,这个例子的文档比较晦涩,而且绝大部分资料也只是对官方文档的翻译。该demo的功能,是实现一套简单的api(含视图),用于管理员查看和编辑Django的用户和组信息。

了解流程

由于Django本身是MVC(MVT)结构,便于开发,但不便于入门理解,简单地说,就是不便于在入门时理解Demo代码中的关联性。

  • Django的MTV模块

    • M:负责业务和数据库的关系映射
    • T:负责如何把html页面展示给用户
    • V:负责业务逻辑,调用M和T(MVC中C的角色)
  • Django对请求的处理流程并不是仅仅MTV三个模块

    Django处理流程

    其实从WSGI以后,都算作Django的工作范围,WSGI通过调用Django中的Django.core.wsgi的入口方法(wgsi.py文件)以后,就将请求的数据结构传递给Django的各种中间件,具体会流经哪些中间件?可见setting.py中的MIDDLEWARE配置(跨域过滤之类的一般会设置在中间件中)

  • Django app内部的流程会相对清晰一些:

    • URLconf是指urls.py选择一个视图来处理请求
    • 被选择的那个视图通常要做下面所列出的一件或者更多件事情:
      • 通过model与数据库对话
      • 使用模板渲染HTML或者任何格式化过的响应
      • 返回一个纯文本响应(不被显示的)(API)
    • 返回response

额外的重点:序列化

序列化和反序列化的目的就是对象和传输数据(或是存储数据)之间的转换。Django本身和Django REST 组件都提供了序列化的辅助类,目的是为了简化代码(可以想象一下JAVA里的序列化操作,虽然有工具可以生成代码),特别是ModelForm、ModelSerializer这些类,可以通过直接连接model模块,完成序列化和反序列化的工作。序列化功能的实现,往往是实现功能的第一步。我们可以理解成数据流驱动了编码顺序。

  • 序列化:对象 -> 字节序列(数据库或者response)
  • 反序列化:字节序列 -> 对象
    从上面的描述上看,序列化功能类一般会用在连接model、return response,以及数据库操作上。

再看Demo代码

1、真正的第一步应该是设计并建立数据库,而demo是要实现项目用户的操作,使用了自带的用户信息所在数据库。
2、实现序列化功能
在知道后端有什么(数据库),前端要什么之后,我们就可以设计序列化功能。因为数据库有什么我们就可以知道应该反序列出来什么,前端要什么,我们就知道需要序列化到response什么内容了。
Django提供了通用的serializers、ModelSerializer、HyperlinkedModelSerializer等封装等级不同和类型不同的序列化器辅助类,唯一的区别就是简化代码的程度不同,如果对序列化的具体操作不熟悉的话,可以参照https://darkcooking.gitbooks.io/django-rest-framework-cn/content/chapter1.html 使用普通的serializers来实现一遍该类会帮助很大。

from django.contrib.auth.models import User, Group
from rest_framework import serializers


class UserSerializer(serializers.HyperlinkedModelSerializer):

    """
    用户信息的序列化器(根据前端需要的数据信息来组织结构)
    """
    class Meta:
        model = User
        fields = ('url', 'username', 'email', 'groups')


class GroupSerializer(serializers.HyperlinkedModelSerializer):

    """
    组信息的序列化器
    """
    class Meta:
        model = Group
        fields = ('url', 'name')

3、实现View
这个Demo变得晦涩难懂的一大原因就是直接用了最简洁的代码方式,这里的viewsets就是其中之一,包括上面HyperlinkedModelSerializer。差别之处可以参考:https://darkcooking.gitbooks.io/django-rest-framework-cn/content/chapter6.html

"""
View中,必然会看到model(操作数据库)和序列化器(组织response内容)
"""
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from quickstart.serializers import UserSerializer, GroupSerializer


class UserViewSet(viewsets.ModelViewSet):

    """
    处理查看、编辑用户的界面
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer


class GroupViewSet(viewsets.ModelViewSet):

    """
    处理查看、编辑用户组的界面
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

为了理解这些晦涩的用法,还是总结下,Django Rest对视图写法的精简可以一步步归纳为:

  • 1、使用常规方式实现,最显式的实现方式,也是最好理解的方式:(读取数据库字节序列->反序列化->序列化后response)
@csrf_exempt
def snippet_list(request):
    """
    展示所以snippets,或创建新的snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JSONResponse(serializer.data)
    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)
  • 2、使用基于函数视图的@api_view装饰器,加快实现
@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    展示或创建snippets.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)
    elif request.method == 'POST':
        serializer = SnippetSerializer(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)
  • 3、使用基于类视图的APIView
class SnippetList(APIView):
    """
    List all snippets, or create a new snippet.
    """
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)
    def post(self, request, format=None):
        serializer = SnippetSerializer(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)
  • 4、基于ViewSets的抽象行为(配合Routers实现),该方法也可以简化urlconf,所以这可能就是Django生态的好处,让我们更加关注api的交互,但是这之前还是需要清楚这些方法的演变过程。从这个角度上看,读了N遍官方文档之后,能发现官方真正想要传达的意思。
class SnippetViewSet(viewsets.ModelViewSet):
    """
    This viewset automatically provides `list`, `create`, `retrieve`,
    `update` and `destroy` actions.
    Additionally we also provide an extra `highlight` action.
    """
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)
    @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

4、实现urlpatterns

使用viewsets和router后,由于封装处理了“get”、“post” 等默认动作,所以在url.py指出特定的uri便可,这就是所谓的RESTFul,因为所有的“动作”都没有暴露到api的命名中。

from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from rest_framework import routers
from quickstart import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)

# 使用URL路由来管理我们的API
# 另外添加登录相关的URL
urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    path('admin/', admin.site.urls),
]

5、运行测试。

python manage.py runserver

便可看到一套自带界面的api,一开始可能会有疑问,说好是写一套apis的,怎么成了一个界面了,其实就是Django MTV的福利(自带doc有没有),大家试着去访问:http://127.0.0.1:8000/users/?format=json ,就能看到一个正常的接口返回了。

运行结果

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,969评论 3 119
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,629评论 18 139
  • 听完永澄老师第三章的解读,有如下收获。 一、通过结构的重新梳理,内容理解更为透彻 我在学习时,自己做的笔记,就是一...
    从零再次起步阅读 224评论 0 1
  • 你才二十多岁,没有遇到喜欢的人很正常,越往后你会发现,大概是遇不到了。突然觉得十几二十岁还没有遇到爱的人很不正常,...
    嫱嫱99加阅读 213评论 0 0
  • 周一: 6:00 闹铃响了,迷糊10钟,起床。 6:40 吃早饭 7:20 做早操 7:40 整理寝室 8:30 ...
    袅雨阅读 100评论 0 0