1. 主页
  2. DRF实战教程
  3. DRF实战之序列化高级

DRF实战之序列化高级

模型类

from django.db import models


# Create your models here.

class Category(models.Model):
    name = models.CharField(verbose_name='名字', max_length=100)

    def __str__(self):
        return self.name


class Article(models.Model):
    title = models.CharField(verbose_name='标题', max_length=100)
    vnum = models.IntegerField(verbose_name='浏览量')
    content = models.TextField(verbose_name='内容')
    category = models.ForeignKey(to=Category, on_delete=models.CASCADE, related_name='articles') # related_name 反向查找

    def __str__(self):
        return self.title

路由

from django.contrib import admin
from django.urls import path, include
from . import views

urlpatterns = [
    path('articles/', views.article_list),  # 获取或创建
    path('articles/<int:id>/', views.article_detail),  # 查找、更新、删除
]

视图

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from .models import Article
from .serializers import ArticleSerializer


class JSONResponse(HttpResponse):
    """
    An HttpResponse that renders its content into JSON.
    """

    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)


@csrf_exempt
def article_list(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return JSONResponse(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = ArticleSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)


@csrf_exempt
def article_detail(request, id):
    try:
        article = Article.objects.get(id=id)
    except Article.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return JSONResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = ArticleSerializer(article, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        article.delete()
        return HttpResponse(status=204)

StringRelatedField

StringRelatedField 将返回一个对应关系 model 的 unicode() 方法的字符串。

文章—>分类

from rest_framework import serializers
from .models import Article


class ArticleSerializer(serializers.ModelSerializer):
    category = serializers.StringRelatedField()

    class Meta:
        model = Article
        # fields = ('id', 'title', 'vnum', 'content', 'category')
        fields = '__all__'
DRF实战之序列化高级

分类—->文章

路由
urlpatterns = [
    path('categorys/', views.category_list),  # 查找、更新、删除
    path('categorys/<int:id>/', views.category_detail),  # 查找、更新、删除
]

视图

@csrf_exempt
def category_list(request):
    if request.method == 'GET':
        categorys = Category.objects.all()
        serializer = CategorySerializer(categorys, many=True)
        return JSONResponse(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = CategorySerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)


@csrf_exempt
def category_detail(request, id):
    try:
        category = Category.objects.get(id=id)
    except Category.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = CategorySerializer(category)
        return JSONResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = CategorySerializer(category, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        category.delete()
        return HttpResponse(status=204)

序列化

class CategorySerializer(serializers.ModelSerializer):
    articles = serializers.StringRelatedField(many=True)

    class Meta:
        model = Category
        fields = '__all__'
DRF实战之序列化高级

PrimaryKeyRelatedField

使用 PrimaryKeyRelatedField 将返回一个对应关系 model 的主键。

参数:

  • queryset 用于在验证字段输入时模型实例查找。 关系必须明确设置 queryset,或设置 read_only = True
  • many 如果是对应多个的关系,就设置为 True
  • allow_null 如果设置为 True,则该字段将接受 None 的值或为空的关系的空字符串。默认为 False
  • pk_field 设置为一个字段以控制主键值的序列化/反序列化。例如,pk_field = UUIDField(format =’hex’) 将UUID主键序列化为紧凑的十六进制表示。

序列化

class CategorySerializer(serializers.ModelSerializer):
    articles = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Category
        fields = '__all__'
DRF实战之序列化高级

HyperlinkedRelatedField

使用 HyperlinkedRelatedField 将返回一个超链接,该链接指向对应关系 model 的详细数据,view-name 是必选参数,为对应的视图生成超链接。

参数:

  • view_name 用作关系目标的视图名称。如果使用的是标准路由器类,那么它的格式为 <modelname>-detail 的字符串
  • queryset 验证字段输入时用于模型实例查询的查询器。关系必须明确设置 queryset,或设置 read_only = True
  • many 如果应用于多对多关系,则应将此参数设置为 True
  • allow_null 如果设置为 True,则该字段将接受 None 的值或为空的关系的空字符串。默认为 False
  • lookup_field 应该用于查找的目标上的字段。应该对应于引用视图上的 URL 关键字参数。默认值为 pk
  • lookup_url_kwarg 与查找字段对应的 URL conf 中定义的关键字参数的名称。默认使用与 lookup_field 相同的值
  • format 如果使用 format 后缀,超链接字段将对目标使用相同的 format 后缀,除非使用 format 参数进行覆盖。
class CategorySerializer(serializers.ModelSerializer):
    articles = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='article_detail',  # 路由的别名
        lookup_field='id',  # 数据库字段的名字
        lookup_url_kwarg="id"  # 路由中参数的名字
    )
​
    class Meta:
        model = Category
        fields = '__all_

错误解决

`HyperlinkedRelatedField` requires the request in the serializer context. Add `context={'request': request}` when instantiating the serializer.

在所有使用CategorySerializerde的地方加上context={‘request’: request}

 serializer = CategorySerializer(categorys, many=True, context={'request': request})
DRF实战之序列化高级

SlugRelatedField

使用 SlugRelatedField 将返回一个指定对应关系 model 中的字段,需要参数 slug_field 中指定字段名称。

参数:

  • slug_field 应该用于表示目标的字段。这应该是唯一标识任何给定实例的字段。例如 username 。这是必选参数
  • queryset 验证字段输入时用于模型实例查询的查询器。 关系必须明确设置 queryset,或设置 read_only = True
  • many 如果应用于多对多关系,则应将此参数设置为 True
  • allow_null 如果设置为 True,则该字段将接受 None 的值或为空的关系的空字符串。默认为 False
class CategorySerializer(serializers.ModelSerializer):
    articles = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
    )

    class Meta:
        model = Category
        fields = '__all__'
DRF实战之序列化高级

HyperlinkedIdentityField

使用 HyperlinkedIdentityField 将返回指定 view-name 的超链接的字段。

参数:

  • view_name 应该用作关系目标的视图名称。如果您使用的是标准路由器类,则它将是格式为 <model_name>-detail的字符串。必选参数
  • lookup_field 应该用于查找的目标上的字段。应该对应于引用视图上的 URL 关键字参数。默认值为 pk
  • lookup_url_kwarg 与查找字段对应的 URL conf 中定义的关键字参数的名称。默认使用与 lookup_field 相同的值
  • format 如果使用 format 后缀,超链接字段将对目标使用相同的 format 后缀,除非使用 format 参数进行覆盖
class ArticleSerializer(serializers.ModelSerializer):
    category = serializers.HyperlinkedIdentityField(view_name="app03:category-detail", lookup_field='id')

    class Meta:
        model = Article
        fields = '__all__'


class CategorySerializer(serializers.ModelSerializer):
    articles = serializers.HyperlinkedIdentityField(view_name="app03:article-detail", lookup_field='id', many=True)

    class Meta:
        model = Category
        fields = '__all__'

HyperlinkedModelSerializer

HyperlinkedModelSerializer 类与 ModelSerializer 类相似,只不过它使用超链接来表示关系而不是主键。

class ArticleSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Article
        fields = '__all__'

        extra_kwargs = {
            'url': {'view_name': 'app04:article-detail', 'lookup_field': 'id'},
            'category': {'view_name': 'app04:category-detail', 'lookup_field': 'id'},
        }


class CategorySerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Category
        fields = ('id', 'name', 'articles')

        extra_kwargs = {
            'url': {'view_name': 'app04:category-detail', 'lookup_field': 'id'},
            'articles': {'view_name': 'app04:article-detail', 'lookup_field': 'id'},
        }
        
        
'''

使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数---字典格式

extra_kwargs = {

			'vnum': {'min_value': 0, 'required': True},
}
'''
DRF实战之序列化高级

嵌套序列化关系模型

顾名思义就是序列化里面有另一个序列化类

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = '__all__'


class CategorySerializer(serializers.ModelSerializer):
    articles = ArticleSerializer(many=True)

    class Meta:
        model = Category
        fields = '__all__'
DRF实战之序列化高级

depth

这个字段可以用来深度遍历

#注意是ModelSerializer
class CategorySerializer(serializers.ModelSerializer):
    articles = ArticleSerializer(many=True)

    class Meta:
        model = Category
        fields = '__all__'
        depth = 2
DRF实战之序列化高级

SerializerMethodField

通过这个属性我们可以自定义一些属性。

class CategorySerializer(serializers.ModelSerializer):
    articles = ArticleSerializer(many=True)
    count = serializers.SerializerMethodField()

    class Meta:
        model = Category
        fields = '__all__'
        depth = 2

    def get_count(self, obj):
        return Category.objects.count()
DRF实战之序列化高级

source

序列化的时候指定数据源

class ArticleSerializer(serializers.ModelSerializer):
    category = serializers.CharField(source='category.name')

    class Meta:
        model = Article
        fields = '__all__'


class MyCharField(serializers.CharField):
    def to_representation(self, value):
        data_list = []
        for row in value:
            data_list.append({'title': row.title, 'content': row.content})
        return data_list


class CategorySerializer(serializers.ModelSerializer):
    arts = MyCharField(source='articles.all')
    # arts = serializers.CharField(source='articles.all')

    class Meta:
        model = Category
        fields = ('id', 'name', 'arts')
DRF实战之序列化高级

利用source实现可读可写

from collections import OrderedDict


class ChoiceDisplayField(serializers.Field):
    """Custom ChoiceField serializer field."""

    def __init__(self, choices, **kwargs):
        """init."""
        self._choices = OrderedDict(choices)
        super(ChoiceDisplayField, self).__init__(**kwargs)

    # 返回可读性良好的字符串而不是 1,-1 这样的数字
    def to_representation(self, obj):
        """Used while retrieving value for the field."""
        return self._choices[obj]

    def to_internal_value(self, data):
        """Used while storing value for the field."""
        for i in self._choices:
            # 这样无论用户POST上来但是CHOICES的 Key 还是Value 都能被接受
            if i == data or self._choices[i] == data:
                return i
        raise serializers.ValidationError("Acceptable values are {0}.".format(list(self._choices.values())))

to_representation方法

序列化器的每个字段实际都是由该字段类型的to_representation方法决定格式的,可以通过重写该方法来决定格式。

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = '__all__'

    def to_representation(self, instance):
        representation = super(ArticleSerializer, self).to_representation(instance)
        representation['category'] = CategorySerializer(instance.category).data
        representation['tags'] = TagSerializer(instance.tags, many=True).data
        return representation

我们要如何帮助您?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注