Django REST framework流程

RESTfull

Web应用模式

在开发Web应用中,有两种应用模式:

  • 前后端不分离
  • 前后端分离

1.前后端不分离

在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示,前端与后端的耦合度很高。

这种应用模式比较适合纯网页应用,但是当后端对接App时,App可能并不需要后端返回一个HTML网页,而仅仅是数据本身,所以后端原本返回网页的接口不再适用于前端App应用,为了对接App后端还需再开发一套接口。

2.前后端分离

在前后端分离的应用模式中,后端仅返回前端所需的数据,不再渲染HTML页面,不再控制前端的效果。至于前端用户看到什么效果,从后端请求的数据如何加载到前端中,都由前端自己决定,网页有网页的处理方式,App有App的处理方式,但无论哪种前端,所需的数据基本相同,后端仅需开发一套逻辑对外提供数据即可。

在前后端分离的应用模式中 ,前端与后端的耦合度相对较低。

在前后端分离的应用模式中,我们通常将后端开发的每个视图都称为一个接口,或者API,前端通过访问接口来对数据进行增删改查。

认识RESTful

在前后端分离的应用模式里,后端API接口如何定义?

对于接口的请求方式与路径,每个后端开发人员可能都有自己的定义方式,风格迥异。

是否存在一种统一的定义方式,被广大开发人员接受认可的方式呢?

这就是被普遍采用的API的RESTful设计风格。

微博API

1. 起源

REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。

Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了关注,并且立即对互联网开发产生了深远的影响。

2. 名称

Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。维基百科称其为“具象状态传输”,国内大部分人理解为“表现层状态转化”。

RESTful是一种开发理念。维基百科说:REST是设计风格而不是标准。 REST描述的是在网络中client和server的一种交互形式;REST本身不实用,实用的是如何设计 RESTful API(REST风格的网络接口),一种万维网软件架构风格。

我们先来具体看下RESTful风格的url,比如我要查询商品信息,那么

非REST的url:http://…/queryGoods?id=1001&type=t01

REST的url:http://…/t01/goods/1001

可以看出REST特点:url简洁,将参数通过url传到服务器,而传统的url比较啰嗦,而且现实中浏览器地址栏会拼接一大串字符,想必你们都见过吧。但是采用REST的风格就会好很多,现在很多的网站已经采用这种风格了,这也是潮流方向,典型的就是url的短化转换。

RESTful设计思想

微博API

以下为新浪微博的API

请求响应

1. 域名

应该尽量将API部署在专用域名之下。

1
https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

1
https://example.org/api/
2. 版本(Versioning)

应该将API的版本号放入URL。

1
2
3
4
5
http://www.example.com/app/1.0/foo

http://www.example.com/app/1.1/foo

http://www.example.com/app/2.0/foo

另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):

1
2
3
4
5
Accept: vnd.example-com.foo+json; version=1.0

Accept: vnd.example-com.foo+json; version=1.1

Accept: vnd.example-com.foo+json; version=2.0
3. 路径(Endpoint)

路径又称”终点”(endpoint),表示API的具体网址,每个网址代表一种资源(resource)

(1) 资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。

举例来说,以下是不好的例子:

1
2
3
/getProducts
/listOrders
/retreiveClientByOrder?orderId=1

对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

1
2
3
4
GET /products :将返回所有产品清单
POST /products :将产品新建到集合
GET /products/4 :将获取产品 4
PUT /products/4 :将更新产品 4

(2) API中的名词应该使用复数。无论子资源或者所有资源。

举例来说,获取产品的API可以这样定义

1
2
获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
获取所有产品: http://127.0.0.1:8080/AppName/rest/products
4. HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • DELETE(DELETE):从服务器删除资源。

还有三个不常用的HTTP动词。

  • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

1
2
3
4
5
6
7
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
5. 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。

1
2
3
4
5
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoos/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

响应相关

1. 状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 200 OK - [GET]:服务器成功返回用户请求的数据
  • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
  • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
  • 204 NO CONTENT - [DELETE]:用户删除数据成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
  • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
  • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见这里这里

2. 错误处理(Error handling)

如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

1
2
3
{
error: "Invalid API key"
}
3. 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT /collection/resource:返回完整的资源对象
  • DELETE /collection/resource:返回一个空文档
4. 超媒体(Hypermedia API)

RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。

比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

1
2
3
4
5
{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}

从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

1
2
3
4
{
"message": "Requires authentication",
"documentation_url": "https://developer.github.com/v3"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。

5. 其他

服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

项目准备

创建项目+创建应用+安装应用+配置模板路径+本地化+mysql数据库+URLconf+视图

  • 安装应用,关闭csrf中间件

  • INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'book'
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        #'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    1
    2

    - mysql数据库使用之前的book
    DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': 'mysql', # 数据库用户密码 'NAME': 'book' # 数据库名字 } }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    - 模型:定义模型类

    - ```py
    # 准备书籍列表信息的模型类
    class BookInfo(models.Model):
    # 创建字段,字段类型...
    name = models.CharField(max_length=20, verbose_name='名称')
    pub_date = models.DateField(verbose_name='发布日期',null=True)
    readcount = models.IntegerField(default=0, verbose_name='阅读量')
    commentcount = models.IntegerField(default=0, verbose_name='评论量')
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
    image = models.ImageField(upload_to='book', null=True, verbose_name='图片')

    class Meta:
    db_table = 'bookinfo' # 指明数据库表名
    verbose_name = '图书' # 在admin站点中显示的名称

    def __str__(self):
    """定义每个数据对象的显示信息"""
    return self.name
    迁移,执行迁移后插入数据
    1
    2
    3
    4
    5
    insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values
    ('射雕英雄传', '1980-5-1', 12, 34, 0),
    ('天龙八部', '1986-7-24', 36, 40, 0),
    ('笑傲江湖', '1995-12-24', 20, 80, 0),
    ('雪山飞狐', '1987-11-11', 58, 24, 0);
  • 模型:定义模型类

  • # 准备人物列表信息的模型类
    class PeopleInfo(models.Model):
    
        name = models.CharField(max_length=20, verbose_name='名称')
        password = models.CharField(max_length=20,verbose_name='密码')
        description = models.CharField(max_length=200, null=True, verbose_name='描述信息')
        book = models.ForeignKey(BookInfo, related_name='people',on_delete=models.CASCADE, verbose_name='图书')  # 外键
        is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
    
        class Meta:
            db_table = 'peopleinfo'
            verbose_name = '人物信息'
    
        def __str__(self):
            return self.name
    
    1
    2

    迁移,执行迁移后插入数据
    insert into peopleinfo(name, password, book_id, description, is_delete) values ('郭靖', '123456abc', 1, '降龙十八掌', 0), ('黄蓉', '123456abc', 1, '打狗棍法', 0), ('黄药师', '123456abc', 1, '弹指神通', 0), ('欧阳锋', '123456abc', 1, '蛤蟆功', 0), ('梅超风', '123456abc', 1, '九阴白骨爪', 0), ('乔峰', '123456abc', 2, '降龙十八掌', 0), ('段誉', '123456abc', 2, '六脉神剑', 0), ('虚竹', '123456abc', 2, '天山六阳掌', 0), ('王语嫣', '123456abc', 2, '神仙姐姐', 0), ('令狐冲', '123456abc', 3, '独孤九剑', 0), ('任盈盈', '123456abc', 3, '弹琴', 0), ('岳不群', '123456abc', 3, '华山剑法', 0), ('东方不败', '123456abc', 3, '葵花宝典', 0), ('胡斐', '123456abc', 4, '胡家刀法', 0), ('苗若兰', '123456abc', 4, '黄衣', 0), ('程灵素', '123456abc', 4, '医术', 0), ('袁紫衣', '123456abc', 4, '六合拳', 0);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    - 视图:

    - ```py
    from django.views.generic import View
    from django.http.response import JsonResponse

    # Create your views here.
    class BookListView(View):
    """
    图书列表
    """
    def get(self,request):

    return JsonResponse({'msg':'ok'})
  • URLconf

    • settings.py中:指定url配置

      1
      ROOT_URLCONF = 'bookmanager.urls'
    • 项目中urls.py:只要不是admin/就匹配成功,包含到应用中的urls.py

      1
      2
      3
      4
      5
      6
      7
      8
      9
      from django.urls import path,include
      from django.contrib import admin

      urlpatterns = [
      path('admin/', include(admin.site.urls)),

      # 只要不是‘admin/’就匹配成功,包含到应用中的urls.py
      path('', include('book.urls')),
      ]
  • 应用中urls.py:匹配books/成功就调用views中的BookListView视图,测试项目逻辑

    1
    2
    3
    4
    5
    from django.urls import path
    from book import views
    urlpatterns = [
    path('books/',views.BookListView.as_view()),
    ]
  • 测试项目逻辑

使用Django开发REST 接口

我们以在Django框架中使用的图书英雄案例来写一套支持图书数据增删改查的REST API接口,来理解REST API的开发。

在此案例中,前后端均发送JSON格式数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# views.py

from django.views.generic import View
from book.models import BookInfo
from django.http import JsonResponse,HttpResponse
import json

# Create your views here.
class BookListView(View):
"""
查询所有图书、增加图书
"""
def get(self, request):
"""
查询所有图书
路由:GET /books/
"""
queryset = BookInfo.objects.all()
book_list = []
for book in queryset:
book_list.append({
'id': book.id,
'name': book.name,
'pub_date': book.pub_date
})
return JsonResponse(book_list, safe=False)

def post(self, request):
"""
新增图书
路由:POST /books/
"""
json_bytes = request.body
json_str = json_bytes.decode()
book_dict = json.loads(json_str)

# 此处详细的校验参数省略

book = BookInfo.objects.create(
name=book_dict.get('name'),
pub_date=book_dict.get('pub_date')
)

return JsonResponse({
'id': book.id,
'name': book.name,
'pub_date': book.pub_date
},safe=False)

class BookDetailView(View):
"""
获取单个图书信息
修改图书信息
删除图书
"""
def get(self, request, pk):
"""
获取单个图书信息
路由: GET /books/<pk>/
"""
try:
book = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return JsonResponse({},status=404)

return JsonResponse({
'id': book.id,
'name': book.name,
'pub_date': book.pub_date
})

def put(self, request, pk):
"""
修改图书信息
路由: PUT /books/<pk>
"""
try:
book = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return JsonResponse({},status=404)

json_bytes = request.body
json_str = json_bytes.decode()
book_dict = json.loads(json_str)

# 此处详细的校验参数省略

book.name = book_dict.get('name')
book.pub_date = book_dict.get('pub_date')
book.save()

return JsonResponse({
'id': book.id,
'name': book.name,
'pub_date': book.pub_date
})

def delete(self, request, pk):
"""
删除图书
路由: DELETE /books/<pk>/
"""
try:
book = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return JsonResponse({},status=404)

book.delete()

return JsonResponse({},status=204)
from django.urls import path
from book.views import BookListView,BookDetailView

urlpatterns = [
path('books/',BookListView.as_view()),
path('books/<int:pk>/', BookDetailView.as_view()),
]

测试

使用Postman测试上述接口

1) 获取所有图书数据

GET 方式访问http://127.0.0.1:8000/books/,返回状态码200,数据如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[
{
"id": 2,
"commentcount": 40,
"pub_date": "1986-07-24",
"readcount": 36,
"name": "天龙八部",
"image": ""
},
{
"id": 3,
"commentcount": 18,
"pub_date": "1995-12-24",
"readcount": 28,
"name": "笑傲江湖",
"image": ""
},
{
"id": 4,
"commentcount": 24,
"pub_date": "1987-11-11",
"readcount": 58,
"name": "雪山飞狐",
"image": ""
},
{
"id": 5,
"commentcount": 0,
"pub_date": "2000-05-01",
"readcount": 0,
"name": "新射雕英雄传",
"image": ""
},
{
"id": 6,
"commentcount": 0,
"pub_date": "1990-05-01",
"readcount": 0,
"name": "射雕英雄传",
"image": ""
}
]

2)获取单一图书数据

GET 访问http://127.0.0.1:8000/books/2/,返回状态码200, 数据如下

1
2
3
4
5
6
7
8
{
"id": 2,
"commentcount": 40,
"pub_date": "1986-07-24",
"readcount": 36,
"name": "天龙八部",
"image": ""
}

GET 访问http://127.0.0.1:8000/books/100/,返回状态码404

3)新增图书数据

POST 访问http://127.0.0.1:8000/books/ ,发送JSON数据

1
2
3
4
5
{

"pub_date": "1990-05-01",
"name": "python入门"
}

返回状态码201,数据如下

1
2
3
4
5
6
7
8
{
"id": 7,
"commentcount": 0,
"pub_date": "1990-05-01",
"readcount": 0,
"name": "python入门",
"image": ""
}

4)修改图书数据

PUT 访问http://127.0.0.1:8000/books/7/,发送JSON数据

1
2
3
4
5
{

"pub_date": "1990-05-01",
"name": "python高级"
}

返回状态码200,数据如下

1
2
3
4
5
6
7
8
{
"id": 7,
"commentcount": 0,
"pub_date": "1990-05-01",
"readcount": 0,
"name": "python高级",
"image": ""
}

5)删除图书数据

DELETE 访问http://127.0.0.1:8000/books/7/,返回204状态

明确REST接口开发的核心任务

分析一下上节的案例,可以发现,在开发REST API接口时,视图中做的最主要有三件事:

  • 将请求的数据(如JSON格式)转换为模型类对象
  • 操作数据库
  • 将模型类对象转换为响应的数据(如JSON格式)

序列化Serialization

维基百科中对于序列化的定义:

序列化(serialization)在计算机科学的资料处理中,是指将数据结构或物件状态转换成可取用格式(例如存成档案,存于缓冲,或经由网络中传送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始物件相同语义的副本。对于许多物件,像是使用大量参照的复杂物件,这种序列化重建的过程并不容易。面向对象中的物件序列化,并不概括之前原始物件所关联的函式。这种过程也称为物件编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组, deserialization, unmarshalling)。

序列化在计算机科学中通常有以下定义:

在数据储存与传送的部分是指将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等,或者透过网络传送资料时进行编码的过程,可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这程序被应用在不同应用程序之间传送对象,以及服务器将对象储存到档案数据库。相反的过程又称为反序列化

简而言之,我们可以将序列化理解为:

将程序中的一个数据结构类型转换为其他格式(字典、JSON、XML等),例如将Django中的模型类对象装换为JSON字符串,这个转换过程我们称为序列化。

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
books = BookInfo.objects.all()
# [Bookinfo,BookInfo,BOokinfo]

#json: [{"id":1,"name":"abc"},{"id":1,"name":"abc"}]
#将对象转换为JSON数据
list = []

for book in books:
list.append({
'id':book.id,
'name':book.name,
'pub_date':book.pub_date
})


#返回json数据
return JsonResponse(list,safe=False)

反之,将其他格式(字典、JSON、XML等)转换为程序中的数据,例如将JSON字符串转换为Django中的模型类对象,这个过程我们称为反序列化。

如:

1
2
3
4
5
6
7
8
9
10
#获取post 的数据
json_bytes = request.body
json_str = json_bytes.decode()

json_dict = json.loads(json_str)
#将POST的数据保存到库里
book = BookInfo.objects.create(
name=json_dict.get('name'),
pub_date = json_dict.get('pub_date')
)

我们可以看到,在开发REST API时,视图中要频繁的进行序列化与反序列化的编写。

总结

在开发REST API接口时,我们在视图中需要做的最核心的事是:

  • 将数据库数据序列化为前端所需要的格式,并返回;
  • 将前端发送的数据反序列化为模型类对象,并保存到数据库中。

Django REST framework 简介

  1. 在序列化与反序列化时,虽然操作的数据不尽相同,但是执行的过程却是相似的,也就是说这部分代码是可以复用简化编写的。
  2. 在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:
    • :校验请求数据 -> 执行反序列化过程 ->保存数据库 ->将保存的对象序列化并返回
    • :判断要删除的数据是否存在 -> 执行数据库删除
    • :判断要修改的数据是否存在 ->校验请求的数据 -> 执行反序列化过程 ->保存数据库 ->将保存的对象序列化并返回
    • :查询数据库 -> 将数据序列化并返回

Django REST framework可以帮助我们简化上述两部分的代码编写,大大提高REST API的开发速度。

认识Django REST framework

Django REST framework 框架是一个用于构建Web API 的强大而又灵活的工具。

通常简称为DRF框架 或 REST framework。

DRF框架是建立在Django框架基础之上,由Tom Christie大牛二次开发的开源项目。

特点

  • 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
  • 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
  • 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
  • 多种身份认证和权限认证方式的支持;
  • 内置了限流系统;
  • 直观的 API web 界面;
  • 可扩展性,插件丰富

资料:

环境安装与配置

Django REST framework需要以下依赖:

  • Python (3.5, 3.6, 3.7, 3.8)
  • Django (1.11, 2.0, 2.1, 2.2, 3.0)

Django REST framework是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)

1. 安装Django REST framework

1
pip install djangorestframework

2. 添加rest_framework应用

我们利用在Django框架学习中创建的demo工程,在settings.pyINSTALLED_APPS中添加’rest_framework’。

1
2
3
4
INSTALLED_APPS = [
...
'rest_framework',
]

接下来就可以使用Django REST framework进行开发了。


本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!