DRF10大接口

自定义Respoense

虽然我们已经在使用,rest_framework的Response,但是代码还是有点冗余,于是就可以自己重写一下(具体封装可以更具实际需求变更)

重写

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基础有数据状态码和数据状态信息
        data = {  
            'status': status,
            'msg': msg
        }
        # 如果后台有数据,响应数据
        if results is not None:  
            data['results'] = results
        # 后台的一切自定义响应数据直接放到响应数据data中
        data.update(**kwargs)  
        super().__init__(data=data, status=http_status, headers=headers, exception=exception)

使用

#查询正确,修改默认的状态码,和msg和http状态码
return APIResponse(status=1,msg="单查 error",http_status=400)

#查询错误,使用默认的msg和状态码
return APIResponse(results=user_data)

modles基表

modles里面的表有公共字段,可以提取到基表上

基表注意

设置内嵌meta类的abstract为True,默认为False(为False会创建这个表)

is_delete字段

用来做逻辑删除,因为数据是有价值的

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

Models去物理外键为逻辑外键

断关联的特点

1.表之间没有外键关联,但是有外键逻辑关联(但是要注意,由于没有物理的约束,所以要在逻辑上保证数据操作的安全)
2、断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(因为少做了一步判断外键数据是否存在的操作)

db_constraint

断关联,但是级联关系还在(默认级联删除),所以要自己设置,如下

db_constraint=False

on_delete--级联删除

在django1.x中默认有,2.x中必须自己实现

on_delete=models.CASCADE

注意多对多例外

1.多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联

2.ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表

作者没了,详情也没:on_delete=models.CASCADE
出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING

#下面两个比较类似
部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT

related_name

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

obj = models.Author.objects.filter(pk=1).first()
print(obj.detail.mobile)
obj2 = models.AuthorDetail.objects.filter(pk=1).first()
print(obj2.author.name)

案例

# 多表:Book,Publish,Author,AuthorDetail
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)
    class Meta:
        abstract = True

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)

    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

class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)

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)

    author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)

DRF多表关系

一对多 一对一

方案一

其实就像利用了,自定义字段的特点,可以去单独返回值拿到数据,最后在序列化数据,但是要注意返回的参数不可以是对象的形式,不然不能支持除本对象其他对象的序列化

fields

 fields = ('name', 'price', 'publish_name', 'author_list')

models

    #self为book对象,直接用它进行多表查询,返回想要的数据
    
    @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

方案二-自定义深度

对比

对比上面的不能传对象,这种方法直接把别的数据的序列化器拿来使用,可以序列化其他的记录数据了

但是不能进行反序列化了,会直接报错

前提

如果只有查需求的接口,自定义深度还可以用子序列化方式完成

class PublishModelSerializer(serializers.ModelSerializer):
   子序列化都是提供给外键(正向方向)完成深度查询的,外键数据是唯一:many=False,不唯一many=True
    #只有带外键的记录能这样搞
    books = BookModelSerializer(many=True)
    class Meta:
        model = models.Publish
        fields = ('name', 'address', 'books')

单查,群查

    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)

单删,群删

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        # 将单删群删逻辑整合,都设为列表形式统一
        if pk:
            pks = [pk]
        else:
            pks = request.data.get('pks')
        # 前台数据有误(主要是群删没有提供pks)
        if not pks:
            return APIResponse(1, 'delete error', http_status=400)
        # 只要有操作受影响行,就是删除成功,反之失败
        # 这里使用的是存orm的方法
        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')

单增,群增

要注意序列化和反序列化的字段设置

原理

其实就是调用了ListSerializer的create方法,然后循环调用ModelSerializer的create增加,最后ListSerializer返回循环的结果

注意

但是ListSerializer只提供了create方法,却没有提供update方法,要实现群改方法还要自己重写实现(为啥不重写呢?可能是因为群改的需求太复杂了)

    def post(self, request, *args, **kwargs):
        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)

单整体改,群整体改

由于默认会调用ListSerializer的create方法,但是ListSerializer没有重写,所以需要我们自己实现

重写

只要重写实现循环调用,ModelSerializer的update函数就行,最后把结果返回

class BookListSerializer(serializers.ListSerializer):

    #父级ListSerializer没有通过update方法的实现体,需要自己重写
    def update(self, instance, validated_data):
        
        #self.child能反向获取ModelSerializer函数
        return [
            self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
        ]

绑定

在ModelSerializer添加与ListSerializer的绑定关系,注意在meta里面加

        # 关联ListSerializer完成群增群改
        list_serializer_class = BookListSerializer
   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要被赋值为被修改对象,这样两个值都有才会去调用update方法
            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)

            # 注:一定不要在循环体中对循环对象进行增删(影响对象长度)的操作(危险操作)
            obj_list = []
            data_list = []
            for dic in request_data:
                #由于内部数据不一定是dict,所以修改操作要注意异常
                try:
                    #如果数据不是字典就会报错
                    pk = dic.pop('pk')
                    try:
                        #可能没有这个id的数据可以修改
                        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)

单局部改,全局部改

局部和全局的区别就是不用提供全部的字段数据

所以只要在反序列化的方法加上partial=True就行了

# 局部修改就是在整体修改基础上设置partial=True,将所有参与反序列化字段设置为required=False
book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data, partial=True)

小知识点

上面的ListSerializer,ModelSerializer相互绑定可能就是这么实现的

下面代码的mm既有wl的内容又有zx的内容

class wl():
    @classmethod
    def w(cls):
        print(cls.child)

class zx():
    class meta():
        @classmethod
        def m(cls):
            mm=wl
            mm.child=zx
            print(mm)
            return mm

zx.meta.m().w()


<class '__main__.wl'>
<class '__main__.zx'>

猜你喜欢

转载自www.cnblogs.com/zx125/p/11909401.html