序列化组件
from django.http import HttpResponse, JsonResponse from django.core import serializers class BookView(View): # 第一版 json def get(self, request): book_list = Book.objects.values("id", "title", "put_time", "category", "publisher") # querset [{}, {}] book_list = list(book_list) ret = json.dumps(book_list, ensure_ascii=False) return HttpResponse(ret) #第二版 JsonResponse def get(self, request): book_list = Book.objects.values("id", "title", "put_time", "category", "publisher") # querset [{}, {}] book_list = list(book_list) for book in book_list: publisher_obj = Publisher.objects.filter(id=book["publisher"]).first() # 手动给每个book对象添加其对应的外键出版社 book["publisher"] = { "id": publisher_obj.id, "title": publisher_obj.title } return JsonResponse(book_list, safe=False, json_dumps_params={"ensure_ascii": False}) #第三版 serialize 用django的serialize方法 外键依然不能够被序列化 取出来的依然是id def get(self, request): book_list = Book.objects.all() ret = serializers.serialize("json", book_list, ensure_ascii=False) return HttpResponse(ret)
使用rest_framework提供的序列化组件serializers
1,声明一个序列化器. 2,视图类继承APIView或其子类
第四版 使用serializers.Serializer
#自己写的serializers.py文件 from rest_framework import serializers from .models import Book class PublisherSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=32) class AuthorSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=32) book_obj = { "title": "xxx", "category": 1, "publisher": 1, "authors": [1, 2] } # (read_only, write_only) class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # required=False反向序列化的时候不进行验证了, title = serializers.CharField(max_length=32) CHOICES = ((1, "Python"), (2, "Linux"), (3, "go")) category = serializers.CharField(max_length=32, source="get_category_display", read_only=True) #使用source参数后就可以使用orm中的指令了,source后的内容就是对应orm的指令 #read_only表示只进行正向序列化,从视图到前端,处理的是get请求 post_category = serializers.ChoiceField(choices=CHOICES, write_only=True)#write_only表示你只进行反向序列化,从前端到视图,处理的是post请求 put_time = serializers.DateField() publisher = PublisherSerializer(read_only=True) #将出版社的Serializer对象传给publisher字段作为外键 authors = AuthorSerializer(many=True, read_only=True) #同样的将作者的Serializer对象列表传给authors字段,作为多对多的关联,这里因为是多对多的关系,所以需要有一个many=True的参数 publisher_id = serializers.IntegerField(write_only=True) # 这里是用于反序列化时的读取,为了区别正向序列化,和前端约定起另外的字段名 author_list = serializers.ListField(write_only=True) def create(self, validated_data): #validated_data是通过验证后的数据 # 执行ORM的新增数据的操作 book_obj = Book.objects.create(title=validated_data["title"], category=validated_data["post_category"], put_time=validated_data["put_time"], publisher_id=validated_data["publisher_id"]) book_obj.authors.add(*validated_data["author_list"]) print(validated_data["author_list"]) return book_obj def update(self, instance, validated_data): # 使用update的可以提交局部数据,当然也要是json格式的,注意最后面不要加逗号,加了逗号不符合json规范 { "title": "旭哥再次再次升职记"} # 有就更新没有就取默认的 instance.title = validated_data.get("title", instance.title) instance.category = validated_data.get("post_category", instance.category) instance.put_time = validated_data.get("put_time", instance.put_time) instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id) if validated_data.get("author_list"): instance.authors.set(validated_data["author_list"]) instance.save() #更新需要保存instance,因为instance=book_obj是从数据库取出来的数据,当然保存操作也可以在views中进行,直接保存 book_obj.save() return instance
# 视图函数 from rest_framework.views import APIView from rest_framework.response import Response from .models import Book from .serializers import BookSerializer class BookAPIView(APIView): def get(self, request): book_list = Book.objects.all() ser_obj = BookSerializer(book_list, many=True) # return Response("DRF接口测试ok") return Response(ser_obj.data) def post(self, request): book_obj = request.data #request.data接收post请求提交的数据,封装好了,不再使用request.POST.get() print(request.data) ser_obj = BookSerializer(data=book_obj) if ser_obj.is_valid(): print(ser_obj.validated_data) ser_obj.save() # save()方法调用了我们在BookSerializer重写的creat方法 return Response(ser_obj.validated_data) else: return Response(ser_obj.errors) class BookEditView(APIView): def get(self, request, id): book_obj = Book.objects.filter(id=id).first() ser_obj = BookSerializer(book_obj) return Response(ser_obj.data) def patch(self, request, id): book_obj = Book.objects.filter(id=id).first() ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True) #partial=True局部等于true,允许部分验证 if ser_obj.is_valid(): ser_obj.save() # post里的save()方法调用了我们在BookSerializer重写的update方法,这可以在源码中看,点进去看很明显 # book_obj.save() 可以在这里保存,也可以在BookSerializer里进行保存 return Response(ser_obj.validated_data) else: return Response(ser_obj.errors)
第五版 使用serializers.ModelSerializer
# 自己写的serializers.py文件 from rest_framework import serializers from .models import Book class BookSerializer(serializers.ModelSerializer): # 正序和反序列化不同的字段 category_display = serializers.SerializerMethodField(read_only=True) publisher_info = serializers.SerializerMethodField(read_only=True) authors_info = serializers.SerializerMethodField(read_only=True) def get_category_display(self, obj): return obj.get_category_display() def get_publisher_info(self, obj): return {"id": obj.publisher.id, "title": obj.publisher.title} def get_authors_info(self, obj): # 列表生成式循环获得每个作者信息 return [{"id": author.id, "name": author.name} for author in obj.authors.all()] class Meta: model = Book fields = "__all__" extra_kwargs = {"category": {"write_only": True}, "publisher": {"write_only": True}, "authors": {"write_only": True}}
# 视图函数 from .serializers import BookSerializer from rest_framework.views import APIView from rest_framework.response import Response from .models import Book, User class BookView(APIView): # authentication_classes = [MyAuth, ] 这个是认证先不用管 def get(self, request): # 查看所有的图书 queryset = Book.objects.all() ser_obj = BookSerializer(queryset, many=True) return Response(ser_obj.data) def post(self, request): # 新增图书 ser_obj = BookSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.validated_data) class BookEditView(APIView): def get(self, request, pk): book_obj = Book.objects.filter(id=pk).first() ser_obj = BookSerializer(book_obj) return Response(ser_obj.data) def put(self, request, pk): book_obj = Book.objects.filter(id=pk).first() ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.validated_data) else: return Response(ser_obj.errors) def delete(self, request, pk): book_obj = Book.objects.filter(id=pk).first() if not book_obj: return Response({"code": 1001, "error": "删除的数据不存在"}) else: book_obj.delete() return Response("")
# 在序列化组件中进行验证 def my_validate(value): print("my_validate") # 对敏感信息进行过滤 if "马化腾" in value.lower(): raise serializers.ValidationError("不能含有敏感词汇") else: return value class BookSerializer(serializers.ModelSerializer): category_dis= serializers.SerializerMethodField(read_only=True) publisher_info = serializers.SerializerMethodField(read_only=True) author_info = serializers.SerializerMethodField(read_only=True) def get_author_info(self, obj): # 通过obj拿到authors # 构建想要的数据结构返回 authors = obj.authors.all() ret = [] for author in authors: ret.append({ "id": author.id, "name": author.name }) return ret def get_category_dis(self, obj): return obj.get_category_display() def get_publisher_info(self, obj): # 序列化的Book对象 # 通过Book对象找到我们的publisher对象 # 就可以拿到我们想要的字段 # 拼接成自己想要的数据结构 ret = { "id": obj.publisher.id, "title": obj.publisher.title } return ret class Meta: model = Book # fields = ["id", "title", "put_time"] fields = "__all__" # depth = 1 # 自动寻找一层外键的字段,depth=2就寻找两层外键的字段, 当时会让你这些外键关系的字段变成read_only = True 一般不建议用, extra_kwargs = {"category": {"write_only": True},"publisher": {"write_only": True}, "authors":{"write_only": True},"title": {"validators": [my_validate]}}
序列化组件类的相互引用
# 序列化组件间的相互引用 from rest_framework import serializers from course.models import Course,CourseDetail class CourseSerializer(serializers.ModelSerializer): course_type_display = serializers.SerializerMethodField(read_only=True) level_display = serializers.SerializerMethodField(read_only=True) status_display = serializers.SerializerMethodField(read_only=True) category_info = serializers.SerializerMethodField(read_only=True) price_policy = serializers.SerializerMethodField(read_only=True) def get_course_type_display(self, obj): return obj.get_course_type_display() def get_level_display(self, obj): return obj.get_level_display() def get_status_display(self, obj): return obj.get_status_display() def get_category_info(self, obj): return {'id': obj.category.id, 'title': obj.category.title} def get_price_policy(self, obj): ret = [{ 'id': price_obj.id, 'valid_price_display': price_obj.get_valid_period_display(), "price": price_obj.price} for price_obj in obj.price_policy.all()] return ret class Meta: model = Course fields = "__all__" # depth = 1 extra_kwargs = {"course_type": {"write_only": True}, "level": {"write_only": True}, "status": {"write_only": True}, "category": {"write_only": True}} class CourseDetailSerializer(serializers.ModelSerializer): course_info=serializers.SerializerMethodField(read_only=True) def get_course_info(self,obj): # print(obj.course,type(obj.course)) # 这里引用了CourseSerializer,就不用再重复写了 course_queryset= Course.objects.filter(id=obj.id) return CourseSerializer(course_queryset,many=True).data[0] # 这样得到是字典,不然是列表,多对多的关系中应该用列表 class Meta: model=CourseDetail fields = '__all__' extra_kwargs = {"course": {"write_only": True},}
这是model.py表关系
from django.db import models # Create your models here. from django.db import models from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType # Create your models here. __all__ = ["Category", "Course", "CourseDetail", "Teacher", "DegreeCourse", "CourseChapter", "CourseSection", "PricePolicy", "OftenAskedQuestion", "Comment", "Account", "CourseOutline"] class Category(models.Model): """课程分类表""" title = models.CharField(max_length=32, unique=True, verbose_name="课程的分类") def __str__(self): return self.title class Meta: verbose_name = "01-课程分类表" db_table = verbose_name verbose_name_plural = verbose_name class Course(models.Model): """课程表""" title = models.CharField(max_length=128, unique=True, verbose_name="课程的名称") course_img = models.ImageField(upload_to="course/%Y-%m", verbose_name='课程的图片') category = models.ForeignKey(to="Category", verbose_name="课程的分类") COURSE_TYPE_CHOICES = ((0, "付费"), (1, "vip专享"), (2, "学位课程")) course_type = models.SmallIntegerField(choices=COURSE_TYPE_CHOICES) degree_course = models.ForeignKey(to="DegreeCourse", blank=True, null=True, help_text="如果是学位课程,必须关联学位表") brief = models.CharField(verbose_name="课程简介", max_length=1024) level_choices = ((0, '初级'), (1, '中级'), (2, '高级')) level = models.SmallIntegerField(choices=level_choices, default=1) status_choices = ((0, '上线'), (1, '下线'), (2, '预上线')) status = models.SmallIntegerField(choices=status_choices, default=0) pub_date = models.DateField(verbose_name="发布日期", blank=True, null=True) order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排,尽量间隔几个数字") study_num = models.IntegerField(verbose_name="学习人数", help_text="只要有人买课程,订单表加入数据的同时给这个字段+1") # order_details = GenericRelation("OrderDetail", related_query_name="course") # coupon = GenericRelation("Coupon") # 只用于反向查询不生成字段 price_policy = GenericRelation("PricePolicy") often_ask_questions = GenericRelation("OftenAskedQuestion") course_comments = GenericRelation("Comment") def save(self, *args, **kwargs): if self.course_type == 2: if not self.degree_course: raise ValueError("学位课必须关联学位课程表") super(Course, self).save(*args, **kwargs) def __str__(self): return self.title class Meta: verbose_name = "02-课程表" db_table = verbose_name verbose_name_plural = verbose_name class CourseDetail(models.Model): """课程详细表""" course = models.OneToOneField(to="Course") hours = models.IntegerField(verbose_name="课程时长", default=7) # course_slogan = models.CharField(max_length=125, blank=True, null=True, verbose_name="课程口号") video_brief_link = models.CharField(max_length=255, blank=True, null=True) summary = models.TextField(max_length=2048, verbose_name="课程概述") why_study = models.TextField(verbose_name="为什么学习这门课程") what_to_study_brief = models.TextField(verbose_name="我将学到哪些内容") career_improvement = models.TextField(verbose_name="此项目如何有助于我的职业生涯") prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024) recommend_courses = models.ManyToManyField("Course", related_name="recommend_by", blank=True) teachers = models.ManyToManyField("Teacher", verbose_name="课程讲师") def __str__(self): return self.course.title class Meta: verbose_name = "03-课程详细表" db_table = verbose_name verbose_name_plural = verbose_name class Teacher(models.Model): """讲师表""" name = models.CharField(max_length=32, verbose_name="讲师名字") brief = models.TextField(max_length=1024, verbose_name="讲师介绍") def __str__(self): return self.name class Meta: verbose_name = "04-教师表" db_table = verbose_name verbose_name_plural = verbose_name class DegreeCourse(models.Model): """ 字段大体跟课程表相同,哪些不同根据业务逻辑去区分 """ title = models.CharField(max_length=32, verbose_name="学位课程名字") def __str__(self): return self.title class Meta: verbose_name = "05-学位课程表" db_table = verbose_name verbose_name_plural = verbose_name class CourseChapter(models.Model): """课程章节表""" course = models.ForeignKey(to="Course", related_name="course_chapters") chapter = models.SmallIntegerField(default=1, verbose_name="第几章") title = models.CharField(max_length=32, verbose_name="课程章节名称") def __str__(self): return self.title class Meta: verbose_name = "06-课程章节表" db_table = verbose_name verbose_name_plural = verbose_name unique_together = ("course", "chapter") class CourseSection(models.Model): """课时表""" chapter = models.ForeignKey(to="CourseChapter", related_name="course_sections") title = models.CharField(max_length=32, verbose_name="课时") section_order = models.SmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时") section_type_choices = ((0, '文档'), (1, '练习'), (2, '视频')) free_trail = models.BooleanField("是否可试看", default=False) section_type = models.SmallIntegerField(default=2, choices=section_type_choices) section_link = models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link") def course_chapter(self): return self.chapter.chapter def course_name(self): return self.chapter.course.title def __str__(self): return "%s-%s" % (self.chapter, self.title) class Meta: verbose_name = "07-课程课时表" db_table = verbose_name verbose_name_plural = verbose_name unique_together = ('chapter', 'section_link') class PricePolicy(models.Model): """价格策略表""" # conten_type 指定表id content_type = models.ForeignKey(ContentType) # 关联course or degree_course # 关联的表里的对象id object_id = models.PositiveIntegerField() # 关联的表里的那个对象 content_object = GenericForeignKey('content_type', 'object_id') valid_period_choices = ((1, '1天'), (3, '3天'), (7, '1周'), (14, '2周'), (30, '1个月'), (60, '2个月'), (90, '3个月'), (120, '4个月'), (180, '6个月'), (210, '12个月'), (540, '18个月'), (720, '24个月'), (722, '24个月'), (723, '24个月'), ) # 周期 valid_period = models.SmallIntegerField(choices=valid_period_choices) # 价格 price = models.FloatField() def __str__(self): return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price) class Meta: verbose_name = "08-价格策略表" db_table = verbose_name verbose_name_plural = verbose_name unique_together = ("content_type", 'object_id', "valid_period") class OftenAskedQuestion(models.Model): """常见问题""" content_type = models.ForeignKey(ContentType) # 关联course or degree_course object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') question = models.CharField(max_length=255) answer = models.TextField(max_length=1024) def __str__(self): return "%s-%s" % (self.content_object, self.question) class Meta: verbose_name = "09-常见问题表" db_table = verbose_name verbose_name_plural = verbose_name unique_together = ('content_type', 'object_id', 'question') class Comment(models.Model): """通用的评论表""" content_type = models.ForeignKey(ContentType, blank=True, null=True) object_id = models.PositiveIntegerField(blank=True, null=True) content_object = GenericForeignKey('content_type', 'object_id') content = models.TextField(max_length=1024, verbose_name="评论内容") account = models.ForeignKey("Account", verbose_name="会员名") date = models.DateTimeField(auto_now_add=True) def __str__(self): return self.content class Meta: verbose_name = "10-评价表" db_table = verbose_name verbose_name_plural = verbose_name class Account(models.Model): username = models.CharField(max_length=32, verbose_name="用户姓名") # head_img = models.CharField(max_length=256, default='/static/frontend/head_portrait/logo@2x.png', # verbose_name="个人头像") def __str__(self): return self.username class Meta: verbose_name = "11-用户表" db_table = verbose_name verbose_name_plural = verbose_name class CourseOutline(models.Model): """课程大纲""" course_detail = models.ForeignKey(to="CourseDetail", related_name="course_outline") title = models.CharField(max_length=128) order = models.PositiveSmallIntegerField(default=1) # 前端显示顺序 content = models.TextField("内容", max_length=2048) def __str__(self): return "%s" % self.title class Meta: verbose_name = "12-课程大纲表" db_table = verbose_name verbose_name_plural = verbose_name unique_together = ('course_detail', 'title')