Django学习
date
Sep 14, 2024
slug
django
author
status
Public
tags
python
summary
type
Post
thumbnail
category
updatedAt
Sep 14, 2024 07:50 AM
Serializer
作用: 将模型实例(Model)序列化和反序列化成json之列的形式但一般情况下你不需要把所有字段信息以JSON格式数据返回给用户。序列化器定义了需要对一个模型实例的哪些字段进行序列化/反序列化, 并可对客户端发送过来的数据进行验证和存储。
from .models import Article
from rest_framework import serializers
class ArticleSerializer(serializers.ModelSerializer):
author = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('id','create_date')
Serializer用在哪里?
在通过models( article = Article.objects.get(pk=pk))获取数据库数据后,通过类似serializer = ArticleSerializer(article) , return Response(serializer.data) 完成http返回json body的操作。 类似golang中
type User struct {
ID uint // Standard field for the primary key
Name string // 一个常规字符串字段
}
var user User
// works because destination struct is passed in
db.First(&user)
不过可以看出django中不需要手动去定义User结构,直接用model的定义,然后做一些二次修改完善。但实际上定义的活在model中干了,总归有一个地方要去定义,但是serializers的功能貌似更丰富,可以在不修改model的情况下去更改response结构,当然这也是python动态语言的优势,而golang则需要一开始就定义好需要的字段结构
serializers在django中的使用方式如下:
from apiproject.blog.models import Article
from apiproject.blog.serializers import ArticleSerializer
@api_view(['GET', 'PUT', 'DELETE'])
def article_detail(request, pk):
""" Retrieve,update or delete an article instance。"""try:
article = Article.objects.get(pk=pk)
except Article.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = ArticleSerializer(article)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = ArticleSerializer(article, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
article.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
view的优化
从上面view的写法可以看出,用户需要判断是get还是post,这样一个大型项目的代码量就很大了。可以使用以下几种方式优化写法:
- 使用基础的
APIView
类
- 使用Mixins类和
GenericAPI
类混配
- 使用通用视图
generics.*
类, 比如generics.ListCreateAPIView
- 使用视图集
ViewSet
和ModelViewSet
比如使用APIView
class ArticleList(APIView):"""
List all articles, or create a new article.
"""
def get(self, request, format=None):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
该视图可以自动将不同请求转发到相应处理方法,逻辑上也更清晰。需要调用
as_view()
的方法才能在视图中实现查找指定方法, 比如GET请求执行get方法。 re_path(r'^articles/$', views.ArticleList.as_view()),
re_path(r'^articles/(?P<pk>[0-9]+)$', views.ArticleDetail.as_view()),
用Mixin类和GenericAPI类混配
# blog/views.py
# 使用Generic APIView & Mixins
from rest_framework import mixins
from rest_framework import generics
from apiproject.blog.models import Article
from apiproject.blog.serializers import ArticleSerializer
class ArticleList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
# get 是GenericAPIView转发
def get(self, request, *args, **kwargs):
#self.list 为mixins.ListModelMixin提供
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
GenericAPIView从APIVIew继承,对用户请求进行转发,它还可以通过
queryset
和serializer_class
属性指定需要序列化与反序列化的模型或queryset及所用到的序列化器类。这里的
ListModelMixin
和 CreateModelMixin
类则分别引入了.list()
和 .create()
方法,当用户发送get请求时调用Mixin提供的list()方法,将指定queryset序列化后输出,发送post请求时调用Mixin提供的create()方法,创建新的实例对象。使用Mixin类后,Seralizer貌似被写死了。没关系mixin类提供perform_create方法可以实现对serializer的自定义
def perform_create(self, serializer):
serializer.save(author=self.request.user)
更简化的用法
DRF还提供了一套常用的将 Mixin 类与 GenericAPI类已经组合好了的视图(不用def get ->self.list),开箱即用,可以进一步简化我们的代码,如下:
from apiproject.blog.models import Article
from apiproject.blog.serializers import ArticleSerializer
# generic class-based views
from rest_framework import generics
class ArticleList(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
# 将request.user与author绑定
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class ArticleDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class =ArticleSerializer
generics.ListCreateAPIView
类支持List、Create两种视图功能,分别对应GET和POST请求。generics.RetrieveUpdateDestroyAPIView
支持Retrieve、Update、Destroy操作,其对应方法分别是GET、PUT和DELETE。 屌不屌ViewSet视图集
ArticleList
和ArticleDetail
两个类中queryset
和serializer_class
属性依然存在代码重复。使用视图集可以将两个类视图进一步合并,一次性提供List、Create、Retrieve、Update、Destroy这5种常见操作,这样queryset
和seralizer_class
属性也只需定义一次就好, 这就变成了视图集(viewset)。from apiproject.blog.models import Article
from apiproject.blog.serializers import ArticleSerializer
# generic class-based views
from rest_framework import viewsets
class ArticleViewSet(viewsets.ModelViewSet):
# 用一个视图集替代ArticleList和ArticleDetail两个视图
queryset = Article.objects.all()
serializer_class = ArticleSerializer
# 自行添加,将request.user与author绑定
def perform_create(self, serializer):
serializer.save(author=self.request.user)
这时ArticleViewSet已经包含了List、Create、Retrieve、Update、Destroy这5种操作,如何实现只要其中一种呢:答案是在
urls.py
中指定方法映射即可,如下所示:from django import views
from django.contrib import admin
from django.urls import path, re_path
from rest_framework.urlpatterns import format_suffix_patterns
article_list = views.ArticleViewSet.as_view(
{
'get': 'list',
'post': 'create'
})
article_detail = views.ArticleViewSet.as_view({
'get': 'retrieve', # 只处理get请求,获取单个记录
})
urlpatterns = [
re_path(r'^articles/$', article_list),
re_path(r'^articles/(?P<pk>[0-9]+)$', article_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
SerializerMethodField
看名字就知道大概是什么功能了,其实就是Serializer使用额外的方法来定义字段,而不是通过
author = serializers.ReadOnlyField(source="author.username")
这种方式,method可以封装额外的逻辑,比如定义了cn_status 就可以通过方法get_cn_status获取值, 类似的函数都以get_变量名 命名
from .models import Article
from rest_framework import serializers
class ArticleSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source="author.username")
status = serializers.ReadOnlyField(source="get_status_display")
cn_status = serializers.SerializerMethodField()
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('id', 'author', 'create_date')
def get_cn_status(self, obj):
if obj.status == 'p':
return "已发表"
elif obj.status == 'd':
return "草稿"
else:
return ''
不过需要注意的是
SerializerMethodField
通常用于显示模型中原本不存在的字段,类似可读字段,你不能通过反序列化对其直接进行修改。to_representation
除了指定source和自定义序列化方法,
to_representation()
也允许我们改变序列化的输出内容, 给其添加额外的数据。