DRF框架

一. 认识restful架构

REST,即Representational State Transfer的缩写 ,我们一般叫他'表现层状态转化'

REST的路径设计思路是简洁:
资源(比如HTML,或者图片,文档之类的)他应是名词的,我们之前在获取商品的时候,我们可能会这样写:/get_products/ 但是这样是不对的.错的.我们在路径中不应该出现动词也就是get,我们使用rest设计路径就 会是这样:GET/products/ 这里的GET是请求方式,表示我们以get的方式来请求数据,当然我们在地址栏里面是不需要输入GET的.这样我们的路径就可以很简洁了.

HTTP动词

HTTP的动词有四个:

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

二. 序列化与反序列化

视图中一般做三件事:

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

  • 操作数据库

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

我们在第一步和第三步的时候我们都会涉及到将json数据转化成模型类对象,以及将模型类对象转化成json的数据返回回去.

这里就会涉及到一个来回重复转化的问题,所以我们使用序列化,以及反序列化.

序列化和反序列化的定义:

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

1.定义方法

对于我们之前所使用的BookInfo来建立一个序列化器.

class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20)
bpub_date = serializers.DateField(label='发布日期', required=False)
bread = serializers.IntegerField(label='阅读量', required=False)
bcomment = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)

字段 字段构造方式


每个选项里面的选项参数的定义:

参数名称                    作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最小值
min_value 最大值通用参数的意义如下表:

参数名称 说明

read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
这三张表以后在定义序列化器的时候可以用到. 在这里补充一下allow_blank 和allow_null的区别: null:
If True, Django will store empty values as NULL in the database. Default # 原文解释
is False.
如果为True,空值将会被存储为NULL,默认为False。
blank:
If True, the field is allowed to be blank. Default is False. # 原文解释
如果为True,字段允许为空,默认不允许。

创建serializer对象

Serializer的构造结构:

Serializer(instance=None, data=empty, **kwarg)

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据

2.序列化

基本的使用:

先创建一个模型类对象:book = BookInfo.objects.get(id=1)

然后创建一个序列化的对象:ser = BookInfoSerializer(book) # 此处的book就是instance

我们可以通过:ser.data取出序列化后的数据:

我们查询的不是一个数据,是多个数据,我们就要加上many = True 这个选项可以序列化模型类里面含有很多数据的情况.

关联对象的序列化

我们在定义一对多的多的一方是,定义外键有很多种办法:

  • PrimaryKeyRelatedField 这个是将被序列化为关键对象的主键.也就是bookinfo的主键

hbook = serializers.PrimaryKeyRelatedField(label='图书', read_only=True)



hbook = serializers.PrimaryKeyRelatedField(label='图书', queryset=BookInfo.objects.all())

read_only = True是指该字段将不能作为反序列化使用

query_set 表示将用作反序列化时参数校验使用.


* StringRelatedField ​ 这个将被序列化为关联对象的字符串表示方式(即str方法的返回值) 序列化器的每个字段其实都是都是有该字段类型的to_representation来决定的,我们重写他. ```python
class BookRelateField(serializers.RelatedField):
"""自定义用于处理图书的字段"""
def to_representation(self, value):
return 'Book: %d %s' % (value.id, value.btitle)
我们定义了一个新的关联字段类型, hbook = BookRelateField(read_only = True)

3.反序列化

同样的,我们使用序列化器进行反序列化的时候,我们需要对数据进行验证,验证通过之后,我们才可以获取保存成功的数据,或者我们保存数据模型类的对象.

  • 我们通过is_valid()来验证数据的正确性.

  • 验证失败:我们可以用序列化对象.errors来获取错误

  • 验证成功:我们可以使用序列化对象.validated_data 来获取数据

is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。 如:

serializer.is_valid(raise_exception=True)

我们还可以自定义验证方法:

validate_<field_name>

对我们后面填入的field_name字段进行验证,在我们的BookInfoSerializer里面添加:

def validate_btitle(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
return value
btitle就是field_name 这样设置之后,这个方法就可以验证btitle的正确性了.btitle的值,就会传给value. 我们验证的时候,依然是用is_valid,来进行验证.
  • validate

一般需要将模型中的多个字段进行验证时,我们可以使用validate进行验证

我们在BookInfoSerializer中定义一个validate方法:

def validate(self, attrs):
bread = attrs['bread']
bcomment = attrs['bcomment']
if bread < bcomment:
raise serializers.ValidationError('阅读量小于评论量')
return attrs
  • validators

我们在序列化器中添加选项参数,也可以补充验证行为.

btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
我们在类的上面定义一个about_Leijingjing方法: def about_django(value):
if 'Leijingjing' not in value.lower():
raise serializers.ValidationError("图书不是关于Leijingjing的")
  • 保存
  验证成功后,我们就可以保存了.我们在BookInfoSerializer里面定义两个方法:create(), update()

  def create(self, validated_data):
"""新建""" # **用于将字典解包,解成 a = 1 的这种形式
return BookInfo.objects.create(**validated_data)
def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
instance.save()
return instance

定义了这个之后,我们就可以在反序列化字段的时候,就可以使用save()来返回一个数据对象实例了.

如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。

还有两点:

1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到serializer.save(owner=request.user)

2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新

  • 模型类序列化器

在生成模型类序列化器的时候,有一些注意点.需要记一下.

我们生成一个模型类序列化器:BookInfoSerializer

class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
model 指向的是哪个模型类 fields指向的是我们序列化哪些字段.
  • 指定字段

我们可以在操作fields 来实现字段控制:fields = ('id', 'btitle', 'bpub_date'),这个表示只显示这三个字段.

  • 使用exclude来排除哪些字段:exclude = ('image',) 这是一个元祖.

  • 使用depth来实现嵌套表示,depth是整数,它表示嵌套的层级数.

class HeroInfoSerializer2(serializers.ModelSerializer):
class Meta:
model = HeroInfo
fields = '__all__'
depth = 1
形成的序列化器如下: 在hbook后面又套了一层. HeroInfoSerializer():
id = IntegerField(label='ID', read_only=True)
hname = CharField(label='名称', max_length=20)
hgender = ChoiceField(choices=((0, 'male'), (1, 'female')), label='性别', required=False, validators=[<django.core.valators.MinValueValidator object>, <django.core.validators.MaxValueValidator object>])
hcomment = CharField(allow_null=True, label='描述信息', max_length=200, required=False)
hbook = NestedSerializer(read_only=True):
id = IntegerField(label='ID', read_only=True)
btitle = CharField(label='名称', max_length=20)
bpub_date = DateField(allow_null=True, label='发布日期', required=False)
bread = IntegerField(label='阅读量', max_value=2147483647, min_value=-2147483648, required=False)
bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False)
image = ImageField(allow_null=True, label='图片', max_length=100, required=False)

我们还可以显示只读字段:

class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
read_only_fields = ('id', 'bread', 'bcomment') # 这些表示是只读字段,也就是只用于序列化输出的字段添加额外参数

我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
extra_kwargs = {
'bread': {'min_value': 0, 'required': True},
'bcomment': {'min_value': 0, 'required': True},
}

三.环境安装与配置

安装drf命令: pip install djangorestframework 然后在install_apps里面注册rest_framework

书写视图函数:在views.py中

class BookInfoViewSet(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer

定义路由:是在urls.py中定义的

urlpatterns = [
...
]
router = DefaultRouter() # 可以处理视图的路由器
router.register(r'books', views.BookInfoViewSet) # 向路由器中注册视图集
urlpatterns += router.urls # 将路由器中的所以路由信息追到到django的路由列表

四.视图及视图集

1.request 和 response

  • DRF框架封装了一个request对象在里面,我们就不在使用原来Django中的httprequest对象了.他一般有两个属性一个是.data ,一个是.query_params

request.data是对应于原先Django中的.json 和.files.这个也是使用最多的,有如下特点:

  • 包含了解析之后的文件和非文件数据

  • 包含了对POST、PUT、PATCH请求方式解析后的数据

  • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据

request.query_params 和原先Django中的GET方法相同,是获取查询字符串中的内容的.

同样的,框架内部也还有一个response对象,我们最开始需要在配置文件里面配置一下,如下

REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
构造方式:Response(data, status=None, template_name=None, headers=None, content_type=None)

一般使用的最多的就是data 和status ,data指的是序列化之后的字典数据,但尚未render的数据. status表示状态码,还有一个.content表示render过后的数据.

2.视图

  • 1)两个基类

APIView是DRF框架提供的所有视图的基类,他继承自Django的View类.他和Django中的View不同的地方在于

传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;

视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;

任何APIException异常都会被捕获到,并且处理成合适的响应信息;

在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。


##### 他其中定义的属性有:权限控制,流量限制,身份认证 * authentication_classes 列表或元祖,身份认证类 * permissoin_classes 列表或元祖,权限检查类 * throttle_classes 列表或元祖,流量控制类 也就是说,如果我们定义的类视图继承了APIView我们就可以使用这些属性. #### GenericAPIView 他继承自APIView类,他在APIView的基础上增加了<font color=#ff0000>列表视图(也就是获取全部数据)和详情视图(获取单个数据)</font>的通用支持方法.使用的时候我们需要搭配多个mixin扩展类使用.他的属性有: ##### 列表视图和详情视图通用的: * queryset 列表视图的查询集 * serializer_class 视图使用的序列化器 ##### 列表视图使用的: * pagination_class 分页控制类 我们获取所有数据,我们就需要使用分页操作,或者过滤操作 * filter_backends 过滤控制后端 ##### 详情视图使用的: * lookup_field 查询单一数据库对象时使用的条件字段,默认为'pk' 这个使用的比较多.pk表示主键 * lookup_url_kwarg 查询单一数据时URL中的参数关键字名称,默认与look_field相同 提供的方法,我们如果继承了他,我们在我们定义的类视图中,就可以直接使用self.方法名调用他.很方便 ##### 列表视图与详情视图通用的: * get_queryset()返回列表视图与详情视图的查询集,是两个视图获取数据的基础. * get_serializer()返回序列化器对象,我们在类视图的最开始的地方,会指定一个查询集,一个序列化集,我们使用这个方法,我们就不用再写序列化集,直接使用这个来返回序列化对象了.比如 本来: ser = BookInfoSerializer(book) 现在 ser = get_Serializer(book) 就好了 get_serializer_class 会返回一个序列化器类,这个有什么用,我也不知道 ##### 详情视图专用的: * get_object() 这个会返回我们需要的详情类的模型类对象.不过我们需要在方法中传入一个pk值, #### 2)五个扩展类 * ListModelMixin(返回所有数据),
* CreateModelMixin(创造一个数据),
* RetrieveModelMixin(获取单个的数据对象),
* UpdateModelMixin (更新某条数据),
* DestoryModelMixin(删除数据) 我们如果继承了:这个其中的方法,我们就可以不用写视图方法中的处理逻辑了. 继承ListModelMixin ,我们就可以这样写: ```python
def get(self, request):
return self.list(request) # 这是因为我们调用了扩展类里面的list方法.我们传入request就好了啊
  • CreateModelMixin里面是create方法,
  • RetrieveModelMixin里面是retrieve方法,
  • UpdateModelMixin 里面是update方法,
  • DestoryModelMixin里面是destroy方法.

3)子视图

还有七个可以的子类视图.就是他已经继承好了,我们继承一个,其实就已经继承了好几个的意思.

  • CreateAPIView 继承自: GenericAPIView、CreateModelMixin

  • ListAPIView 继承自:GenericAPIView、ListModelMixin

  • RetrieveAPIView 继承自:GenericAPIView、RetrieveModelMixin

  • DestroyAPIView 继承自:GenericAPIView、DestoryModelMixin

  • UpdateAPIView 继承自:GenericAPIView、UpdateModelMixin

  • RetrieveUpdateAPIView 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin

  • RetrieveUpdateDestroyAPIView 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

3).视图集

视图集就是Viewset(),就是将一系列逻辑相关的动作放在一个类中,

  • list() 提供一组数据

  • retrieve() 提供单个数据

  • create() 创建数据

  • update() 更新数据

  • destory() 删除数据

我们在视图集中不在实现get 和post方法了,我们就使用上面列的几种方法.

# 比如我们定义了以下两个方法
class BookInfoViewSet(viewsets.ViewSet):
pass
def list(self, request):
pass
def retrieve(self, request, pk=None):
pass

我们定义路由的时候,就这样定义

urlpatterns = [
url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]

在as_view后面加上什么类型,以及对应的方法.

上面的五个已经定义好的,我们不需要给他指定请求方法.

但是我们要是定义一个自定义的方法,我们就需要给他指定一个请求方式了.

这时,我们就需要给我们写的方法,添加一个装饰器,action,

action需要接收两个参数:

  • methods: 该action支持的请求方式,使用列表包裹

  • detail: 表示是action中要处理的是否是视图资源的对象(即是否通过url路径获取主键),如果是True,我们就是要给他传入一个主键pk,False就不用传.

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
# detail为False 表示不需要处理具体的BookInfo对象
@action(methods=['get'], detail=False)
def latest(self, request):
"""
返回最新的图书信息
"""
book = BookInfo.objects.latest('id')
serializer = self.get_serializer(book)
return Response(serializer.data)

detail为True,表示要处理具体与pk主键对应的BookInfo对象

@action(methods=['put'], detail=True)
def read(self, request, pk):
"""
修改图书的阅读量数据
"""
book = self.get_object()
book.bread = request.data.get('read')
book.save()
serializer = self.get_serializer(book)
return Response(serializer.data)

我们定义路由的时候,我们这样定义

url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})),
url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),

视图集父类

这其中也有很多已经封装好的子类用于继承的,如下:

  • 1) ViewSet

    继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

    在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

  • 2)GenericViewSet

    继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。

  • 3)ModelViewSet

    继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

  • 4)ReadOnlyModelViewSet

    继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。

五.路由

路由一共分为两种: DefaultRouter ,和 SimpleRouter

创建路由对象,并注册视图集

router = routers.SimpleRouter()
router.register(r'books', BookInfoViewSet, base_name='book')

注册的语法如下:register(prefix, viewset, base_name)

  • books 就是prefix , viewset就是BookInfoViewSet, book就是 base_name

  • prefix是路由前缀,在请求的地址中.如127.0.0.1:8000/books

  • base_name就是区别视图方法的,一般是这样的格式 : book-list (list方法)

最新文章

  1. STM32 IIC
  2. Map拷贝 关于对象深拷贝 浅拷贝的问题
  3. 从Nodejs脚本到vue首页看开源始末的DemoHouse
  4. Service错误
  5. php by oneself
  6. freeCodeCamp:Find the Longest Word in a String
  7. js中的包装对象。
  8. IHttpModule在webconfig中的注册
  9. [Android Pro] Android 之使用LocalBroadcastManager解决BroadcastReceiver安全问题
  10. 【leetcode❤python】 19. Remove Nth Node From End of List
  11. OC基础(22)
  12. ubuntu vim 7.4 编译安装
  13. Spring实例化bean的三种方法
  14. CSS 定位 (Positioning)学习
  15. Linux系统下常用的快捷键
  16. Java 读书笔记 (十三) for each 循环
  17. 使用FindBugs寻找bug,代码分析
  18. Browserify模块化使用教程
  19. TCP 三次握手理解和过程
  20. CF321E Ciel and Gondolas 【决策单调性dp】

热门文章

  1. CentOS 编译安装 PyCrypto
  2. 安装Docker到Ubuntu(APT)
  3. C# 读取Excel到DataTable两种方式对比
  4. css 基础教程学习
  5. PHP目录操作(附封装好的目录操作函数文件)
  6. caffe+win10+git使用sh文件
  7. [国家集训队] 拉拉队排练 - Manacher
  8. 【PAT甲级】1117 Eddington Number (25分)
  9. 【Python】无限循环
  10. 载 js验证密码 必须由大小写字母、数字和特殊字符组成