DRF之序列化
一. DRF序列化
我们前后端交互一般都选择JSON数据格式,JSON是一个轻量级的数据交互格式。
那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到的数据进行序列化。
接下来我们看下django序列化和rest_framework序列化的对比。
1.Django的序列化方法
表结构:
from django.db import models # Create your models here. __all__ = ["Book", "Publisher", "Author"] class Book(models.Model): title = models.CharField(max_length=32, verbose_name="图书名称") CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux")) category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别") pub_time = models.DateField(verbose_name="图书的出版日期") publisher = models.ForeignKey(to="Publisher", on_delete=models.CASCADE) author = models.ManyToManyField(to="Author") def __str__(self): return self.title class Meta: verbose_name_plural = "01-图书表" db_table = verbose_name_plural class Publisher(models.Model): title = models.CharField(max_length=32, verbose_name="出版社的名称") def __str__(self): return self.title class Meta: verbose_name_plural = "02-出版社表" db_table = verbose_name_plural class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者的姓名") def __str__(self): return self.name class Meta: verbose_name_plural = "03-作者表" db_table = verbose_name_plural
urls:
path('list/', BookView.as_view()),
views:
我们自己处理数据:
class BookView(View): def get(self, request): book_queryset = models.Book.objects.values('id','title','pub_time', 'publisher') book_list = list(book_queryset) ret = [] for book in book_list: publish_id = book['publisher'] publish_obj = models.Publisher.objects.filter(pk=publish_id).first() book['publisher'] = {'id':publish_id, 'title': publish_obj.title} ret.append(book) ret = json.dumps(book_list, ensure_ascii=False, cls=MyJson) return HttpResponse(ret) # return JsonResponse(ret, safe=False, json_dumps_params={'ensure_ascii':False}) 通过JasonResponse就不需要在处理日期显示问题了。 # json.JSONEncoder.default() 解决json不能序列化时间字段的问题 class MyJson(json.JSONEncoder): def default(self, field): if isinstance(field, datetime.datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, datetime.date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field)
用Django给我们提供的序列化方法,可以得到数据,就是数据格式有点复杂:
from django.core import serializers # 能够得到我们要的效果 结构有点复杂
class BookView(View):
def get(self, request):
book_queryset = models.Book.objects.all()
ret = serializers.serialize('json', book_queryset, ensure_ascii=False)
return HttpResponse(ret)
"""
结果:
[{"model": "xuliehua.book", "pk": 1, "fields": {"title": "python基础", "category": 1, "pub_time": "2020-10-14",
"publisher": 1, "author": [1, 2]}}, {"model": "xuliehua.book", "pk": 2, "fields": {"title": "数据库基础", "category": 3,
"pub_time": "2020-10-01", "publisher": 2, "author": [1]}}]
"""
2. DRF的序列化方法
urls:
from django.urls import path from xuliehua.views import BookView, BookEditView urlpatterns = [ path('list/', BookView.as_view()), path('retrieve/<int:pk>/', BookEditView.as_view()) ]
views:
from rest_framework.views import APIView
from rest_framework.response import Response
from xuliehua import models
from xuliehua.serializers import BookSerializer,BookModelSerializer
class BookView(APIView): def get(self, request): book_queryset = models.Book.objects.all() ret = BookModelSerializer(book_queryset, many=True) return Response(ret.data) def post(self, request): ret = BookModelSerializer(data=request.data, many=False) if ret.is_valid(): ret.save() return Response(ret.data) else: return Response(ret.errors) """
通过postman提交的数据格式
{ "title": "数据库基础1", "w_category": "1", "pub_time": "2020-10-01", "publisher_id": "1", "author_list": [ "1", "2" ] } """ class BookEditView(APIView): def get(self, request, pk): book_obj = models.Book.objects.filter(pk=pk).first() ret = BookModelSerializer(book_obj, many=False) return Response(ret.data) def put(self, request, pk): book_obj = models.Book.objects.filter(pk=pk).first() ret = BookModelSerializer(book_obj, data=request.data, partial=True) if ret.is_valid(): ret.save() return Response(ret.data) else: return Response(ret.errors)
自己写的序列化器,继承于 serializers.ModelSerializer 或者 serializers.Serializer,这两个就有点像form和modelform
先看serializers.Serializer:
# 自定义校验器
def my_validator(value): if '敏感' in value.lower(): raise serializers.ValidationError('不能含有敏感信息') return value class PublishSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=32) class AuthorSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=32) class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) title = serializers.CharField(max_length=32, validators=[my_validator,]) CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux")) category = serializers.CharField(source='get_category_display', read_only=True) w_category = serializers.ChoiceField(choices=CHOICES, write_only=True) pub_time = serializers.DateField() publisher = PublishSerializer(read_only=True) publisher_id = serializers.IntegerField(write_only=True) author = AuthorSerializer(many=True, read_only=True) author_list = serializers.ListField(write_only=True) def create(self, validated_data): print(validated_data) book = models.Book.objects.create(title=validated_data['title'], category=validated_data['w_category'],pub_time= validated_data['pub_time'], publisher_id=validated_data['publisher_id']) book.author.add(*validated_data['author_list']) return book def update(self, instance, validated_data): instance.title = validated_data.get("title", instance.title) instance.category = validated_data.get("w_category", instance.category) instance.pub_time = validated_data.get("pub_time", instance.pub_time) instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id) if validated_data.get("author_list"): instance.author.set(validated_data["author_list"]) instance.save() return instance # 局部狗子 def validate_title(self, value): if 'python' not in value.lower(): raise serializers.ValidationError('标题必须含有python') return value # 全局钩子 def validate(self, attrs): if attrs.get('w_category') == 1 and attrs.get('publisher_id') == 2: return attrs else: raise serializers.ValidationError('分类以及标题不符合要求')
serializers.ModelSerializer:
class BookModelSerializer(serializers.ModelSerializer): category_display = serializers.CharField(source='get_category_display',read_only=True) publisher_info = serializers.SerializerMethodField(read_only=True) authors = serializers.SerializerMethodField(read_only=True) def get_publisher_info(self, obj): publish_obj = obj.publisher return {'id':publish_obj.pk, 'title': publish_obj.title} def get_authors(self, obj): author_queryset = obj.author.all() return [{'id': author.pk, 'name': author.name} for author in author_queryset] def validate_title(self, value): if 'python' not in value.lower(): raise serializers.ValidationError('标题必须含有python') return value class Meta: model = models.Book fields = '__all__' extra_kwargs = {'category':{'write_only':True}, 'publisher': {'write_only': True}, 'author': {'write_only':True}}
views中,就可以通过这两个序列化器,选择一个进行数据的序列化。
通过postman的GET方法访问 http://127.0.0.1:8000/books/retrieve/1/:
{ "id": 1, "category_display": "Go", "publisher_info": { "id": 1, "title": "北京出版社" }, "authors": [ { "id": 1, "name": "alex" } ], "title": "python全栈开发", "pub_time": "2020-10-14" }
通过postman的PUT方法访问 http://127.0.0.1:8000/books/retrieve/1/:
提交的json数据:
{ "title":"python全栈开发", "category": 2, "publisher":1, "author": [1,2] }
返回的数据:
{ "id": 1, "category_display": "Go", "publisher_info": { "id": 1, "title": "北京出版社" }, "authors": [ { "id": 2, "name": "wupeiqi" } ], "title": "python全栈开发", "pub_time": "2020-10-14" }