0

0

Django REST Framework中高效选择与序列化模型字段的实践

花韻仙語

花韻仙語

发布时间:2025-10-15 08:18:05

|

378人浏览过

|

来源于php中文网

原创

Django REST Framework中高效选择与序列化模型字段的实践

本文旨在解决django rest framework中使用`modelserializer`时,通过`.values()`方法选择特定模型字段导致的`'int' object has no attribute 'pk'`错误。我们将深入探讨为何应避免将`.values()`的输出直接传递给`modelserializer`,并提供一套正确的、结合`select_related()`优化关联查询和`dynamicfieldsmodelserializer`实现动态字段选择的专业解决方案。

在使用Django REST Framework(DRF)构建API时,我们经常需要从数据库中检索特定模型的部分字段并将其序列化为JSON响应。然而,一个常见的误区是试图通过Django ORM的.values()方法来限制查询结果的字段,然后将这些结果直接传递给ModelSerializer。这种做法通常会导致'int' object has no attribute 'pk'这样的错误,因为它违背了ModelSerializer的工作原理。

理解.values()与ModelSerializer的冲突

Django ORM的.values()方法会返回一个字典列表,其中每个字典代表模型实例的一行数据,键是字段名,值是对应的字段值。对于外键(ForeignKey)字段,.values()默认返回的是关联对象的ID(通常是整数)。

ModelSerializer的设计目标是处理Django模型实例。当它接收一个模型实例或一个模型实例的查询集时,它会遍历实例的属性来获取字段值。更重要的是,对于关联字段,ModelSerializer会尝试访问关联模型实例的属性(例如,如果meter_id是一个ForeignKey,它会期望得到一个PowerMeter对象,而不是一个整数ID),或者在内部使用实例的pk属性来解析和序列化关联数据。

当我们将.values()返回的字典列表传递给ModelSerializer时,ModelSerializer会将其视为一个模型实例的集合。但在尝试访问这些“实例”的属性时,它发现它们实际上是字典。当遇到一个本应是模型实例但实际上是整数(例如外键的ID)时,它会尝试访问这个整数的pk属性,从而抛出'int' object has no attribute 'pk'错误。这是一种典型的“原始类型痴迷”(Primitive Obsession)反模式,即过度使用原始类型而非更合适的抽象。

正确的字段选择与序列化方法

要正确地选择特定字段并序列化,我们应该将原始的Django模型实例(或其查询集)传递给ModelSerializer,并让序列化器负责字段的筛选。

1. 传递模型实例而非字典

首先,避免在查询集上使用.values()。直接将模型实例的查询集传递给序列化器。

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import PowerMeter # 假设你的模型定义在models.py中
from .serializers import VAndISerializer # 假设你的序列化器定义在serializers.py中

class VAndIAPIView(APIView): # 建议重命名视图类以避免与模型名称冲突
    def get(self, request):
        # 直接获取模型实例的查询集,不使用 .values()
        queryset = PowerMeter.objects.order_by('-id')[:5]

        # 将模型实例查询集传递给序列化器
        serializer = VAndISerializer(instance=queryset, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

2. 优化关联字段的查询(使用select_related())

如果你的模型中包含外键(例如meter_id可能是一个指向其他表的ForeignKey),并且你希望在序列化时包含关联对象的数据,那么直接传递查询集可能会导致N+1查询问题。为了优化性能,可以使用select_related()来在一次数据库查询中预先加载关联对象。

AITDK
AITDK

免费AI SEO工具,SEO的AI生成器

下载
class VAndIAPIView(APIView):
    def get(self, request):
        # 使用 select_related() 预加载关联的 meter_id 对象
        # 假设 meter_id 是 PowerMeter 模型的一个 ForeignKey 字段
        queryset = PowerMeter.objects.select_related('meter_id').order_by('-id')[:5]
        serializer = VAndISerializer(instance=queryset, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

select_related()适用于一对一和多对一关系(ForeignKey),它通过JOIN语句来减少数据库查询次数。对于多对多或一对多关系,应使用prefetch_related()。

3. 利用DynamicFieldsModelSerializer实现动态字段选择

你提供的DynamicFieldsModelSerializer是一个非常好的模式,它允许你在运行时动态指定需要序列化的字段。要正确使用它,你需要在实例化序列化器时通过fields参数传入一个字段列表。

序列化器定义:

from rest_framework import serializers

class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    一个ModelSerializer,接受一个额外的`fields`参数来控制显示哪些字段。
    """
    def __init__(self, *args, **kwargs):
        # 不将'fields'参数传递给父类
        fields = kwargs.pop('fields', None)

        # 正常实例化父类
        super().__init__(*args, **kwargs)

        if fields is not None:
            # 移除任何未在`fields`参数中指定的字段
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

class VAndISerializer(DynamicFieldsModelSerializer):
    class Meta:
        model = PowerMeter
        # 这里定义了所有可能的字段。DynamicFieldsModelSerializer的__init__方法
        # 会在运行时根据传入的`fields`参数进行过滤。
        fields = ["id", "meter_id", "State", "date", "VII1", "VII2", "VII3", "VII_avg"]

在APIView中使用动态字段选择:

class VAndIAPIView(APIView):
    def get(self, request):
        queryset = PowerMeter.objects.select_related('meter_id').order_by('-id')[:5]

        # 在实例化 VAndISerializer 时传入 fields 参数
        serializer = VAndISerializer(
            instance=queryset,
            many=True,
            fields=[
                'id',
                'meter_id',
                'State',
                'date',
                'VII1',
                'VII2',
                'VII3',
                'VII_avg',
            ],
        )
        return Response(serializer.data, status=status.HTTP_200_OK)

通过这种方式,VAndISerializer会接收到完整的PowerMeter模型实例,然后根据fields参数中指定的字段进行序列化。这样既避免了'int' object has no attribute 'pk'错误,又实现了灵活的字段选择。

注意事项与总结

  • 避免.values()与ModelSerializer混用: 当你的目标是使用ModelSerializer对模型实例进行序列化时,切勿在查询集上使用.values()。.values()适用于直接获取字典数据,而不需要ModelSerializer的复杂逻辑(如字段验证、关联对象处理等)。
  • 性能优化: 对于包含外键的查询,始终考虑使用select_related()或prefetch_related()来优化数据库查询,避免N+1问题。
  • 动态字段选择: DynamicFieldsModelSerializer模式非常强大,它允许API消费者(或内部逻辑)在运行时控制返回哪些字段,这增加了API的灵活性。
  • 命名约定: 在Django中,类视图通常会以APIView或View后缀命名(例如VAndIAPIView),以避免与模型名称或其他模块中的名称冲突,并清晰地表明其用途。

遵循这些最佳实践,可以确保你的Django REST Framework应用在字段选择和序列化方面既高效又健壮。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python Web 框架 Django 深度开发
Python Web 框架 Django 深度开发

本专题系统讲解 Python Django 框架的核心功能与进阶开发技巧,包括 Django 项目结构、数据库模型与迁移、视图与模板渲染、表单与认证管理、RESTful API 开发、Django 中间件与缓存优化、部署与性能调优。通过实战案例,帮助学习者掌握 使用 Django 快速构建功能全面的 Web 应用与全栈开发能力。

166

2026.02.04

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1031

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

613

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

334

2025.08.29

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 10.2万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号