djangorestframe的学习笔记

djangorestframework,简称为DRF,它是一种基于前后端分离设计架构的基于rest风格的一种API接口的实现,也就是说,它本身是一种基于rest的接口设计风格,并且是在Django中实现了这种设计。

常见的rest设计风格有9方面:

一、域名

尽量使用专有域名,如:api.zytk.xyz

二、版本

版本信息可放入url中,也可放入到请求的参数中,但建议最好放在url中。

三、路由

路由中只有名词,不能有动词,并且名词尽量使用复数形式。

四、请求

不同的请求使用不同的请求方法来表示,常见请求与请求方法对应如下表:

请求 请求方法 功能
获取信息 GET 查
创建对象  POST 增
更新对象 PUT 改
删除对象 DELETE 删
局部更新对象 PATCH 局部改
获取资源的元数据 HEAD 查资源头

 

五、过滤

过滤是指对信息的过滤或筛选,一般是将过滤的条件通过字符串查询参数的形式传给后端程序。然后后端程序对数进行过滤后,返回给前端。

六、状态码

不同的请求状态对应不同的状态码,每次请求的结果,服务器端都应该返回相应的状态码,常见的状态码如下表:

状态码 状态
201 创建对象成功
202 接受请求成功,正在排队等待处理...
204 删除对象成功
200 查询信息成功
401 无权访问,认证失败
404 url有误,请检查请求的url

 

七、错误处理

每一次请求如果出错,一定要返回错误处理信息。该信息的格式一般是json格式。

八、返回值格式

一般都要返回json格式,当然也可返回text、xml等其它格式,但是,最好是返回json这种格式的数据。

九、数据格式

一切数据格式,无论是请求时携带的参数,还是请求响应时返回的数据,包括错误信息以及状态码的返回,最好全部使用json数据格式。

第一个drf示例——手动实现一个杂志订阅的api接口

项目简介:实现杂志的增删改查操作

实现功能如下表所示:

功能 请求方式 请求路径
获取所有杂志对象 GET api/magazines
创建单本杂志对象 POST api/magazines
获取单本杂志对象 GET api/magazines/{pk}
修改单本杂志对象 PUT api/magazines/{pk}
删除单本杂志对象 DELETE api/magazines/{pk}

 

 从请求路径上来看分为两类请求:

一类是不需要带pk参数的请求

一类是需要带pk参数的请求

我们可以把不需要带pk参数的两个请求归类为列表视图,而把需要带pk参数的三个请求归类为详情视图。

项目数据库使用mongodb

步骤:

一、创建项目

1. 进入虚拟环境

2. 创建项目

django-admin startproject 【项目名】

二、创建一个新的Django应用

1. 进入项目根目录

2.创建新应用

python manage.py startapp 【应用名称】

3. 注册应用

在settings.py中的【INSTALLED_APPS】中添加一项“magazie”

三、配置mongodb库支持

在setting.py中【DATABASES】中添加一项配置,如下所示:

'mongodb': {
    'ENGINE': None
}
然后再配置mongodb的连接方式
from mongoengine import *
conn = connect('magazines', host="127.0.0.1", port=27017, connect=False)
添加以上代码时,需要先安装mongoengine扩展,该扩展是用来操作mongodb数据库的python扩展之一。

四、配置路由

1.配置主路由

path('api/', include("magazine.urls")),

2.配置子路由

path('magazines/',views.MagazineListView.as_view()),
path('magazines/<pk>',views.MagazineDetailView.as_view())
 
五、创建数据模型
因为是基于mongodb的数据模型,所以要引入mongoengine
from mongoengine import *
创建模型类如下所示:
"""
杂志期刊类型模型
"""
class PublisherType(Document):
    name = StringField()
    publisherTime = DateTimeField()


"""
出版社
p1 = Publisher(name="人民出版社")
p1.save()
p2 = Publisher(name="清华出版社")
p2.save()
p3 = Publisher(name="河南出版社")
p3.save()
"""
class Publisher(Document):
    name = StringField()
"""
杂志模型
m1 = Magazine(title="计算机应用基础",publisherType=pt1,price=35.6,publisher=p1)
m1.save()
m2 = Magazine(title="计算机组装与维修",publisherType=pt2,price=41,publisher=p2)
m2.save()
m3 = Magazine(title="电子商务基础",publisherType=pt3,price=60.412,publisher=p3)
m3.save()

"""
class Magazine(Document):
    # 杂志名
    title = StringField()
    # 杂志期刊类型
    publisherType = ReferenceField(PublisherType)
    # 杂志价格
    price = FloatField(default=0)
    # 出版社
    publisher = ReferenceField(Publisher)
 
六、编写视图
1. MagazineListView视图
在这个视图中实现两个操作:获取所有杂志对象和创建单个杂志对象。
获取所有杂志对象的请求方法是get方法,具体的实现步骤:
①从模型中获取所有杂志对象
queryset = models.Magazine.objects.all()
②将对象列表转换成字典列表(数据转换,序列化操作)
objlist=[]
        objdict={}
        for obj in queryset:
            objdict = {
                "id": str(obj.id),
                "title":obj.title,
                "publisherType": {
                    "id": str(obj.publisherType.id),
                    "name": obj.publisherType.name
                    },
                "price": obj.price,
                "publisher": {
                  "id": str(obj.publisher.id),
                  "name": obj.publisher.name
                }
            }
            objlist.append(objdict)
③返回结果
return JsonResponse(objlist,safe=False)
创建单个杂志对象的请求方法是post方法,具体的实现步骤是:
①获得客户端传来的数据
  objdict = json.loads(request.body.decode())
  title = objdict["title"]
        publisherType = objdict["publisherType"]
        price = objdict["price"]
        publisher = objdict["publisher"]
        print(publisherType["id"])
②校验数据(省略)
因为字段越多,校验数据的复杂度越大,因此这里是实现的重点,但不是难点,就是代码量较大,所以在这里省略不写了,这是我们手写api时留下的一个问题。
③数据入库
A、查询期刊类型
  publisherType = models.PublisherType.objects.get(pk=publisherType["id"])
        publisher = models.Publisher.objects.get(pk=publisher["id"])
B、创建杂志并入库
  magazine = models.Magazine.objects.create(title=title,publisherType=publisherType,price=price, publisher=publisher)
④数据转换
  magazinedict = {
                "id": str(magazine.id),
                "title":magazine.title,
                "publisherType": {
                    "id": str(magazine.publisherType.id),
                    "name": magazine.publisherType.name
                    },
                "price": magazine.price,
                "publisher": {
                  "id": str(magazine.publisher.id),
                  "name": magazine.publisher.name
                }
            }
⑤返回结果
return JsonResponse(magazinedict,safe=False)
2.MagazineDetailView视图

 这个视图类实现了获取、修改和删除单本杂志对象的功能。

其源码如下:

class MagazineDetailViewset(View):
    # 获取单个杂志对象
    def get(self,request,pk):
        # 1.根据参数pk获得单个杂志对象
        magazine = models.Magazine.objects.get(pk=pk)
        # 2.序列化该对象
        magazinedict = {
                "id": str(magazine.id),
                "title":magazine.title,
                "publisherType": {
                    "id": str(magazine.publisherType.id),
                    "name": magazine.publisherType.name
                    },
                "price": magazine.price,
                "publisher": {
                  "id": str(magazine.publisher.id),
                  "name": magazine.publisher.name
                },
                # "photo": magazine.photo
            }
        # 3.返回结果
        return JsonResponse(magazinedict,safe=False)
    
    
    def put(self,request,pk):
        # 1.获取更新参数
        objdict  = json.loads(request.body.decode())
        title = objdict["title"]
        publisherType = objdict["publisherType"]
        price = objdict["price"]
        publisher = objdict["publisher"]
        print(publisherType["id"])
        print(objdict)
        # 2.数据检验(省略)
        # 3.获取要更新的对象
        magazine = models.Magazine.objects.get(pk=pk)
        # 4. 更新对象并入库
        # 4.1. 查询期刊类型对象和出版社对象
        publisherType = models.PublisherType.objects.get(pk=publisherType["id"])
        publisher = models.Publisher.objects.get(pk=publisher["id"])
        # 4.2. 更新对象
        magazine.title = title
        magazine.publisherType=publisherType
        magazine.publisher = publisher
        magazine.price = magazine.price
        # 4.3.入库
        magazine.save()
        # 5.序列化数据
        magazinedict = {
                "id": str(magazine.id),
                "title":magazine.title,
                "publisherType": {
                    "id": str(magazine.publisherType.id),
                    "name": magazine.publisherType.name
                    },
                "price": magazine.price,
                "publisher": {
                  "id": str(magazine.publisher.id),
                  "name": magazine.publisher.name
                },
                # "photo": magazine.photo
            }
        # 6.返回结果
        return JsonResponse(magazinedict,safe=False)
    
    def delete(self,request,pk):
        # 1.获取要删除的对象
        magazine = models.Magazine.objects.get(pk=pk)
        # 2. 删除对象
        magazine.delete()
        # 3.返回结果
        return HttpResponse("-1")
到这里,我们就自己手写了一个基于rest风格的api接口。但就目前的设计而言,还存在着一些问题,这些问题如下:
1. 重复代码较多,不能很好的实现代码重用,因此,后面我们要对这些重复的代码进行封装,以实现代码的重用,使代码变的简洁。
2. 在接口的实现过程中,经常遇到将模型对象转换成json数据的过程,我们称之为序列化,这个序列化过程经常被用到,我们能否将这一过程抽离出来,形成一个仅对数据时行序列化的类,用这个类来完成这个专门的工作呢?
3. 在接口的实现过程中,还有对数据进行校验的操作,这个过程是否也可以抽取出来呢?
4. 同样,在增加对象接口的实现过程中,需要将外部传来的json数据转换成模型对象,这个过程我们称之为反序列化,那么,反序列化是否也能够封装呢?
下面,我们就来一步一步的进行封装和改进。
 
在改进前,我们先来了解几个概念。
概念一:序列化:就是将模型数据转换为json数据的过程。主要应用在get请求时,获取所有模型类对象或获取单个模型类对象。
概念二:反序列化:就是将json数据转换成模型数据的过程。主要用在post或put请求创建模型对象或修改模型对象的场景中。
概念三:序列化器:它是一个模型的序列化或反序列化的封装,它是一个类,该类的作用是用来序列化或反序列一个模型的对象。它起到的主要功能就是校验数据和数据转换。
 
序列化器的定义
目的:是参考数据模型,定义序列化器。
定义序列化器的4个原则:
1. 序列化器中的字段要和模型中的字段名相同。
2. 序列化器中的字段类型要和模型中的字段类型要一样。
3. 序列化器中的字段的选项也要和模型中的字段的选项相同。
4. 最重要的是序列化器要继承自DocumentSerializer类(针对于mongodb数据库模型的序列化器),即rest_framework_mongoengine.serializers.DocumentSerializer;而针对于mysql、sqite、sql等数据库模型类的序列化器要继承自Serializer类,即rest_framework.Serializer。
 
posted @ 2021-03-03 16:39  智一  阅读(169)  评论(0)    收藏  举报