Django模型序列化返回自然主键值示例代码


Posted in Python onJune 12, 2019

场景

在设计表结构时,难免需要建立一些外键关联。例如这样两个模型:

from django.db import models

class Person(models.Model):
 username = models.CharField(max_length=100)
 birthdate = models.DateField()

class Book(models.Model):
 name = models.CharField(max_length=100)
 author = models.ForeignKey(Person, on_delete=models.CASCADE)

表 Book 的字段 author 是表 Person 的外键,我们试用 Django 原生的 Serializer 模块来对 Book 实例序列化:

from django.core import serializers
book_json = serializers.serialize("json", Book.objects.get(pk=1))

JSON 序列化结果如下:

{
 "pk": 1,
 "model": "store.book",
 "fields": {
  "name": "Mostly Harmless",
  "author": 42
 }
}

这个 "author": 42 对用户来说相当于未知,我们需要的是 Person 表中主键为 42 的用户姓名,即 username 的值。

解决方案

在 Django 官方文档的「序列化」一节中提到了用 models.Manager 处理的方案;在搜索解决方案过程中,也接触到 Django-REST-Framework(DRF) ,了解到 DRF 中的 Serializer 模块也能解决这类问题。那我们不妨对比一下两种解决方案。

方案一:models.Manager

根据文档,要返回自然主键,我们需要定义一个模型管理器,创建一个 get_by_natural_key 方法,如下:
from django.db import models

from django.db import models

class PersonManager(models.Manager):
 def get_by_natural_key(self, username):
  return self.get(username=username)

class Person(models.Model):
 username = models.CharField(max_length=100)
 birthdate = models.DateField()
 objects = PersonManager()

然后再次序列化 Book 实例:

from django.core import serializers
book_json = serializers.serialize("json", Book.objects.get(pk=1), use_natural_foreign_keys=True)

得到新的结果如下:

{
    "pk": 1,
    "model": "store.book",
    "fields": {
        "name": "Mostly Harmless",
        "author": ["DouglasAdams"]
    }
}

如果需要对其他应用的数据模型做修改,例如使用了 django.auth.User(默认认证后端)作为 Book  的外键,要想不修改 User 模型又使用新的模型管理器,可以使用代理模式完成:

from django.db import models

class NewManager(models.Manager):
 # ...
 pass

class MyPerson(Person):
 objects = NewManager()

 class Meta:
  proxy = True

总的来说,这个方案可以完美解决我所遇到的问题,代码量稍微大一些,但是也更灵活。

方案二:DRF 的 Serializer

下面我们试试用 Django-REST-Framework 的序列化模块:

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
 author_name = serializers.CharField(source='author.username')

 class Meta:
  model = Book
  fields = '__all__'

这段代码表示,在序列化 Book 实例时,添加一个新的属性 author_name,该值的来源为 source 参数定义的外键 author 实例的自然主键 username。

然后是执行序列化的过程:

queryset = Book.objects.get(pk=1)
BookSerializer(instance=queryset)

序列化结果:

{
    "id": 1,
    "name": "Mostly Harmless",
    "author": 42,
    "author_name": "DouglasAdams"
}

当然,序列化一批 Book 实例也是可以的:

queryset = Book.objects.all()
BookSerializer(instance=queryset, many=True)

序列化结果:

[
    {
        "id": 1,
        "name": "Mostly Harmless",
        "author": 42,
        "author_name": "DouglasAdams"
    },
    {
        "id": 2,
        "name": "Harry Potter",
        "author": 2,
        "author_name": "JKRowling"
    }
]

可以看到,使用 DRF 的序列化模块返回自然主键,不仅代码清晰改动少,而且效果也很不错,序列化数据少了一个层级,对前端也是十分友好的。

方案三:手动修改序列化后的外键

当然,还有一种最傻也是最容易想到的办法,就是在序列化后,手动修改 JSON 串中对应的外键值为自然主键值。

这种做法可以得到和方案一一样的效果,但是遇到查询结果为列表时我们需要遍历替换。同时试想一下,如果我们在每个视图中都这么处理,那代码会变得十分糟糕。不建议使用该方案。

总结

对比两种序列化方案,我个人更偏向于 DRF 优雅的处理方式。当然,除了序列化,DRF 还有很多功能,例如分页等,强烈建议学习学习。

当然,可能不存在最好的最好的技术方案,遇到这类问题选择最合适自己的就好。也可能还有更多的方法可以解决标题的问题,也欢迎留言探讨!

参考

  • docs.djangoproject.com/zh-hans/2.2…
  • docs.djangoproject.com/en/2.2/topi…
  • www.django-rest-framework.org/api-guide/f…

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
用Python脚本生成Android SALT扰码的方法
Sep 18 Python
python 删除大文件中的某一行(最有效率的方法)
Aug 19 Python
Python插件virtualenv搭建虚拟环境
Nov 20 Python
Python基于递归和非递归算法求两个数最大公约数、最小公倍数示例
May 21 Python
详解Numpy中的广播原则/机制
Sep 20 Python
python实现转圈打印矩阵
Mar 02 Python
浅析Python与Mongodb数据库之间的操作方法
Jul 01 Python
Python+Kepler.gl轻松制作酷炫路径动画的实现示例
Jun 02 Python
Python xlrd模块导入过程及常用操作
Jun 10 Python
python实现b站直播自动发送弹幕功能
Feb 20 Python
python获取淘宝服务器时间的代码示例
Apr 22 Python
Python编程根据字典列表相同键的值进行合并
Oct 05 Python
pandas实现将dataframe满足某一条件的值选出
Jun 12 #Python
python 列表输出重复值以及对应的角标方法
Jun 11 #Python
使用python list 查找所有匹配元素的位置实例
Jun 11 #Python
python找出一个列表中相同元素的多个索引实例
Jun 11 #Python
三步实现Django Paginator分页的方法
Jun 11 #Python
python-tkinter之按钮的使用,开关方法
Jun 11 #Python
人工神经网络算法知识点总结
Jun 11 #Python
You might like
PHP 裁剪图片成固定大小代码方法
2009/09/09 PHP
PHP目录函数实现创建、读取目录教程实例
2011/01/13 PHP
PHP中VC6、VC9、TS、NTS版本的区别与用法详解
2013/10/26 PHP
thinkphp模板继承实例简述
2014/11/26 PHP
php常用字符串String函数实例总结【转换,替换,计算,截取,加密】
2016/12/07 PHP
php获取给定日期相差天数的方法分析
2017/02/20 PHP
JavaScript 继承详解(一)
2009/07/13 Javascript
AngularJS表单编辑提交功能实例
2015/02/13 Javascript
javascript作用域问题实例分析
2015/07/13 Javascript
JavaScript的jQuery库中ready方法的学习教程
2015/08/14 Javascript
jQuery实现的超简单点赞效果实例分析
2015/12/31 Javascript
浅析Javascript中bind()方法的使用与实现
2016/05/30 Javascript
JavaScript实现输入框与清空按钮联动效果
2016/09/09 Javascript
用v-html解决Vue.js渲染中html标签不被解析的问题
2016/12/14 Javascript
MUI  Scroll插件的使用详解
2017/04/13 Javascript
微信小程序点击控件修改样式实例详解
2017/07/07 Javascript
vue2.0.js的多级联动选择器实现方法
2018/02/09 Javascript
详解extract-text-webpack-plugin 的使用及安装
2018/06/12 Javascript
layui 表单标签的校验方法
2019/09/04 Javascript
[02:08]什么藏在DOTA2 TI9“小紫本”里?斧王历险记告诉你!
2019/05/17 DOTA
python实现猜数字游戏(无重复数字)示例分享
2014/03/29 Python
Python实现多行注释的另类方法
2014/08/22 Python
Python用Pillow(PIL)进行简单的图像操作方法
2017/07/07 Python
Python利用公共键如何对字典列表进行排序详解
2018/05/19 Python
Python骚操作之动态定义函数
2019/03/26 Python
Python爬虫逆向分析某云音乐加密参数的实例分析
2020/12/04 Python
使用Django的JsonResponse返回数据的实现
2021/01/15 Python
美国一站式电动和手动工具商店:International Tool
2020/11/26 全球购物
2015年元旦文艺汇演主持词
2014/03/26 职场文书
远程培训的心得体会
2014/09/01 职场文书
教师年度个人总结
2015/02/11 职场文书
幼儿园教师师德师风承诺书
2015/04/28 职场文书
舌尖上的中国观后感
2015/06/02 职场文书
篮球赛新闻稿
2015/07/17 职场文书
党风廉政建设心得体会(2016最新版)
2016/01/22 职场文书
vue实现拖拽交换位置
2022/04/07 Vue.js