zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    ZOJ 3332 Strange Country II
    ZOJ 3331 Process the Tasks(双塔DP)
    ZOJ 3326 An Awful Problem(模拟)
    HDU 1796 How many integers can you find(容斥原理)
    HDU 4059 The Boss on Mars(容斥原理)
    HDU 4135 Co-prime(容斥原理)
    HDU 5677 ztr loves substring(回文串加多重背包)
    CodeForces 668B Little Artem and Dance
    CodeForces 667A Pouring Rain
    Java实现 LeetCode 764 最大加号标志(暴力递推)
  • 原文地址:https://www.cnblogs.com/wwei4332/p/11908347.html
Copyright © 2011-2022 走看看