def序列化器---视图&序列化&反序列化

0.介绍

a. 本文介绍了常用的,视图&反序列化&序列化,中使用的方法。

b. 序列化器他有两个作用:序列化、校验。

c. 序列化、校验,可以走一个序列化器,也可以走两个。

1. 视图

1.1 选择serializer

class TestView(ListAPIView,CreateAPIView):
    queryset = models.Topic.objects
    serializer_class = TestSER

    def get_serializer_class(self):
        # self.auction == 'list'
        if self.request.method == "GET":
            return TestSER
        return TestSER2

1.2 选择authenticator

权限频率都是一样的使用,都是做判断,返回一个对象列表即可

class CommentAPIView(APIView):
    """获取更多评论"""

    def get_authenticators(self):
        # 重写认证类
        if self.request.method == 'POST':
            return [LoginAuthentication(), ]
        return [PublicAuthentication(), ]

    def get(self, request, *args, **kwargs):
        """获取更多评论,通过root"""
        # print('1', request.user)
        root = int(request.query_params.get('root'))
        # print(root)
        queryset = models.Comment.objects.filter(root=root)
        # print(queryset)
        ser = CommentModelSerializer(instance=queryset, many=True)
        return Response(data=ser.data)

    def post(self, request, *args, **kwargs):
        """保存评论数据"""
        ser = PostCommentModelSerializer(data=request.data)  # ...
        # print(request.data)
        if ser.is_valid():
            ser.save(userinfo_id=request.user)
            # 评论数+1
            new_id = ser.validated_data.get('new').id
            models.Release.objects.filter(id=new_id).update(release_num=F('release_num') + 1)
            return Response(ser.data, status=status.HTTP_201_CREATED)
        return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)

1.3 自定义视图函数

比如:继承ListAPIView重写list方法,

继承RetrieveAPIView重写list方法retrieve

class AuctionItemDetailView(RetrieveAPIView):
    """拍品详细,前端传过来,相应的商品id"""
    queryset = models.AuctionItem.objects.filter(status__gt=1)
    serializer_class = AuctionItemDetailModelSerializer

    def retrieve(self, request, *args, **kwargs):
        response = super(AuctionItemDetailView, self).retrieve(request, *args, **kwargs)
        # 路由的pk可以从kwargs中取
        pk = kwargs.get('pk')
        user_obj = request.user
        if user_obj:
            models.BrowseRecord.objects.get_or_create(user=user_obj, item_id=pk)
            # 商品记录加一
            models.AuctionItem.objects.filter(pk=pk).update(look_count=F('look_count') + 1)
        return response


1.4 自定义filter做筛选

自定义过滤/分页,实现微信小程序,上拉下拉https://blog.csdn.net/qq_52385631/article/details/123101613?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164623539316780265457863%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=164623539316780265457863&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-15-123101613.nonecase&utm_term=%E5%BE%AE%E4%BF%A1&spm=1018.2226.3001.4450

1.5 perform_create(用户自己传入一些数据)

# 用户自己传入一些数据
# 与重写create不同的是,perform_create,使用默认的create,但可以自己传入数据

# 案例

class CommentAPIView(ListAPIView, CreateAPIView):
    """获取更多评论&&保存评论视图"""

    queryset = models.Comment.objects.all()
    serializer_class = TextSerializer


    def perform_create(self, serializer):
        # 执行创建传入默认的数据
        serializer.save(userinfo=self.request.user)

1.5 重写视图/添加过滤条件/create之前

class AuctionBidView(ListAPIView, CreateAPIView):
    """竞价 www.xxxx.com/../../?item_id=1"""
    authentication_classes = [LoginAuthentication, ]
    queryset = models.BidRecord.objects.filter(status=1)
    serializer_class = BidRecordModelSerializer

    def get_queryset(self):
        # 进行过滤,找到当前出价的商品
        item_id = self.request.query_params.get('item_id')
        queryset = self.queryset.filter(item_id=item_id)
        return queryset

    def get(self, request, *args, **kwargs):
        # 重写get,构造返回的数据
        response = super(AuctionBidView, self).get(request, *args, **kwargs)
        # 序列化之后的数据
        # [OrderedDict([('id', 1), ('status', '竞价'), ('item', 2), ('user', 1)]), OrderedDict([('id', 2), ('status', '竞价'), ('item', 2), ('user', 1)])]
        # print(response.data)
        item_id = self.request.query_params.get('item_id')
        item_obj = models.AuctionItem.objects.filter(id=item_id).first()
        # 取最大价格,如果没有,取低价

        # 进行聚合,求出最大值,{'max_price':1200}
        max_price = models.BidRecord.objects.filter(status=1, item_id=item_id).aggregate(max_price=Max('price'))[
            'max_price']
        data = {
            'unit': item_obj.unit,
            'price': max_price or item_obj.start_price,
            'result': response.data
        }
        response.data = data
        return response

    def perform_create(self, serializer):
        # 用户自己添加数据
        # 添加锁
        from django.db import transaction
        from rest_framework.exceptions import ValidationError
        with transaction.atomic():
            item_id = self.request.data.get('item_id')
            price = self.request.data.get('price')
            # 加锁,同一个价格只能一个出价  select_for_update
            result = models.BidRecord.objects.filter(status=1, item_id=item_id).aggregate(
                max_price=Max('price')).select_for_update()
            max_price = result.get('max_price')
            if price < max_price:
                raise ValidationError('价格不够!')
            serializer.save(user=self.request.user)

1.6 重写视图,拿到序列化好的字段,进行修改

1.6.1 案例一

    def get(self, request, *args, **kwargs):
        # 重写get,构造返回的数据
        response = super(AuctionBidView, self).get(request, *args, **kwargs)
        # 序列化之后的数据
        # [OrderedDict([('id', 1), ('status', '竞价'), ('item', 2), ('user', 1)]), OrderedDict([('id', 2), ('status', '竞价'), ('item', 2), ('user', 1)])]
        # print(response.data)
        item_id = self.request.query_params.get('item_id')
        item_obj = models.AuctionItem.objects.filter(id=item_id).first()
        # 取最大价格,如果没有,取低价

        # 进行聚合,求出最大值,{'max_price':1200}
        max_price = models.BidRecord.objects.filter(status=1, item_id=item_id).aggregate(max_price=Max('price'))[
            'max_price']
        data = {
            'unit': item_obj.unit,
            'price': max_price or item_obj.start_price,
            'result': response.data
        }
        response.data = data
        return response

1.6.2 案例二 

class CouponUserView(ListAPIView, CreateAPIView):
    """用户的优惠劵"""
    authentication_classes = [LoginAuthentication, ]
    serializer_class = CouponUserSerializer

    def get_queryset(self):
        queryset = models.UserCoupon.objects.filter(user=self.request.user, is_delete=False, is_show=True).order_by(
            '-id')
        return queryset

    def list(self, request, *args, **kwargs):
        response = super(CouponUserView, self).list(request, *args, **kwargs)
        # 如果有问题,报错
        if response.status_code != status.HTTP_200_OK:
            return response
        # 更改返回的格式 {1:{'text':'未使用',child:[] }}
        # 有序字典
        status_dict = OrderedDict()
        for item in models.UserCoupon.status_type:
            status_dict[item[0]] = {'text': item[1], 'child': []}

        for row in response.data:
            # print(row)
            status_dict[int(row['status'])]['child'].append(row)

        response.data = status_dict
        return response

  

2. 反序列化

2.1 普通校验

class TestSER(serializers.ModelSerializer):
    class Meta:
        model = models.Topic
        fields = "__all__"

2.2 自定义校验规则---validators

import re
from rest_framework.exceptions import ValidationError


def phone_validator(value):
    if not re.match(r"^(1[3|4|5|6|7|8|9])\d{9}$",value):
        raise ValidationError('手机格式错误')

class TestSER(serializers.ModelSerializer):
    title = serializers.CharField(label='手机号',validators=[phone_validator,])
    class Meta:
        model = models.Topic
        fields = "__all__"

2.3 自定义方法校验---全局/局部钩子

class TestSER(serializers.ModelSerializer):
    title = serializers.CharField(label='手机号')
    class Meta:
        model = models.Topic
        fields = "__all__"
    
    def validate_title(self, value):
        # 具体的校验规则
        return value

2.4 序列化嵌套(反序列化) 一般前端传送列表套字典,都是用该方法

url 
    path('release/', ReleaseAPIView.as_view()),
    
view
from rest_framework.views import APIView
from utils.centent.cos import credential
from rest_framework.response import Response
from rest_framework.generics import CreateAPIView

from api.serializer import business

class ReleaseAPIView(CreateAPIView):
    """发布保存视图"""
    serializer_class = business.ReleaseModelSerializer

    def get_queryset(self):
        # 重写queryset
        queryset = models.Release.objects.filter(is_delete=False, is_show=True)
        return queryset

    def post(self, request, *args, **kwargs):
        # 重写post
        response = super(ReleaseAPIView, self).post(request, *args, **kwargs)
        # 进行自定义返回数据
        response.data = ''
        return response
    def perform_create(self, serializer):
        """在保存之前为对应的topic人数+1"""
        print("serializer", serializer.data)
        # serializer {'position': 'xx', 'content': 'xx', 'userinfo': 2, 'topic': 1, 'imageList': [OrderedDict([('cos_url', 'xx'), ('cos_media_name', 'xx')]), OrderedDict([('cos_url', 'xx'), ('cos_media_name', 'xx')])]}
        print("serializer", serializer.data.get('topic'))
		# serializer 1
        
 ser
 from rest_framework import serializers

from api import models


class MediaModelSerializer(serializers.ModelSerializer):
    # 用作序列化器嵌套
    class Meta:
        model = models.Media
        fields = ['cos_url', 'cos_media_name']


class ReleaseModelSerializer(serializers.ModelSerializer):
    """发布保存序列化器"""
	# imageList默认会序列化,必须加一个write_only,不然报错
    imageList = MediaModelSerializer(many=True, write_only=True)

    class Meta:
        model = models.Release
        fields = ['position', 'content', 'userinfo', 'topic', 'imageList']

    def validate(self, attrs):
        imageList = attrs.get('imageList')
        # print(imageList)  # [OrderedDict([('cos_url', 'xx'), ('cos_media_name', 'xx')]), OrderedDict([('cos_url', 'xx'), ('cos_media_name', 'xx')])]
        return attrs

2.5 某个字段,反序列化不进行校验,序列化的时候忽略

class TestSER(serializers.ModelSerializer):
    # 序列化忽略
    name = serializers.CharField(label='手机号',wirte_only=True)
    # 反序列化不进行校验
    title = serializers.CharField(label='手机号',read_only=True)
    class Meta:
        model = models.Topic
        fields = "__all__"

2.6 ps 关于write_only/read_only 详解

详解https://blog.csdn.net/qq_52385631/article/details/122767023?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164604096016780366595849%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=164604096016780366595849&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-3-122767023.nonecase&utm_term=%E5%BA%8F%E5%88%97%E5%8C%96&spm=1018.2226.3001.4450

3. 序列化

3.1 普通序列化

class TestSER(serializers.ModelSerializer):
    class Meta:
        model = models.Topic
        fields = "__all__"

3.2 自定义字段+source

class TestSER(serializers.ModelSerializer):
    v1 = serializers.CharField(label='手机号',source='get_gender_display')
    class Meta:
        model = models.Topic
        fields = "__all__"

3.3 自定义时间字段

class TestSER(serializers.ModelSerializer):
    v1 = serializers.DateTimeField(format="%Y")
    class Meta:
        model = models.Topic
        fields = "__all__"

3.4 自定义返回格式---get_xxx

from django.forms.models import model_to_dict


class NewsDetailModelSerializer(serializers.ModelSerializer):
    images = serializers.SerializerMethodField()
    create_date = serializers.DateTimeField(format="%Y-%m-%d %H:%M")

    user = serializers.SerializerMethodField()
    topic = serializers.SerializerMethodField()

    viewer = serializers.SerializerMethodField()
    comment = serializers.SerializerMethodField()

    is_favor = serializers.SerializerMethodField()

    class Meta:
        model = models.News
        exclude = ['cover',]

    def get_images(self,obj):
        detail_queryset = models.NewsDetail.objects.filter(news=obj)
        # return [row.cos_path for row in detail_queryset]
        # return [{'id':row.id,'path':row.cos_path} for row in detail_queryset]
        return [model_to_dict(row,['id','cos_path']) for row in detail_queryset]

    def get_user(self, obj):
        return model_to_dict(obj.user, fields=['id', 'nickname', 'avatar'])

    def get_topic(self, obj):
        if not obj.topic:
            return
        return model_to_dict(obj.topic, fields=['id', 'title'])

    def get_viewer(self,obj):
        # 根据新闻的对象 obj(news)
        # viewer_queryset = models.ViewerRecord.objects.filter(news_id=obj.id).order_by('-id')[0:10]
        queryset = models.ViewerRecord.objects.filter(news_id=obj.id)
        viewer_object_list = queryset.order_by('-id')[0:10]
        context = {
            'count':queryset.count(),
            'result': [model_to_dict(row.user,['nickname','avatar']) for row in viewer_object_list]
        }
        return context

    def get_comment(self,obj):
        """
        获取所有的1级评论,再给每个1级评论获取一个耳机评论。
        :param obj:
        :return:
        """

        # 1.获取所有的 一级 评论
        first_queryset = models.CommentRecord.objects.filter(news=obj,depth=1).order_by('id')[0:10].values(
            'id',
            'content',
            'depth',
            'user__nickname',
            'user__avatar',
            'create_date'
        )
        first_id_list = [ item['id'] for item in first_queryset]
        # 2.获取所有的二级评论
        # second_queryset = models.CommentRecord.objects.filter(news=obj,depth=2)
        # 2. 获取所有1级评论下的二级评论
        # second_queryset = models.CommentRecord.objects.filter(news=obj, depth=2,reply_id__in=first_id_list)
        # 2. 获取所有1级评论下的二级评论(每个二级评论只取最新的一条)
        from django.db.models import Max
        result = models.CommentRecord.objects.filter(news=obj, depth=2, reply_id__in=first_id_list).values('reply_id').annotate(max_id=Max('id'))
        second_id_list = [item['max_id'] for item in result] # 5, 8

        second_queryset = models.CommentRecord.objects.filter(id__in=second_id_list).values(
            'id',
            'content',
            'depth',
            'user__nickname',
            'user__avatar',
            'create_date',
            'reply_id',
            'reply__user__nickname'
        )

        import collections
        first_dict = collections.OrderedDict()
        for item in first_queryset:
            item['create_date'] = item['create_date'].strftime('%Y-%m-%d')
            first_dict[item['id']] = item

        for node in second_queryset:
            first_dict[node['reply_id']]['child'] = [node,]

        return first_dict.values()

    def get_is_favor(self,obj):
        # 1. 用户未登录
        user_object = self.context['request'].user
        if not user_object:
            return False

        # 2. 用户已登录
        exists = models.NewsFavorRecord.objects.filter(user=user_object,news=obj).exists()
        return exists

3.5 自定义返回数据格式---models添加方法

class Release(BaseModel):
    """发布"""
    position = models.CharField(verbose_name='位置', max_length=225, blank=True, null=True)
    content = models.CharField(verbose_name='内容', max_length=225)

    # 这三个存数量,做一个优化
    release_up = models.BigIntegerField(verbose_name='新闻点赞数量', default=0)
    watch_num = models.BigIntegerField(verbose_name='观看人数数量', default=0)
    release_num = models.BigIntegerField(verbose_name='评论数量', default=0)
    # db_constraint=False db_constraint=False  逻辑上的关联,实质上没有外键联系,增删不会受外键影响,orm查询不影响
    userinfo = models.ForeignKey(verbose_name='用户', to='UserInfo', on_delete=models.DO_NOTHING, db_constraint=False)
    topic = models.ForeignKey(verbose_name='话题', to='Topic', on_delete=models.DO_NOTHING, db_constraint=False,
                              null=True, blank=True, related_name='topic_release')

    # 返回图片
    @property
    def cover_msg_url(self):
        # 每一个对象走一遍
        msg_url = self.media.all().filter(is_show=True, is_delete=False).first().cosUrl
        # print(msg_url)
        return msg_url


################序列化器####################
class ReleaseModelSerializer(serializers.ModelSerializer):
    """话题序列化器"""

    class Meta:
        model = models.Release
        fields = ['id', 'cover_msg_url']

3.6 1.6 在序列化器里面使用序列化器

class AuctionItem(serializers.ModelSerializer):
    """拍卖场详细--拍品 序列化器嵌套"""
    is_deposit = serializers.SerializerMethodField()

    class Meta:
        model = models.AuctionItem
        fields = ['id', 'uid', 'title', 'cover', 'is_deposit',
                  'start_price', 'reserve_price', 'highest_price',
                  'unit',
                  ]

    # 是否缴纳单品保证金
    # 是否缴纳全场/单品保证金
    def get_is_deposit(self, obj):
        # 判断是否登录
        user = self.context['request'].user
        if not user:
            return False
        # 如果是全场,单品为null
        # 判断是否交了保障金,True/False
        exist = models.DepositRecord.objects.filter(user=user, item=obj, status=2).exists()
        return exist


class AuctionDetailModelSerializer(serializers.ModelSerializer):
    """拍卖场详细"""
    status = serializers.CharField(source='get_status_display')
    auction_item = serializers.SerializerMethodField()
    preview_start_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
    preview_end_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
    auction_start_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
    auction_end_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
    is_deposit = serializers.SerializerMethodField()

    class Meta:
        model = models.Auction
        fields = [
            'id', 'status', 'title', 'cover', 'is_deposit', 'preview_start_time',
            'preview_end_time', 'auction_start_time', 'auction_end_time',
            'look_count', 'auction_item'
        ]

    def get_auction_item(self, obj):
        # 查询拍卖品
        item_list = models.AuctionItem.objects.filter(auction=obj)
        # 把request传入上一个序列化器,序列化器里面使用序列化器
        ser = AuctionItem(instance=item_list, many=True, context=self.context)
        return ser.data

    # 是否缴纳全场/单品保证金
    def get_is_deposit(self, obj):
        # 判断是否登录
        user = self.context['request'].user
        if not user:
            return False
        # 如果是全场,单品为null
        # 判断是否交了保障金,True/False
        exist = models.DepositRecord.objects.filter(user=user, item__isnull=True, status=2).exists()
        return exist

4. 序列化的方法中获取request||context存值

class TestSER(serializers.ModelSerializer):
    title = serializers.SerializerMethodField()
    class Meta:
        model = models.Topic
        fields = "__all__"

    def get_title(self,obj):
        request = self.context['request']


# serializer放值
# ser
    def _give_data(self, user_obj, token):
        """
        在context赋值,用于返回前端
        """
        self.context['user_id'] = user_obj.id
        self.context['username'] = user_obj.username
        self.context['phone'] = user_obj.phone
        self.context['avatar'] = user_obj.avatar
        self.context['bucket'] = user_obj.bucket
        self.context['region'] = user_obj.region
        self.context['balance'] = user_obj.balance
        self.context['token'] = token
# view
from rest_framework import status
from rest_framework.response import Response
from rest_framework.generics import RetrieveAPIView, CreateAPIView

from api.serializer.account import LoginRegisterModelSerializer


class LoginRegisterAPIView(RetrieveAPIView, CreateAPIView):
    """登录注册视图"""
    # post请求进行
    authentication_classes = []

    def post(self, request, *args, **kwargs):
        ser = LoginRegisterModelSerializer(data=request.data)

        if ser.is_valid():
            # 获取数据返回
            result = {
                "status": 1000,
                "data": {
                    "user_id": ser.context.get('user_id'),
                    "username": ser.context.get('username'),
                    "phone": ser.context.get('phone'),
                    "avatar": ser.context.get('avatar'),
                    "bucket": ser.context.get('bucket'),
                    "region": ser.context.get('region'),
                    "balance": ser.context.get('balance'),
                    "token": ser.context.get('token'),
                }
            }
            return Response(data=result, status=status.HTTP_200_OK)
        error = ser.errors
        return Response(data=error, status=status.HTTP_400_BAD_REQUEST)

5. 序列化器里面使用序列化器,如果最上面的,想要使用request的话,是下面的序列化器,传过去的

  
        ser = AuctionItem(instance=item_list, many=True, context=self.context)

6. 在局部钩子里面拿到其他的字段

    def validate_price(self, attrs):


        # initial_data--在局部钩子里面,获取其他的字段
        item_id = self.initial_data.get('item')

   
        return attrs

7. 聚合

class BidRecordModelSerializer(serializers.ModelSerializer):
    status = serializers.CharField(source='get_status_display', read_only=True)
    username = serializers.CharField(source='user.nickName', read_only=True)
    id = serializers.CharField(read_only=True)

    class Meta:
        model = models.BidRecord
        fields = ['id', 'status', 'item', 'price', 'username']

    def validate_item(self, attrs):
        # item传的就是id,但被反射成立对象
        exist = models.AuctionItem.objects.filter(id=attrs.id, status=3).exists()
        if not exist:
            raise serializers.ValidationError('当前商品未开始竞拍,请等待!')

    def validate_price(self, attrs):
        # 底价/最高价/加价幅度 item---这里是id
        item_id = self.initial_data.get('item')
        auction_obj = models.AuctionItem.objects.filter(id=item_id).first()
        if int(attrs) < auction_obj.start_price:
            raise serializers.ValidationError('价格必须比低价高!')
        # 1. 去出价表,查出最新最大的一条价格记录
        # 2. 当前价格与查询的价格进行比较,
        from django.db.models import Max
        # 获取前端传送改过来的item---initial_data# 进行聚合,求出最大值,{'max_price':1200}
        old_price = models.BidRecord.objects.filter(status=1, item_id=item_id).aggregate(max_price=Max('price'))[
            'max_price']
        # 4. 如果比查询出来的小,异常
        if int(attrs) < old_price:
            raise serializers.ValidationError('出价不能比当前价格小!')
        # 3. 如果不是加价幅度,异常

        if int(attrs) % auction_obj.unit != 0:
            raise serializers.ValidationError('请按照加价幅度出价!')
        return attrs

8. 分组

 from django.db.models import Max
        # 每条二级评论的id的最大值
        # <QuerySet [{'reply_id': 7, 'max_id': 15}, {'reply_id': 8, 'max_id': 11}, {'reply_id': 9, 'max_id': 12}]>
        result = models.Comment.objects.filter(is_show=True, is_delete=False, depth=2,
                                               reply_id__in=first_id_list).values('reply_id').annotate(
            max_id=Max('id'))

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑猪去兜风z1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值