• day 79 drf 多表关联操作


    1、(重点)二次封装Response:自定义APIResponse继承Response,重写__init__方法

     api/response.py

    from rest_framework.response import Response

    class APIResponse(Response):
    # 格式化data
    def __init__(self, status=0, msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
    data = { # json的response基础数据状态码和数据状态信息
    'status': status,
    'msg': msg
    }
    if results is not None: # 后台有数据,响应数据
    data['results'] = results
    data.update(**kwargs) # 后台的一切自定义响应数据直接放到响应数据data中
    super().__init__(data=data, status=http_status, headers=headers, exception=exception)

    2、(正常)设置了abstract为True的模型类,称之为基表,这样的模型类是专门作为基类来提供公有属性的

    # Model类的内部配置Meta类要设置abstract=True,这样的Model类就是用来作为基表
    class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)
    class Meta:
    # 基表必须设置abstract,基表就是给普通Model类继承使用的,设置了abstract就不会完成数据库迁移完成基表的建表
    abstract = True



    3、(重点)ORM多表关联操作:

    外键所放位置
    一对多:外键放在多的一方
    多对多:外键放在常用的一方
    一对一:外键放在不常用的一方

    外键字段为正向查询字段,related_name是反向查询字段

    外键断关联:设置外键字段db_constraint=False

    # 1)表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
    # 2)断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
    # 3)断关联一定要通过逻辑保证表之间数据的安全

    外键间的级联关系
    一对一:作者没了,详情也没:on_delete=models.CASCADE
    一对多:出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
    一对多:部门没了,员工没有部门(设置为空):null=True, on_delete=models.SET_NULL
    一对多:部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT
    多对多:不能设置on_delete

    class Author(BaseModel):
    name = models.CharField(max_length=16)
    sex = models.IntegerField(choices=[(0, '男'),(1, '女')], default=0)
    class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    # 有作者可以没有详情,删除作者,详情一定会被级联删除
    # 外键字段为正向查询字段,related_name是反向查询字段
    author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)

    4、(重点)连表序列化,在Model类中定义插拔序列化方法属性,完成连表查询

    class Book(BaseModel):
    name = models.CharField(max_length=16)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
    # 重点:多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联
    # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)

    # 自定义连表深度,不能反序列化,因为自定义插拔属性不参与反序列化
    @property
    def publish_name(self):
    return self.publish.name
    @property
    def author_list(self):
    temp_author_list = []
    for author in self.authors.all():
    temp_author_list.append({
    'name': author.name,
    'sex': author.get_sex_display(),
    'mobile': author.detail.mobile
    })
    return temp_author_list

    5、(正常)子序列化可以辅助快速实现自定义外键深度的序列化,但是不能进行反序化

    子序列化的意思其实就是序列化中的序列化,因为,books(book类的related_name)是publish的外键,会通过这层联系,将book类传入,然后

    将publish的books外键进行格式化,这样序列化返回出的字段,是book类中的所有字段,如果没有子序列化的话,books序列化返回的仅是pubish的

    外键值

    class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)
    class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
            model = models.Book
    # 了解
    # fields = '__all__' #所有字段都序列化
    # exclude = ('id', )  #除了id字段都序列化
    # depth = 1        #默认深度,返回一次连表的字段

    # 序列化与反序列化整合
    fields = ('name', 'price', 'publish_name', 'author_list', 'publish', 'authors')
    extra_kwargs = {
    'publish': {
    'write_only': True
    },
    'authors': {
    'write_only': True
    }
    }
    # 前提:如果只有查需求的接口,自定义深度还可以用子序列化方式完成
    class PublishModelSerializer(serializers.ModelSerializer):
    # 子序列化都是提供给外键(包括:正向反向)完成深度查询的,外键数据是唯一:many=False;不唯一:many=True
    # 注:只能参与序列化,且反序列化不能写(反序列化外键字段会抛异常)
    books = BookModelSerializer(many=True)
    class Meta:
    model = models.Publish
    fields = ('name', 'address', 'books')

    6、(重点)单查群查接口,序列化类提供序列化对象,many参数控制着操作的数据是一条还是多条

    class BookAPIView(APIView):
    # 单查、群查
    def get(self, request, *args, **kwargs):
    pk = kwargs.get('pk')
    if pk:
    book_obj = models.Book.objects.filter(pk=pk, is_delete=False).first()
    if not book_obj:
    return APIResponse(1, 'pk error', http_status=400)
    book_data = serializers.BookModelSerializer(book_obj).data
    return APIResponse(results=book_data)

    book_query = models.Book.objects.filter(is_delete=False).all()
    return APIResponse(0, 'ok', data=serializers.BookModelSerializer(book_query, many=True).data)

    7、(正常)单删群删接口,后台操作删除字段即可,前台提供pk就是单删,提供pks就是群删

    # 单删、群删
    def delete(self, request, *args, **kwargs):
    """
    单删:前台数据为pk,接口为 /books/(pk)/
    群删:前台数据为pks,接口为 /books/
    """
    pk = kwargs.get('pk')
    # 将单删群删逻辑整合
    if pk: # /books/(pk)/的接口就不考虑群删,就固定为单删
    pks = [pk]
    else:
    pks = request.data.get('pks')
    # 前台数据有误(主要是群删没有提供pks)
    if not pks:
    return APIResponse(1, 'delete error', http_status=400)
    # 只要有操作受影响行,就是删除成功,反之失败
    rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
    if rows:
    return APIResponse(0, 'delete ok')
    return APIResponse(1, 'delete failed')

    8、(重点)单增群增接口,根据数据判断是单增还是群增,对应序列化类要设置many,而序列化类只需要通过data即可

    # 单增、群增
    def post(self, request, *args, **kwargs):
    """
    单增:前台提交字典,接口 /books/
    群增:前台提交列表套字典,接口 /books/
    """
    request_data = request.data
    if isinstance(request_data, dict): # 单增
    book_ser = serializers.BookModelSerializer(data=request_data)
    if book_ser.is_valid():
    book_obj = book_ser.save()
    return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
    return APIResponse(1, msg=book_ser.errors)
    elif isinstance(request_data, list) and len(request_data) != 0 : # 群增
    book_ser = serializers.BookModelSerializer(data=request_data, many=True)
    book_ser.is_valid(raise_exception=True)
    book_obj_list = book_ser.save()
    return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)
    else:
    return APIResponse(1, 'data error', http_status=400)

    9、(正常)单局部改,序列化类参数instance=修改的对象, data=修改的数据, partial=是否能局部修改,单整体修改就是partial=False(默认就是False)

    # 单整体改、群整体改
    def put(self, request, *args, **kwargs):
    """
    单整体改:前台提交字典,接口 /books/(pk)/
    群整体改:前台提交列表套字典,接口 /books/,注每一个字典都可以通过pk
    """
    pk = kwargs.get('pk')
    request_data = request.data
    if pk: # 单改
    try:
    book_obj = models.Book.objects.get(pk=pk)
    except:
    return APIResponse(1, 'pk error')

    # 修改和新增,都需要通过数据,数据依旧给data,修改与新增不同点,instance要被赋值为被修改对象
    book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data)
    book_ser.is_valid(raise_exception=True)
    book_obj = book_ser.save()
    return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
    else: # 群改
    if not isinstance(request_data, list) or len(request_data) == 0:
    return APIResponse(1, 'data error', http_status=400)

    # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
    # 要考虑pk对应的对象是否被删,以及pk没有对应的对象
    # 假设pk3被删,pk100没有 => [obj1] + [{...}]

    # 注:一定不要在循环体中对循环对象进行增删(影响对象长度)的操作
    obj_list = []
    data_list = []
    for dic in request_data:
    # request_data可能是list,单内部不一定是dict
    try:
    pk = dic.pop('pk')
    try:
    obj = models.Book.objects.get(pk=pk, is_delete=False)
    obj_list.append(obj)
    data_list.append(dic)
    except:
    pass
    except:
    return APIResponse(1, 'data error', http_status=400)

    book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True)
    book_ser.is_valid(raise_exception=True)
    book_obj_list = book_ser.save()
    return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)

    # 单局部改、群局部改
    def patch(self, request, *args, **kwargs):
    """
    单整体改:前台提交字典,接口 /books/(pk)/
    群整体改:前台提交列表套字典,接口 /books/,注每一个字典都可以通过pk
    """
    pk = kwargs.get('pk')
    request_data = request.data
    if pk:
    try:
    book_obj = models.Book.objects.get(pk=pk)
    except:
    return APIResponse(1, 'pk error')
    # 局部修改就是在整体修改基础上设置partial=True,将所有参与反序列化字段设置为required=False
    book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data, partial=True)
    book_ser.is_valid(raise_exception=True)
    book_obj = book_ser.save()
    return APIResponse(results=serializers.BookModelSerializer(book_obj).data)

    else: # 群改
    if not isinstance(request_data, list) or len(request_data) == 0:
    return APIResponse(1, 'data error', http_status=400)

    # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
    # 要考虑pk对应的对象是否被删,以及pk没有对应的对象
    # 假设pk3被删,pk100没有 => [obj1] + [{...}]

    # 注:一定不要在循环体中对循环对象进行增删(影响对象长度)的操作
    obj_list = []
    data_list = []
    for dic in request_data:
    # request_data可能是list,单内部不一定是dict
    try:
    pk = dic.pop('pk')
    try:
    obj = models.Book.objects.get(pk=pk, is_delete=False)
    obj_list.append(obj)
    data_list.append(dic)
    except:
    pass
    except:
    return APIResponse(1, 'data error', http_status=400)

    book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True, partial=True)
    book_ser.is_valid(raise_exception=True)
    book_obj_list = book_ser.save()
    return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)

    10、(了解)群改,前台提供的数据,后台要转化成要修改的对象们和用来更新的数据们,ModelSerializer设置list_serializer_class关联自己的ListSerializer,重写update方法,完成群改

    群增,群改的时候,不走modelserializer的create和update方法,而是走listserializer里的create和update方法,但是listserializer只提供了create方法,update

    方法需要自己写

    class BookListSerializer(serializers.ListSerializer):
    # 1、create方法父级ListSerializer已经提供了
    # def create(self, validated_data):
    # # 通过self.child来访问绑定的ModelSerializer
    # print(self.child)
    # raise Exception('我不提供')

    # 2、父级ListSerializer没有通过update方法的实现体,需要自己重写
    def update(self, instance, validated_data):
    # print(instance)
    # print(validated_data)
    return [
    self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
    ]
    class BookModelSerializer(serializers.ModelSerializer):
    # 通过BookModelSerializer.Meta.list_serializer_class来访问绑定的ListSerializer
    class Meta:
    # 关联ListSerializer完成群增群改
    list_serializer_class = BookListSerializer
  • 相关阅读:
    作为一个程序猿,是不是经常会用到.chm文档,但是我们可能会遇到这样那样的问题,比如.chm文档打不开
    总结
    图片轮播的几种方式
    学习中于遇到的问题总结
    node 总结
    webpack 总结
    babel 的总结和理解
    关于css 的AST 语法树的理解
    js中的正则表达式
    八皇后
  • 原文地址:https://www.cnblogs.com/wwei4332/p/11908347.html
Copyright © 2020-2023  润新知