Django之drf三

一、序列化高级用法之source

我们先在models中创建五张表,以这五张表为例

from django.db import models


# Create your models here.
# 图书表
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    # publish一对多外键
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    # 作者表多对多外键
    authors = models.ManyToManyField(to='Author')

    # 第二种
    def sb_name(self):
        return self.name + '_sb'

    @property  # 可加可不加
    def publish_detail(self):
        return {'id': self.publish.pk, 'name': self.publish.name, 'addr': self.publish.addr}

    def author_list(self):
        l = []
        for author in self.authors.all():
            l.append({'id': author.pk, 'name': author.name, 'phone': author.phone, 'age': author.author_detail.age})
        return l


# 出版社表
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


# 作者表
class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.IntegerField()
    # 作者详情表一对一外键
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    @property
    def author_tail(self):
        return {'id': self.author_detail.pk, 'email': self.author_detail.email, 'age': self.author_detail.age}

    def email(self):
        return self.author_detail.email

    def age(self):
        return self.author_detail.age


# 作者详情表
class AuthorDetail(models.Model):
    email = models.CharField(max_length=32)
    age = models.IntegerField()

补充:

# on_delete:
# CASCADE:级联删除,只要删除publish,跟publish关联的book,全都被删除
# SET_DEFAULT:只要删除publish,跟publish关联的book,的publish字段会变成默认值,一定要配合default使用
# SET_NULL:只要删除publish,跟publish关联的book,的publish字段会变成空,一定要配合null=True使用
# models.SET(add):括号中可以放个值,也可以放个函数内存地址,只要删除publish,跟publish关联的book,的publish字段会变成set设的值或执行函数
# models.DO_NOTHING:什么都不做,但它需要跟db_constraint=False配合,表示不建立外键约束,创建逻辑外键,不是物理外键
# 不建立物理外键的好处?增删查改数据快
#缺点:容易出现脏数据
# 实际工作中,都不建立物理外键,都是建立逻辑外键

对book进行序列化

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()

1.1source用法

1 source的用法
# 方法一:将传给前端的字段名改为book_name
book_name = serializers.CharField(source='name')
# 方法二:在书名后加上sb---》在models定义sb_name
book_name = serializers.CharField(source='sb_name')
# 方法三:关联查询,拿出出版社的名字
pub_name = serializers.CharField(source='publish.name')
name = serializers.CharField()
price = serializers.CharField()

 总结:source的用法

-1 修改前端看到的字段key值---》source指定的必须是对象的属性
    	book_name = serializers.CharField(source='name')
    -2 修改前端看到的value值,---》source指定的必须是对象的方法
    	表模型中写方法
          def sb_name(self):
        	return self.name + '_sb'
        序列化类中
        	book_name = serializers.CharField(source='sb_name')
            
     -3 可以关联查询(得有关联关系)
    	publish_name = serializers.CharField(source='publish.name')

2定制字段的两种方式一

方式一:在序列化类中写
    1 写一个字段,对应的字段类是:SerializerMethodField
    2 必须对应一个 get_字段名的方法,方法必须接受一个obj,返回什么,这个字段对应的value就是什么

### 2。1 定制字段方式1
# 拿出出版社的id和名字和addr,放到一个字典中--->在models中定义pubilsh_destail
# SerializerMethodField来定制,如果写了这个,必须配合一个方法get_字段名,这个方法返回什么,##这个字段的值就是什么
publish_detail = serializers.SerializerMethodField()

def get_publish_detail(self, book):
    return {'id': book.publish.pk, 'name': book.publish.name, 'addr': book.publish.addr}

# 练习拿出所有作者的信息 ---》多条
authors_list = serializers.SerializerMethodField()

def get_authors_list(self, book):
    l = []
    for author in book.authors.all():
        l.append({'id': author.pk, 'name': author.name, 'phone': author.phone, 'email': author.author_detail.email,
                  'age': author.author_detail.age})
    return l

2.1定制字段的两种方式二

方式二:在表模型中写
    1 在表模型中写一个方法(可以使用:property),方法有返回值(字典,字符串,列表)
    2 在序列化类中,使用DictField,CharField,ListField

# 2.2 定制字段方式2
#       1 序列化类中这样写
#     # 2 到表模型中写一个方法,方法名必须叫 publish_detail,这个方法返回什么,这个字段的   # value就是什么
     publish_detail = serializers.DictField()
     author_list = serializers.ListField()

 class BookSerialzier2(serializers.Serializer):
     name = serializers.CharField()
     price = serializers.CharField()

3、序列化类,即做序列化,又做反序列化,还做数据校验

1 定制字段方式1

1 序列化类中这样写
2 到表模型中写一个方法,方法名必须叫 publish_detail,这个方法返回什么,这个字段的value就是什么

4反序列化之保存与修改

序列化类

    def create(self, validated_data):
        # book = Book.objects.create(**validated_data)
        # book = Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'))
        # 方法一
        # book = Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'),
        #                            publish_id=validated_data.get('publish_id'))
        # book.authors.add(*validated_data.get('authors'))
        # 方法二
        authors = validated_data.pop('authors')
        book = Book.objects.create(**validated_data)
        book.authors.add(*authors)
        book.save()
        return book

    def update(self, instance, validated_data):
        authors = validated_data.pop('authors')
        for item in validated_data:
            setattr(instance, item, validated_data[item])
        instance.authors.set(authors)
        # instance.authors.remove()
        # # instance.authors.add(*authors)
        instance.save()
        return instance

视图类

# 创建
class BookView(APIView):
    def post(self, request):
        ser = BookSerialzier(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})
# 修改
class BookDetailView(APIView):

    def put(self, request,pk):
        book=Book.objects.get(pk=pk)
        ser = BookSerialzier(data=request.data,instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

5、ModelSerializer使用

之前写的序列化类,继承了Serializer,写字段,跟表模型没有必然联系

class XXSerialzier(Serializer)
	id=serializer.CharField()
	name=serializer.CharField()
    
 XXSerialzier既能序列化Book,又能序列化Publish

现在学的ModelSerializer,表示跟表模型一一对应,用法跟之前基本类似

1 写序列化类,继承ModelSerializer
    2 在序列化类中,再写一个类,必须叫
    	class Meta:
			model=表模型
             fields=[] # 要序列化的字段
    3 可以重写字段,一定不要放在class Meta
    	-定制字段,跟之前讲的一样
    4 自定制的字段,一定要在fields中注册一下
    5 class Meta: 有个extra_kwargs,为某个字段定制字段参数
    6 局部钩子,全局钩子,完全一致
    7 大部分请情况下,不需要重写 create和update了

反序列化校验源码分析

序列化类的校验功能
    -局部钩子:必须    validate_字段名
    -全局钩子: validate

# 入口:
	-ser.is_valid 才做的校验---》入口
    -BookSerializer---》Serializer——-》BaseSerializer---》is_valid---》继承了Field
    -is_valid 方法
        def is_valid(self, *, raise_exception=False):
            # self中没有_validated_data,只有执行完后,才有
            if not hasattr(self, '_validated_data'):
                try:
                    # 核心---》这一句
                    # 想看它的源代码,按住ctrl+鼠标点击是不对的---》只能找当前类的父类
                    #但它真正的执行是,从根上开始找
                    self._validated_data = self.run_validation(self.initial_data)
                except ValidationError as exc:
                    self._validated_data = {}
                    self._errors = exc.detail
                else:
                    self._errors = {}

            if self._errors and raise_exception:
                raise ValidationError(self.errors)

            return not bool(self._errors)
    -self.run_validation(self.initial_data),不能按住ctrl+鼠标点点击,要从根上开始找
    -Serializer的run_validation
        def run_validation(self, data=empty):
             # 局部钩子
            value = self.to_internal_value(data)
            try:
                # 全局钩子
                value = self.validate(value) # BookSerializer只要写了,优先执行它的
            except (ValidationError, DjangoValidationError) as exc:
                raise ValidationError(detail=as_serializer_error(exc))

            return value
    
    -self.to_internal_value(data)---》Serializer类的方法
        def to_internal_value(self, data):
            for field in fields: #序列化类中写的一个个的字段类的对象列表
                # 一个field是name对象,field.field_name字符串 name
                # self是谁的对象:序列化类的对象,BookSerializer的对象  validate_name
                validate_method = getattr(self, 'validate_' + field.field_name, None)
                try:
                    # 字段自己的校验规则
                    validated_value = field.run_validation(primitive_value)
                    if validate_method is not None:
                        # 局部钩子
                        validated_value = validate_method(validated_value)
                except ValidationError as exc:
                    errors[field.field_name] = exc.detail
                except DjangoValidationError as exc:
                    errors[field.field_name] = get_error_detail(exc)
                except SkipField:
                    pass
                else:
                    set_value(ret, field.source_attrs, validated_value)

            if errors:
                raise ValidationError(errors)

            return ret

总结:

-ser.is_valid---》走局部钩子的代码---》是通过反射获取BookSerializer中写的局部钩子函数,如果写了,就会执行----》走全局钩子代码---》self.validate(value)--->只要序列化类中写了,优先走自己的

断言

断定某个东西是我认为的,如果不是就抛异常

assert hasattr(self, 'initial_data'), (
            'Cannot call `.is_valid()` as no `data=` keyword argument was '
            'passed when instantiating the serializer instance.'
        )



assert res == 16, Exception('不等于16')

等同于if判断+抛异常

def add(a, b):
    return a + b

res = add(8, 9)

if not res==16:
    raise Exception('不等于16')
print('随便')

3 drf之请求

视图类:APIView
序列化组件:Serializer,ModelSerializer
drf:Request类的对象
drf:Response

3.1 Request类对象的分析

1).data
    request.data 返回解析之后的请求体数据。类似于Django中标准的request.POST和 request.FILES属性,但提供如下特性:
    包含了解析之后的文件和非文件数据
    包含了对POST、PUT、PATCH请求方式解析后的数据
    利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
2).query_params
	request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而
3) 其他的属性用起来跟之前一样

3.2 请求,能够接受的编码格式

urlencoded
form-data
json

三种都支持
限制只能接受某种或某几种编码格式

限制方式一:在视图类上写---》只是局部视图类有效

# 总共有三个:JSONParser, FormParser, MultiPartParser
	class BookView(APIView):
    	parser_classes = [JSONParser, FormParser]

限制方式二:在配置文件中写---》全局有效

# drf的配置,统一写成它
    REST_FRAMEWORK = {
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',
            # 'rest_framework.parsers.FormParser',
            # 'rest_framework.parsers.MultiPartParser',
        ],
    }

全局配置了只支持json,局部想支持3个

-只需要在局部,视图类中,写3个即可
    class BookView(APIView):
    	parser_classes = [JSONParser, FormParser,MultiPartParser]

# 总结:能够处理的请求方式编码
    -优先从视图类中找
    -再去项目配置文件找
    -再去drf默认的配置中找

4 drf之响应

4.1 响应类的对象Response

三个重要的:data,status,headers

# return Response({code:100})
-data:响应体的内容,可以字符串,字典,列表
-status:http响应状态码  
	-drf把所有响应码都定义成了一个常量
	
template_name:模板名字,用浏览器访问,看到好看的页面,用postman访问,返回正常数据
	-自定制页面
    -根本不用
headers:响应头加数据(后面讲跨域问题再讲)
	-headers={'name':'lqz'}
content_type:响应编码,一般不用

4.2 响应的格式

默认是两种:纯json,浏览器看到的样子

限制方式一:在视图类上写---》只是局部视图类有效

# 总共有两个个:JSONRenderer,BrowsableAPIRenderer
	from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
    class BookView(APIView):
        renderer_classes = [JSONRenderer]

限制方式二:在配置文件中写---》全局有效

# drf的配置,统一写成它
     REST_FRAMEWORK = {
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            # 'rest_framework.renderers.BrowsableAPIRenderer',
        ],
    }

全局配置了只支持json,局部想支持2个

-只需要在局部,视图类中,写2个即可
	from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
    class BookView(APIView):
        renderer_classes = [JSONRenderer,BrowsableAPIRenderer]

5 视图之两个视图基类

视图类:

-APIView:之前用过
	-GenericAPIView:GenericAPIView继承了APIView

# GenericAPIView
	-类属性:
    	queryset:要序列化的所有数据
    	serializer_class:序列化类
        lookup_field = 'pk' :查询单条时的key值
     -方法:
    	-get_queryset():获取所有要序列化的数据【后期可以重写】
        -get_serializer  : 返回序列化类
        -get_object :获取单个对象

总结:以后继承GenericAPIView写接口

1 必须配置类属性
        queryset
        serializer_class
2 想获取要序列化的所有数据
        get_queryset()
3 想使用序列化类:
        get_serializer
4 想拿单条
        get_object

5.1 使用APIView+序列化类+Response写接口

from rest_framework.views import APIView

from .serializer import BookSerialzier
from rest_framework.response import Response
from .models import Book

# class BookView(APIView):
#     def get(self, request):
#         qs = Book.objects.all()
#         ser = BookSerialzier(qs, many=True)
#         return Response({'code': 100, 'msg': '成功', 'results': ser.data})
#
#     def post(self, request):
#         ser = BookSerialzier(data=request.data)
#         if ser.is_valid():
#             ser.save()
#             return Response({'code': 100, 'msg': '成功'})
#         else:
#             return Response({'code': 100, 'msg': ser.errors})
#
#
# class BookDetailView(APIView):
#     def get(self, request, pk):
#         book = Book.objects.all().get(pk=pk)
#         ser = BookSerialzier(book)
#         return Response({'code': 100, 'msg': '成功', 'results': ser.data})
#
#     def put(self, request, pk):
#         book = Book.objects.get(pk=pk)
#         ser = BookSerialzier(data=request.data, instance=book)
#         if ser.is_valid():
#             ser.save()
#             return Response({'code': 100, 'msg': '更新成功'})
#         else:
#             return Response({'code': 100, 'msg': ser.errors})


####2 使用GenericAPIView+序列化类+Response写接口

# 咱们写的
# class GenericAPIView(APIView):
#     query_set=None
#     serialzier_class=None
#     def get_queryset(self):
#         return self.query_set
#     def get_serializer(self):
#         return self.serialzier_class
#     def get_object(self):
#         return self.query_set.filter(pk=pk)

# 人家写的
from rest_framework.generics import GenericAPIView


# class BookView(GenericAPIView):
#     queryset = Book.objects.all()
#     serializer_class = BookSerialzier
#
#     def get(self, request):
#         qs = self.get_queryset()
#         ser = self.get_serializer(qs, many=True)
#         return Response({'code': 100, 'msg': '成功', 'results': ser.data})
#
#     def post(self, request):
#         ser = self.get_serializer(data=request.data)
#         if ser.is_valid():
#             ser.save()
#             return Response({'code': 100, 'msg': '成功'})
#         else:
#             return Response({'code': 100, 'msg': ser.errors})
#
#
# class BookDetailView(GenericAPIView):
#     queryset = Book.objects.all()
#     serializer_class = BookSerialzier
#
#     def get(self, request, pk):
#         book = self.get_object()
#         ser = self.get_serializer(book)
#         return Response({'code': 100, 'msg': '成功', 'results': ser.data})
#
#     def put(self, request, pk):
#         book = self.get_object()
#         ser = self.get_serializer(data=request.data, instance=book)
#         if ser.is_valid():
#             ser.save()
#             return Response({'code': 100, 'msg': '更新成功'})
#         else:
#             return Response({'code': 100, 'msg': ser.errors})

5.2 使用GenericAPIView+序列化类+Response写接口

class BookView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier

    def get(self, request):
        qs = self.get_queryset()
        ser = self.get_serializer(qs, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class BookDetailView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier

    def get(self, request, pk):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

    def put(self, request, pk):
        book = self.get_object()
        ser = self.get_serializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值