当前位置: 首页 > article >正文

Django一分钟:DRF中序列化器字段source参数的作用

在DRF中,source参数用于在序列化器字段和模型字段之间建立映射关系。本文将从用途和示例出发,帮助你理解其作用。

它的主要用途如下:

  1. 字段重命名
  2. 访问嵌套模型字段
  3. 处理多对多关系

1. 字段重命名

假如我们有如下model:

from django.db import models


class Order(models.Model):
    name = models.CharField(max_length=255)
    
    def __str__(self):
        return self.name

当前端需要order_name字段名来替代name时,我们可以创建如下的序列化器:

from rest_framework import serializers
from api.models import Order


class OrderSerializer(serializers.ModelSerializer):
    order_name = serializers.CharField(source='name')
   
    class Meta:
        model = Order
        fields = ['id', 'order_name']

在上面的示例中,我们使用source字段创建了序列化器字段order_name和模型字段name之间的映射关系:当我们在进行反序列化的时候,order_name将填充到name中,当进行序列化时从查询集中获取的name字段将被用于填充order_name

在下面的测试中,观察vaildate_data和序列化的结果,你可以清晰的看到这一点:

from rest_framework.test import APITestCase
from .serializers import OrderSerializer


class TestSource(APITestCase):
    def test_source(self):
        user = User.objects.create_user(username='用户1', password='test')
        data = {
            'order_name': "测试订单",
            'notes': "用于测试",
        }
        s = OrderSerializer(data=data)
        s.is_valid(raise_exception=True)
        print(s.validated_data)
        # {'notes': '用于测试', 'name': '测试订单'}
        # name由order_name填充
        s.save(created_by=user)
        data = OrderSerializer(Order.objects.get(pk=1)).data
        print(data)
        # {'id': 1, 'order_name': '测试订单'}
        # order_name由name填充

2.访问嵌套(关系)模型字段

现在为刚刚的Order模型添加一个外键:

from django.db import models
from django.contrib.auth.models import User


class Order(models.Model):
    name = models.CharField(max_length=255)
    notes = models.CharField(max_length=255)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)

    def __str__(self):
        return self.name

更改序列化器如下:

from rest_framework import serializers
from api.models import Order


class OrderSerializer(serializers.ModelSerializer):
    created_by = serializers.StringRelatedField(read_only=True)
    order_name = serializers.CharField(source='name')
    create_user_name = serializers.CharField(source='created_by.username', read_only=True)

    class Meta:
        model = Order
        fields = ['id', 'notes', 'created_by', 'create_user_name', 'order_name']

测试一下:

class TestSource(APITestCase):
    def test_source(self):
        user = User.objects.create_user(username='用户1', password='test')
        data = {
            'order_name': "测试订单",
            'notes': "用于测试",
        }
        s = OrderSerializer(data=data)
        s.is_valid(raise_exception=True)
        print(s.validated_data)
        # {'notes': '用于测试', 'name': '测试订单'}
        s.save(created_by=user)
        data = OrderSerializer(Order.objects.get(pk=1)).data
        print(data)
        # {'id': 1, 'notes': '用于测试', 'created_by': '用户1', 'create_user_name': '用户1', 'order_name': '测试订单'}

3.处理多对多关系

模型如下:

from django.db import models


class Blog(models.Model):
    title = models.CharField(max_length=255)
    detail = models.TextField()


class BlogGroup(models.Model):
    name = models.CharField(max_length=255)
    blogs = models.ManyToManyField(Blog, related_name='groups')

    def __str__(self):
        return self.name

假如有这样的需求:我们希望在创建blog的时候可以选择传入一组group_ids,模型中BlogGroup是多对多关系,如果使用重写ModelSerializercreate方法显得很麻烦,此时借助source我们可以很轻松的完成这项工作:

from rest_framework import serializers
from .models import Blog, BlogGroup


class BlogSer(serializers.ModelSerializer):
    groups = serializers.StringRelatedField(read_only=True, many=True)
    group_ids = serializers.PrimaryKeyRelatedField(queryset=BlogGroup.objects.all(), write_only=True, source='groups',
                                                   many=True)

    class Meta:
        model = Blog
        fields = ['id', 'title', 'detail', 'groups', 'group_ids']

测试一下:

from rest_framework.test import APITestCase
from .serializers import BlogSer
from .models import Blog, BlogGroup

class TestSource(APITestCase):
    def test_source_get(self):
        BlogGroup.objects.create(name='测试组1')
        BlogGroup.objects.create(name='测试组2')
        data = {
            'title': 'blog1',
            'detail': 'my blog detail.',
            'group_ids': []
        }
        s = BlogSer(data=data)
        s.is_valid(raise_exception=True)
        print(s.validated_data)
        # {'title': 'blog1', 'detail': 'my blog detail.', 'groups': [<BlogGroup: 测试组1>, <BlogGroup: 测试组2>]}
        s.save()
        print(BlogSer(Blog.objects.get(pk=1)).data)
        # {'id': 1, 'title': 'blog1', 'detail': 'my blog detail.', 'groups': ['测试组1', '测试组2']}

上述的做法缺点也很明显:如果要创建的数据较多,则会导致大量的查询:

>>> data = {
            'title': 'blog1',
            'detail': 'my blog detail.',
            'group_ids': [1, 2, 3, 4, 5, 6, 7, 8, 9]
        }
>>> s = BlogSer(data=data)
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" LIMIT 21; args=(); alias=default
>>> s.is_valid(raise_exception=True)
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 1 LIMIT 21; args=(1,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 2 LIMIT 21; args=(2,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 3 LIMIT 21; args=(3,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 4 LIMIT 21; args=(4,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 5 LIMIT 21; args=(5,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 6 LIMIT 21; args=(6,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 7 LIMIT 21; args=(7,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 8 LIMIT 21; args=(8,); alias=default
# (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" WHERE "api_bloggroup"."id" = 9 LIMIT 21; args=(9,); alias=default
True
>>> (0.000) SELECT "api_bloggroup"."id", "api_bloggroup"."name" FROM "api_bloggroup" LIMIT 21; args=(); alias=default

如果你比较在意性能,或者明确知道此接口可能经常批量的处理大量的数据,还是自己重写createupdate方法为好:

from rest_framework import serializers
from .models import Blog, BlogGroup


class BlogSer(serializers.ModelSerializer):
    groups = serializers.StringRelatedField(read_only=True, many=True)
    group_ids = serializers.ListField(child=serializers.IntegerField(), write_only=True)

    class Meta:
        model = Blog
        fields = ['id', 'title', 'detail', 'groups', 'group_ids']

    def validate_group_ids(self, value):
        existing_ids = set(BlogGroup.objects.filter(id__in=value).values_list('id', flat=True))
        invalid_ids = set(value) - existing_ids
        if invalid_ids:
            raise serializers.ValidationError(f"Invalid group ids: {', '.join(map(str, invalid_ids))}")
        return value

    def create(self, validated_data):
        group_ids = validated_data.pop('group_ids')
        blog = Blog.objects.create(**validated_data)
        blog.groups.set(BlogGroup.objects.filter(id__in=group_ids))
        return blog

    def update(self, instance, validated_data):
        group_ids = validated_data.pop('group_ids', None)
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        if group_ids is not None:
            instance.groups.set(BlogGroup.objects.filter(id__in=group_ids))
        instance.save()
        return instance

http://www.kler.cn/a/354133.html

相关文章:

  • 【学习总结|DAY023】Java高级技术
  • 计算机毕业设计Python+Spark知识图谱酒店推荐系统 酒店价格预测系统 酒店可视化 酒店爬虫 酒店大数据 neo4j知识图谱 深度学习 机器学习
  • GFPS扩展技术原理(七)-音频切换消息流
  • 国产之光--仓颉编程语言的实战案例分析
  • Docker--宿主机执行docker容器的命令
  • Unity 6 中的新增功能
  • H5对接海康硬盘录像机视频简单说明
  • mac git管理(新)
  • 【前端】Matter:物体的高级控制
  • selenium获取cookie的方法
  • T3矩阵看功率
  • 深度探讨Java中String和StringBuilder
  • 【顺序表的模拟实现Java】
  • 自动化测试工具在API测试中的优势是什么?
  • 并查集(Union-Find)
  • Gitlab 完全卸载–亲测可行
  • 泰坦尼克号生存预测CART-基于Python
  • 机器学习笔记20241017
  • python实现屏幕录制,录音录制工具
  • 如何在OceanBase中新增系统变量及应用实践
  • wps图标没有坐标轴标题怎么办?wps表格不能用enter下怎么办?
  • 生成式AI可能成为DevSecOps的圣杯?
  • 适配器设计模式:基础解析与应用实例
  • AI能否颠覆转化医学研究?|行业前沿
  • 2.6.ReactOS系统中从内核中发起系统调用
  • QT 实现按钮多样化