django DRF框架学习心得

最近复习一下DRF框架(高度封装,快速开发),结合开发经验和理解。特写笔记,以供自己学习回顾和朋友们参考学习。

Django框架的序列化和反序列化

在这里插入图片描述

学习本框架首先要搞清楚,什么是序列化及反序列化,因为序列化是前后端分离框架的核心

将模型对象转换为json数据 称之为 序列化 后端向前端传数据 (后传前,序列化)
将json转换为数据对象 称之为 反序列化前端向后端传数据(前传后,反序列)
举个例子

# 查询项目下的节点
class SelectNode(GenericAPIView):
    """
    查询项目节点列表
    """
    def post(self, request, *args, **kwargs):
        serializer = SelectNodeSerializers(data=request.data)  # json转对象 反序列化 
        verify_token(request)
        if serializer.is_valid():
        # 对象转json 序列化
            nodes = serialize("json", Node.objects.filter(node_project_name=serializer.data['node_project_name']))
            node = json.loads(nodes)
            response_data = {'code': ['200'], 'data': node}
            return HttpResponse(json.dumps(response_data))
        return Response(serializer.errors)

序列化中的关联对象序列化

用来访问主表属性时可以访问其从表(一对多,多的那个表)的信息 以及级联删除
例子:
进行一对多关联的返回 :一个书籍下有多个人物
目的:查询书籍及书籍里的人物
主表 书籍表model

class BookInfo(models.Model):
    title = models.CharField(max_length=50)
    pub_date = models.DateField()
    readcount = models.IntegerField(default=0)
    comment = models.IntegerField(default=0)
    is_delete = models.BooleanField(default=False)

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

    def __str__(self):
        """定义每个数据对象的显示信息"""
        return self.title

副表 人物表

# 定义英雄模型类
class HeroInfo(models.Model):
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    hname = models.CharField(max_length=20, verbose_name='名称')
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
    hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE,verbose_name='图书') # '外键'

    class Meta:
        db_table = 'tb_heros'
        verbose_name = '英雄'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hname

hbook字段的BookInfo属性表明主表模型类 此外 on_delete属性必须标明,用来进行级联删除,即删除某book其下关联的英雄全部删除

级联删除结果
删除成功
在这里插入图片描述
序列器中书写代码

# 自定义序列化器
class BookSerialzier(serializers.Serializer):
    # 序列化返回字段
    title = serializers.CharField(max_length=60)
    readcount = serializers.IntegerField()
    pub_date = serializers.DateField()
    # 返回关联的英雄id
    # heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
    # 返回关联英雄的str方法值
    heroinfo_set = serializers.StringRelatedField(read_only=True, many=True)

注意返回子表的字段属性名一定要是子表model类小写加_set

View代码

# 附带查询从表数据
class Books(View):
    def get(self, request):
        # 1 查询所有对象
        books = BookInfo.objects.all()
        ser = BookSerialzier(books, many=True)
        return JsonResponse(ser.data, safe=False)

# 级联删除
class DeleteBook(View):
    def get(self,request):
        books = BookInfo.objects.get(title='西游记')
        books.delete()
        return JsonResponse("删除成功")

查询结果
在这里插入图片描述

嵌套序列化返回

PrimaryKeyRelatedField 返回关联对象的id

StringRelatedField 返回关联模型类的str方法值

单独定义序列化器
例子:

class HeroInfoSerialzier(serializers.Serializer):
	# 英雄序列化器
	hname = serializers.CharField()
	hcomment = serializer.CharField()
	hook = serializers.StringRelatedField()
	# 返回值加入英雄所在书籍(返回字段名需和自己model中定义的外键字段名相同)
	h_book = serializers.PrimaryKeyRelatedField(read_only=True,)

class BookSerialzier(serializers.Serializer)
	# 书籍序列化器 
	# 序列化字段
	btitle = serializers.CharField(max_length=20,min_lenth=5)
	bread = serializers.Integerfield(max_value=100)
	# 返回关联英雄的model中str方法定义的返回值
	# heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)
	heroinfo_set = HeroInfoSerialzier(many = True)	

read_only 与 write_only的使用

**

在这里插入图片描述
如果字段属性中添加read_only 那么表明此字段只参加序列化返回,不参加序列化验证,即此字段是用来读取的 不存
如果字段属性中添加write_only 那么表明此字段值不参加序列化返回,参加序列化验证,即此字段是拿来存的不是用来读的
请添加图片描述
ser.validated_data 为序列化器验证后的字段数据

反序列化

序列器的验证功能(主要为了反序列化)

选项参数的验证

class BookSerialzier(serializers.Serializer):
    # 序列化返回字段 (length 字符串长度 单个汉字长度为1)
    title = serializers.CharField(min_length=4, max_length=60)
    read_count = serializers.IntegerField(max_value=100, min_value=2)
    pub_date = serializers.DateField(required=False)
    # 当指定默认值 或者 allow_null时 前端可以不用传此值
    comment = serializers.IntegerField(default=10)
    # require =False 可以前端可以不传此值  默认为True

view

 def post(self, request):
        data = request.body.decode()
        data_dic = json.loads(data)
        # 此处必须指定data参数
        ser = BookSerialzier(data=data_dic)
        #  加了raise_exception=True的话 就自动抛出异常 不在response了
        ser.is_valid(raise_exception=True)
        if not ser.is_valid():
            # 返回验证的错误结果
            return JsonResponse(ser.errors)
        # 如果is valid 验证错误的话 就不能保存数据
        # ser.save()
        return JsonResponse(ser.data)

序列化器中单一字段验证 和多个字段的验证,其中单一字段验证需validate_字段名。
在这里插入图片描述

反序列化保存方法的使用

第一步 在序列化器中定义create方法

validated_data 接受验证后的字段数据
def create(self,validated_data):
	# 保存数据 第一种方式 
	BookInfo.objects.create(btitle=validated_data["btitle"])
	# 保存数据 第二种方式 **拆包处理 例子在下面
	BookInfo.objects.create(**validated_data)
	return book

如需同时保存多个数据,需使用事务

def create(self,validated_data):
	try:
		# 设置回滚点
		with atomic():
			# 保存数据 第二种方式 **拆包处理 例子在下面
			BookInfo.objects.create(**validated_data)
			xxxxxxx略
	except:
		# 回滚到回滚点
			
		
	return book

第二步 在views中保存数据

# 类继承APIview的情况
def post(self,request):
	# 1 获取前端数据
	data_dict = request.data
	# 2 验证数据
	ser = BookSerialzier(data=data_dict)
	# 3 保存数据 此处调用的是序列化器中的create方法
	ser.save()
	# 4 返回结果 此处的data来自于序列化器中的create方法 返回的book对象json数据
	
	return JsonResponse(ser.data)
	

反序列化更新数据方法的使用

第一步序列化器中定义update方法

# instance 是序列化器初始化时传来的数据对象
def update(self,instance,validated_data):
	# 更新数据
	instance.btitle=validated_data["btitle"]
	instance.bread = validated_data["bread"]
	instance.save()
	return instance

第二步视图中定义put方法

def put(self,request,pk)
	# 1获取前端数据
	data= request.body.decode()
	data_dict = json.loads(data)
	# 验证数据
	try:
		book = BookInfo.objects.get(id=pk)
	except :
		return JsonResponse({'error':"错误信息"},status=400)
	# 此处的book就是instance值
	ser = BookSerialzier(book,data=data_dict)
	# 更新数据
	ser.save()
	# 4 返回结果
	return JsonResponse(ser.data)

save() 方法源码 对instance进行判断 有instance则使用update方法 ,没有则使用create方法
在这里插入图片描述

Serializers 与 ModelSerializers的区别

1 基于模型类自动生成生成字段
在这里插入图片描述

2 ModelSerializer比Serializer多了create 与 update方法。
即继承了ModelSerializer的序列化器不用再写update和create方法 在view中直接可以使用save方法
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
第三 实现了对字段唯一值的验证
在这里插入图片描述

通过模型类序列化器进行指定模型字段的序列化

在这里插入图片描述
注意 fields 与 exclude 不能同时存在
且field = 'all’为 验证全部字段

在这里插入图片描述
在这里插入图片描述

对字段设置特殊要求

虽然根据模型类生成了字段及其验证方式,但我们也可另外指定验证规则
如果模型类字段属性的长度没有任何规定 则会默认最大长度
在这里插入图片描述

可以通过显示指明字段的方式 进行限制字段长度
在这里插入图片描述

在这里插入图片描述
也可通过extra_kwargs={ }来进行字段属性的修改
在这里插入图片描述
read_only_fields=(“id”,“xx”,“xx”) 指明只读字段 即仅用于序列化输出的字段
**

增加模型类中没有的字段
在这里插入图片描述
注意:此处field若不等于"all" 则需在field中指明增加的字段

类视图的说明

APIview 继承Django的View类 增加的功能: 权限(判断用户有没有操作权限) ,认证(认证是否为注册用户), 限流(反爬虫)

GenericAPIView 继承自APIView 增加的功能
1分页
2过滤排序
3指定查询集
例子 books =Book.objects.all()
查询集特性:1惰性执行 (生成查询集的时候不进行数据库查询操作,当对查询集进行具体的操作才进行数据库查询操作) 2缓存 查询过一次后不再从数据库中查询 ,而是在已查询完的数据中操作
queryset = (查询集) Book.objects.all()

指定序列化器 serializer_class = 序列化器

APIView 的使用

在这里插入图片描述
即可使用data = request.data 获取data

常用属性
使用rest_framework.request
from rest_framework.request import Request
.data
使用APIview之前使用的是JsonResponse

使用APIview后可以直接使用rest_framework的Response类
from rest_framework.request import Response
此时 获取查询字符串可以用request.query_parms

示例代码

class Test(APIView):
    authentication_classes = []
    permission_classes = []

    # serializer_class = EditcompanySerializer
    def get(self, request):
        # 1 注意此处get后只能加单‘’
        id = request.query_params.get('id')
  
        print(request.query_params.get('id'))
        # print(id)
        if id:
            books = BookInfo.objects.get(id=id)
            ser = BookSerialzier(books)
        else:
            books = BookInfo.objects.all()
            ser = BookSerialzier(books, many=True)
        return Response(ser.data)

    # 新建保存数据 测试需使用form-data表单传输数据
    def post(self, request):
        # 输出 request中传入的查询字符串
        print(request.query_params)
        ser = TestSerializer(data=request.data)
        ser.is_valid()
        ser.save()
        return Response(ser.data)

    # 更新数据
    def put(self, request):
        # 1获取前端数据
        id = request.GET.get("id")
        # request.data 是表单里的数据
        data = request.data
        # id = s.data["id"]
        # 2验证数据
        try:
            c = ContactUs.objects.get(id=id)
        except:
            return Response("没取到数据")
        ser = TestSerializer(c, data=data)
        ser.is_valid()
        # 3更新数据
        ser.save()
        return Response(ser.data)

Genericapiview的使用

继承自APIview
from rest_framework.generics import GenericAPIView
指定序列化器
指定查询集

例子:

class Test(GenericAPIView):
    authentication_classes = []
    permission_classes = []

    # 指定当前类视图使用的序列化器
    serializer_class = TestSerializer
    # 指定当前类视图使用的查询集数据
    queryset = ContactUs.objects.all()

    def get(self, request):
        # 获取查询集中的所有数据
        contactus = self.get_queryset()
        # 使用指定序列化器,获取序列化器对象(即上面的TestSerializer)
        ser = self.get_serializer(contactus, many=True)
        return Response(ser.data)

    # 保存数据 测试需使用form-data表单传输数据
    def post(self, request):
        data = request.data
        ser = self.get_serializer(data=data)
        ser.is_valid()
        ser.save()
        return Response(ser.data)

    # 更新数据
    def put(self, request, pk):
        # 1获取前端数据
        # request.data 是表单里的数据
        data = request.data
        # id = s.data["id"]
        # 2验证数据
        try:
            # 使用pk值从查询集中获取指定的单一数据对象()
            c = self.get_object()
        except:
            return Response("没取到数据")
        ser = TestSerializer(c, data=data)
        ser.is_valid()
        # 3更新数据
        ser.save()
        return Response(ser.data)

业务逻辑没有变化 代码量也没有简化多少 为什么要单独创建一个GenericApiview类呢?
因为要和拓展类配合使用,从而简化代码

扩展类的使用

在这里插入图片描述

# 拓展类 此时函数的业务逻辑交给拓展类来执行
class Test(GenericAPIView, CreateModelMixin, ListModelMixin):
    authentication_classes = []
    permission_classes = []

    # 指定当前类视图使用的序列化器
    serializer_class = TestSerializer
    # 指定当前类视图使用的查询集数据
    queryset = ContactUs.objects.all()

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)
# 拓展类 此时函数的业务逻辑交给拓展类来执行
class Test_Update(GenericAPIView, UpdateModelMixin, DestroyModelMixin):
    authentication_classes = []
    permission_classes = []

    # 指定当前类视图使用的序列化器
    serializer_class = TestSerializer
    # 指定当前类视图使用的查询集数据
    queryset = ContactUs.objects.all()

    # 更新数据
    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

url文件是

urlpatterns = [
    url(r'^test/', Test.as_view()),
    url(r'^test_update/(?P<pk>\d+)', Test_Update.as_view()),
]

serializer:

from rest_framework.exceptions import ValidationError

from engineer.models import *
from One.models import *


class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = ContactUs
        fields = "__all__"

    def validate(self, attrs):
        if attrs["name"] == '44526':
            raise ValidationError(detail={'code': 998, "message": "您输入的name值不对"})
        return attrs

这里暂时没有实验成功 显示pk值接收不到。。后来发现是postman原因 重启后解决 绝了
postman测试ip
在这里插入图片描述
自己修改后的代码:无论是执行更改,删除等操作成功或者失败都要返回东西 否则报错 是框架源码规定

# 拓展类 此时函数的业务逻辑交给拓展类来执行
class Test_Update(GenericAPIView, UpdateModelMixin, DestroyModelMixin):
    authentication_classes = []
    permission_classes = []

    # 指定当前类视图使用的序列化器
    serializer_class = TestSerializer
    # 指定当前类视图使用的查询集数据
    queryset = ContactUs.objects.all()

    # 更新数据
    def put(self, request, pk):
        try:
            self.update(request, pk)
            
            return Response("更新数据成功")
        except:
            return Response("数据不存在")
        # return self.update(request, pk)

    def delete(self, request, pk):
        try:
            self.destroy(request, pk)
            return Response("数据已删除")
        except:
            return Response("数据未找到")

扩展类的子类的使用

例如ListCreateAPIView继承的是GenericAPIView ListAPIview CreateAPIView
所以上文中的Test类可以改写为
查询所有数据 增加新数据

# 拓展类 此时函数的业务逻辑交给拓展类来执行
class Test(ListCreateAPIView):
    authentication_classes = []
    permission_classes = []

    # 指定当前类视图使用的序列化器
    serializer_class = TestSerializer
    # 指定当前类视图使用的查询集数据
    queryset = ContactUs.objects.all()

另一个拓展类
查询单一数据 更新数据 删除数据

# 拓展类 此时函数的业务逻辑交给拓展类来执行
class Test(RetrieveUpdateDestroyAPIView):
    authentication_classes = []
    permission_classes = []

    # 指定当前类视图使用的序列化器
    serializer_class = TestSerializer
    # 指定当前类视图使用的查询集数据
    queryset = ContactUs.objects.all()

视图集

在这里插入图片描述
和ApiVIew的区别:自已定义方法名,post get 等 ,那也得对应系统已经定义的list,create等方法,如需对应别的下文有方法展示
这时候相应的url也要发生改变

ViewSet的使用

因为Viewset继承于ApiView
所以写法以及用法和ApiView相似 不需要指定查询集数据和序列化器
例子:
在这里插入图片描述

在这里插入图片描述

自定义方法路由匹配规则说明

在这里插入图片描述
这五个方法 可以用上文中的方法来匹配
如果是自己定义的别的方法名,匹配方法如下:
在这里插入图片描述
在这里插入图片描述

GenericViewSet的使用

和GenericApiView相同需要指定 查询集数据和序列化器
例子:
在这里插入图片描述

modelviewset的使用

modelviewset 继承于Genericapiview
在这里插入图片描述
modelset中已经实现了增删改查功能
在这里插入图片描述
在这里插入图片描述

路由的简化 自动生成路由

自动生成路由前提:必须使用视图集
在url中 先导入包SimpleRouter

from rest_framework.routers import SimpleRouter

register 的三个参数分别是:url名,view文件名.类名,自己指定的命名
在这里插入图片描述
此时启动项目会看到:
在这里插入图片描述
如果定义了超过规定的五个方法名之外 自动生成路由要借助action方法
在这里插入图片描述
methods规定请求方法 detail=True生成pk值,detail=False 不生成pk值
在这里插入图片描述

使用视图集和视图拓展类的时机?

根据实际需求来选择:
例如单一需求如注册等,就使用拓展类,需要增删改查就使用视图集,节省空间、资源,且涉及到redis也不能使用视图集,因为存储是在mysql中,和redis无关

Default Router 与SimpleRouter的区别

Default Router可以对首页进行匹配 simpleRouter可以
Default Router继承自SimpleRouter
在这里插入图片描述

在这里插入图片描述

self.action属性的说明及使用

self.action就是view方法名
使用:可以使用get_serializer()方法 指定多个序列化器使用(概率较小)

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值