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在linux系统下获取系统内存使用情况的方法
May 11 Python
python简单文本处理的方法
Jul 10 Python
实例讲解Python中函数的调用与定义
Mar 14 Python
Python字符串切片操作知识详解
Mar 28 Python
Python正则表达式使用经典实例
Jun 21 Python
Python实现的多叉树寻找最短路径算法示例
Jul 30 Python
python中字符串内置函数的用法总结
Sep 13 Python
python的内存管理和垃圾回收机制详解
May 18 Python
python 代码实现k-means聚类分析的思路(不使用现成聚类库)
Jun 01 Python
Pycharm调试程序技巧小结
Aug 08 Python
浅析python中的del用法
Sep 02 Python
Python 如何利用ffmpeg 处理视频素材
Nov 27 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相关资料
2006/10/09 PHP
PHP+MYSQL的文章管理系统(二)
2006/10/09 PHP
利用discuz实现PHP大文件上传应用实例代码
2008/11/14 PHP
PHP容易忘记的知识点分享
2013/04/30 PHP
php简单浏览目录内容的实现代码
2013/06/07 PHP
PHP URL路由类实例
2013/11/12 PHP
PHP批量生成静态HTML的简单原理和方法
2014/04/20 PHP
php定界符
2014/06/19 PHP
PHP+Javascript实现在线拍照功能实例
2015/07/18 PHP
PHP中一个有趣的preg_replace函数详解
2018/08/15 PHP
php微信公众号开发之图片回复
2018/10/20 PHP
修改jQuery.Autocomplete插件 支持中文输入法 避免TAB、ENTER键失效、导致表单提交
2009/10/11 Javascript
如何使用jQuery来处理图片坏链具体实现步骤
2013/05/02 Javascript
javascript判断机器是否联网的2种方法
2013/08/09 Javascript
jqueyr判断checkbox组的选中(示例代码)
2013/11/08 Javascript
JavaScript实现的in_array函数
2014/08/27 Javascript
JS实现的5级联动Select下拉选择框实例
2015/08/17 Javascript
微信js-sdk地理位置接口用法示例
2016/10/12 Javascript
jQuery得到多个值只能用取Class ,不能用取ID的方法
2016/12/04 Javascript
JS+html5 canvas实现的简单绘制折线图效果示例
2017/03/13 Javascript
nodejs 子进程正确的打开方式
2017/07/03 NodeJs
浅析Javascript中双等号(==)隐性转换机制
2017/10/27 Javascript
详解如何使用PM2将Node.js的集群变得更加容易
2017/11/15 Javascript
Vue自定义指令实现checkbox全选功能的方法
2018/02/28 Javascript
layui中使用jquery控制radio选中事件的示例代码
2018/08/15 jQuery
[04:27]2014DOTA2国际邀请赛 NAVI战队官方纪录片
2014/07/21 DOTA
python学习手册中的python多态示例代码
2014/01/21 Python
Python ORM框架SQLAlchemy学习笔记之数据添加和事务回滚介绍
2014/06/10 Python
简单了解python的break、continue、pass
2019/07/08 Python
python爬虫 爬取超清壁纸代码实例
2019/08/16 Python
CSS3 display知识详解
2015/11/25 HTML / CSS
社区助残日活动总结
2014/08/29 职场文书
2014年公务员退休工资改革方案
2014/10/01 职场文书
出租车拒载检讨书
2015/01/28 职场文书
乐山大佛导游词
2015/02/02 职场文书
会计求职自荐信范文
2015/03/04 职场文书