Django Rest Framework 介绍(re_path写法)序列化和反序列化

 在没有ajax和不知道前端name的情况下接受post请求的值

 Rest Framework

 先安装

注册进去

 

在子应用的views中写个类去继承Rest Framework

(1)指定一个查询的模型类

(2)等serializers.py里完成序列化后在指定刚刚写好的类

ModeSerializer

在子应用中写一个serializers.py文件去做序列化

fields=('name','put')       指定要做序列化的字段

exclude =('image',)       单独排除这个字段

1

 对应路由配置在子路由中导入Rest Framework的指定路由

 单独注册到uils中

Serializer单独去做序列化

是为了在这判断后直接上到数据库中

 用外键去点上对应的序列化类可以得到全部的数据

 

## 引入DjangoRESTframework

### 1.Web 应用模式

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

- 前后端不分离

- 前后端分离

#### 1.1 前后端不分离

![1611729769910](DRF.assets/1611729769910.png)

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

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

#### 1.2 前后端分离

![1611729952523](DRF.assets/1611729952523.png)

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

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

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

### 2.认识RESTful

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

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

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

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

- 例如对于后端数据库中保存了商品的信息,前端可能需要对商品数据进行增删改查,那相应的每个操作后端都需要提供一个API接口:

| 请求方法 | 请求地址 | 后端操作          |

| -------- | -------- | ----------------- |

| GET      | /goods   | 获取所有商品      |

| POST     | /goods   | 增加商品          |

| GET      | /goods/1 | 获取编号为1的商品 |

| PUT      | /goods/1 | 修改编号为1的商品 |

| DELETE   | /goods/1 | 删除编号为1的商品 |

#### 2.1 起源

REST这个词,是[Roy Thomas Fielding](http://en.wikipedia.org/wiki/Roy_Fielding)在他2000年的[博士论文](http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)中提出的。

![1611730118168](DRF.assets/1611730118168.png)

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

#### 2.2 说明

- RESTful是一种开发理念**,REST是设计风格而不是标准**

- 公司中的后端程序员在定义后端的接口时,可以选择是否遵守这种设计风格;

- RESTful设计风格的API,每种请求方法,都对应后端一种数据库操作。

### 3.RESTful设计方法

#### 3.1 域名

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

```

https://api.example.com

```

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

```

https://example.org/api/

```

前后端分离,前端域名和后端域名分开

#### 3.2 版本

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

```

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](https://developer.github.com/v3/media/#request-specific-version)采用这种做法。

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见[Versioning REST Services](http://www.informit.com/articles/article.aspx?p=1566460)):

```

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.3 路径(Endpoint)

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

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

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

`

/getProducts

/listOrders

/retreiveClientByOrder?orderId=1

```

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

```

GET /products :将返回所有产品清单

POST /products :将产品新建到集合

GET /products/4 :将获取产品 4

PATCH(或)PUT /products/4 :将更新产品 4

```

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

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

```

获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1

获取所有产品: http://127.0.0.1:8080/AppName/rest/products

```

#### 3.4 HTTP动词

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

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

- GET(SELECT):从服务器取出资源(一项或多项)。

- POST(CREATE):在服务器新建一个资源。

- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。

- DELETE(DELETE):从服务器删除资源。

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

- PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。

- HEAD:获取资源的元数据。

- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

```

GET /zoos:列出所有动物园

POST /zoos:新建一个动物园(上传文件)

GET /zoos/ID:获取某个指定动物园的信息

PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)

PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)

DELETE /zoos/ID:删除某个动物园

GET /zoos/ID/animals:列出某个指定动物园的所有动物

DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

```

#### 3.5 过滤信息(Filtering)

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

下面是一些常见的参数。

```

?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 的含义是相同的。通过传参来把过滤的数据传过来

#### 3.6 状态码(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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见[这里](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)或[这里](https://zh.wikipedia.org/wiki/HTTP状态码)。

#### 3.7 错误处理(Error handling)

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

```json

{

    error: "Invalid API key"

}

```

#### 3.8  返回结果

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

- GET /collection:返回资源对象的列表(数组)

- GET /collection/resource:返回单个资源对象

- POST /collection:返回新生成的资源对象

- PUT /collection/resource:返回完整的资源对象

- PATCH /collection/resource:返回完整的资源对象

- DELETE /collection/resource:返回一个空文档

#### 3.9 超媒体(Hypermedia API)

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

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

```json

{

"current_user_url": "https://api.github.com/user",

"authorizations_url": "https://api.github.com/authorizations",

// ...

}

```

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

```json

{

  "message": "Requires authentication",

  "documentation_url": "https://developer.github.com/v3"

}

```

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

#### 3.10  其他

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

###  4.使用Django开发REST接口

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

在此案例中,前后端均发送JSON格式数据。(注意要关掉Csrf,(注释掉Settings的MIDDLEWARE里面的Csrf))

```python

"""

  GET       /goods      获取所有商品  

  POST      /goods      增加商品    

  GET       /goods/<pk>/    获取编号为1的商品

  PUT       /goods/<pk>/    修改编号为1的商品

  DELETE    /goods/<pk>/    删除编号为1的商品

响应数据  JSON

列表视图:路由后面没有pk/ID

详情视图:路由后面 pk/ID

"""

from django.views import View

class BookListView(View):

    """列表视图""" 

    def get(self, request):

        """查询所有图书接口"""

        pass

    def post(self, request):

        """新增图书接口"""

        pass

class BookDetailView(View):

    """详情视图"""

   

    def get(self, request, pk):

        """查询指定某个图书接口"""

        pass

    def put(self, request, pk):

        """修改指定某个图书接口"""

        pass

    def delete(self, request, pk):

        """删除指定某个图书接口"""

        pass

   

# from django.urls import path, re_path

# path('books/',views.BookListView.as_view())

# re_path(r'^books/(?P<pk>\d+)/$',views.BookDetailView.as_view())  

# 为什么要分成两个类视图?

  1.一个类里面不能有两个get

  2.路由定义是列表视图没有PK,详情视图有pk  

``

详细实现

```python

from django.views import View

class BookListView(View):

    """列表视图"""

    def get(self, request):

        """查询所有图书接口"""

        # 1.查询所有图书接口

        books = BookInfo.objects.all()

        # 2. 遍历查询集,取出每个书籍模型对象,把模型对象转换成字典

        # 定义一个列表变量用来保存所有字典

        book_list = []

        for book in books:

            book_dict = {

                # 通过键获取值

                'id': book.id,

                'name': book.name,

                'pub_date': book.pub_date,

                'readcount': book.readcount,

                'commentcount': book.commentcount,

                'image': book.image.url if book.image else ''

            }

            book_list.append(book_dict)

        # 响应

        print(book_list)

        return JsonResponse(book_list, safe=False)

        # return JsonResponse("你好", safe=False)

    def post(self, request):

        """新增图书接口"""

        # 获取前端传入的请求体数据

        json_bytes = request.body

        # 把bytes类型的json字符串转换成json_str

        json_str = json_bytes.decode()

        # 将json字符串转换成字典

        book_dict = json.loads(json_str)

        # 创建模型对象并保存(把字典转换成模型并存储)

        book = BookInfo.objects.create(

            name=book_dict['name'],

            pub_date=book_dict['pub_date']

        )

        book_dict = {

            'id': book.id,

            'pub_date': book.pub_date,

            'readcount': book.readcount,

            'commentcount': book.commentcount,

            'image': book.image.url if book.image else ''

        }

        return JsonResponse(book_dict, status=201)


 

class BookDetailView(View):

    """详情视图"""

    def get(self, request, pk):

        """查询指定某个图书接口"""

        # 1.获取出指定pk的那个模型对象

        try:

            book = BookInfo.objects.get(id=pk)

        except BookInfo.DoesNotExist:

            return HttpResponse({"message", "查询的数据不存在"}, status=404)

        # 2.模型对象转字典

        book_dict = {

            'id': book.id,

            'pub_date': book.pub_date,

            'readcount': book.readcount,

            'commentcount': book.commentcount,

            'image': book.image.url if book.image else ''

        }

        return JsonResponse(book_dict, safe=False)

    def put(self, request, pk):

        """修改指定某个图书接口"""

        try:

            book = BookInfo.objects.get(id=pk)

        except BookInfo.DoesNotExist:

            return HttpResponse({"message", "查询的数据不存在"}, status=404)

        # 获取前端传入的请求体数据

        json_bytes = request.body

        # 把bytes类型的json字符串转换成json_str

        json_str = json_bytes.decode()

        # 将json字符串转换成字典

        book_dict = json.loads(json_str)

        # 重新给模型对象赋值

        book.name = book_dict['name']

        book.pub_date = book_dict['pub_date']

        # 调用save方法进行修改操作

        book.save()

        json_dict = {

            'id': book.id,

            'name': book.name,

            'pub_date': book.pub_date,

            'image': book.image.url if book.image else ''

        }

        # 响应

        return JsonResponse(json_dict)

    def delete(self, request, pk):

        """删除指定某个图书接口"""

        # 获取要删除的模型对象

        try:

            book = BookInfo.objects.get(id=pk)

        except BookInfo.DoesNotExist:

            return HttpResponse({"message", "查询的数据不存在"}, status=404)

        # 删除模型对象

        book.delete()  # 物理删除(真正从数据库中删除)

        # book.is_delete = True

        # book.save() # 逻辑删除

        # 响应:删除时不需要有响应体但要指定状态码为 204

        return HttpResponse(status=204)

```





 

查询效果:

![1611736530457](DRF.assets/1611736530457.png)

单个查询:

![1611745806893](DRF.assets/1611745806893.png)

新增效果

![1611745723623](DRF.assets/1611745723623.png)

![1611745822479](DRF.assets/1611745822479.png)



 

修改效果:

![1611746396134](DRF.assets/1611746396134.png)

删除效果:

![1611746481413](DRF.assets/1611746481413.png)

可以发现,在开发REST API接口时,视图中做的最主要有三件事:

- 将请求的数据(如JSON格式)转换为模型类对象

- 操作数据库

- 将模型类对象转换为响应的数据(如JSON格式)



 

## DRF 工程搭建

### 1.DRF 介绍

1. 在序列化与反序列化时,虽然操作的数据不尽相同,但是执行的过程却是相似的,也就是说这部分代码是可以复用简化编写的。

2. 在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:

   - **增**:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

   - **删**:判断要删除的数据是否存在 -> 执行数据库删除

   - **改**:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

   - **查**:查询数据库 -> 将数据序列化并返回

3.DRF将序列化和反序列化的业务逻辑进⾏了封装,程序员只需要将序列化和反序列化的数据传给DRF即可

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

**认识Django REST framework**

![1611753351019](DRF.assets/1611753351019.png)

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

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

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

**特点**

- 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;

- 提供了丰富的类视图、Mixin扩展类,简化视图的编写;

- 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;

- 多种身份认证和权限认证方式的支持;

- 内置了限流系统;

- 直观的 API web 界面;

- 可扩展性,插件丰富

资料:

- [官方文档](http://www.django-rest-framework.org/)

- [Github源码](https://github.com/encode/django-rest-framework/tree/master)

  ### 2.环境安装与配置

  DRF需要以下依赖:

  - Python (3.5, 3.6, 3.7, 3.8, 3.9)

  - Django (2.2, 3.0, 3.1)

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

**1. 安装DRF**

```shell

pip install djangorestframework

```

**2. 添加rest_framework应用**

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

```python

INSTALLED_APPS = [

    ...

    'rest_framework',

]

```

接下来就可以使用DRF进行开发了。

### 3.见识DRF的魅力

我们仍以在学习Django框架时使用的图书英雄为案例,使用Django REST framework快速实现图书的REST API。

#### 3.1 创建序列化器

在booktest应用中新建serializers.py用于保存该应用的序列化器。

创建一个Serializer.py文件,再创建一个BookInfoSerializer用于序列化与反序列化。

```python

from rest_framework import serializers

from book.models import BookInfo      #

class BookInfoSerializer(serializers.ModelSerializer):

    """图书数据序列化器"""

    class Meta:

        model = BookInfo

        fields = '__all__'

```

- **model** 指明该序列化器处理的数据字段从模型类BookInfo参考生成

- **fields** 指明该序列化器包含模型类中的哪些字段,'__all__'指明包含所有字段

#### 3.2 编写视图

在booktest应用的views.py中创建视图BookInfoViewSet,这是一个视图集合。

```python

from .models import BookInfo

from rest_framework.viewsets import ModelViewSet

from book.serializer import BookInfoSerializer

class BookInfoView(ModelViewSet):

    """定义类视图"""

    """指定查询集"""

    queryset = BookInfo.objects.all()

    serializer_class = BookInfoSerializer

```

- **queryset** 指明该视图集在查询数据时使用的查询集

- **serializer_class** 指明该视图在进行序列化或反序列化时使用的序列化器

#### 3.3  定义路由

在book应用的urls.py中定义路由信息。

```python

from book import views

from rest_framework.routers import DefaultRouter

urlpatterns = [

    # path('books/', views.BookListView.as_view()),

    # re_path(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view())

]

router = DefaultRouter()  # 可以处理视图的路由器

router.register(r'books', views.BookInfoView)  # 向路由器中注册视图集

urlpatterns += router.urls  # 将路由器中的所以路由信息追到到django的路由列表中

```

注意:要将前面写的对应路由注释,否则以之前写的为准

#### 3.4. 运行测试

运行当前程序(与运行Django一样)

```shell

python manage.py runserver

```

在浏览器中输入网址127.0.0.1:8000,可以看到DRF提供的API Web浏览页面:

![1611757921421](DRF.assets/1611757921421.png)

1)点击链接127.0.0.1:8000/books/ 可以访问**获取所有数据的接口**,呈现如下页面:

![1611758040977](DRF.assets/1611758040977.png)

![1611758051753](DRF.assets/1611758544104.png)

2)在页面底下表单部分填写图书信息,可以访问**添加新图书的接口**,保存新书

![](DRF.assets/1611758254790.png)

点击POST后,返回如下页面信息:

![1611758290821](DRF.assets/1611758290821.png)

3)在浏览器中输入网址127.0.0.1:8000/books/1/,可以访问**获取单一图书信息的接口**(id为1的图书),呈现如下页面:

![1611758430487](DRF.assets/1611758430487.png)



 

4)在页面底部表单中填写图书信息,可以访问**修改图书的接口**:

![1611758463083](DRF.assets/1611758463083.png)



 

点击PUT,返回如下页面信息:

![1611758632849](DRF.assets/1611758860440.png)

5)点击DELETE按钮,可以访问**删除图书的接口**:

![1611758898396](DRF.assets/1611758898396.png)

返回,如下页面:

![1611758925023](DRF.assets/1611758925023.png)

至此,是不是发现Django REST framework很好用!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值