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中split方法用法分析
Apr 17 Python
PyCharm使用教程之搭建Python开发环境
Jun 07 Python
在python的类中动态添加属性与生成对象
Sep 17 Python
Python有序字典简单实现方法示例
Sep 28 Python
numpy中实现二维数组按照某列、某行排序的方法
Apr 04 Python
selenium+python设置爬虫代理IP的方法
Nov 29 Python
深入了解Python枚举类型的相关知识
Jul 09 Python
Python基于类路径字符串获取静态属性
Mar 12 Python
python对XML文件的操作实现代码
Mar 27 Python
python topk()函数求最大和最小值实例
Apr 02 Python
解决pyqt5异常退出无提示信息的问题
Apr 08 Python
Python如何使用神经网络进行简单文本分类
Feb 25 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
Yii2.0表关联查询实例分析
2016/07/18 PHP
PHP实现QQ、微信和支付宝三合一收款码实例代码
2018/02/19 PHP
PHP以json或xml格式返回请求数据的方法
2018/05/31 PHP
php实现的生成排列算法示例
2019/07/25 PHP
cssQuery()的下载与使用方法
2007/01/12 Javascript
javascript Excel操作知识点
2009/04/24 Javascript
JavaScript 滚轮事件使用说明
2010/03/07 Javascript
JavaScript 产生不重复的随机数三种实现思路
2012/12/13 Javascript
如何让页面在打开时自动刷新一次让图片全部显示
2012/12/17 Javascript
Javascript 颜色渐变效果的实现代码
2013/10/01 Javascript
javascript解三阶幻方(九宫格)
2015/04/22 Javascript
JavaScript中的原型prototype属性使用详解
2015/06/05 Javascript
Javascript技术栈中的四种依赖注入详解
2016/02/23 Javascript
微信小程序 仿美团分类菜单 swiper分类菜单
2017/04/12 Javascript
Vue使用mixins实现压缩图片代码
2018/03/14 Javascript
vue forEach循环数组拿到自己想要的数据方法
2018/09/21 Javascript
解决vue 给window添加和移除resize事件遇到的坑
2020/07/21 Javascript
[56:42]VP vs RNG 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python文档生成工具pydoc使用介绍
2015/06/02 Python
Python用Bottle轻量级框架进行Web开发
2016/06/08 Python
python中的线程threading.Thread()使用详解
2019/12/17 Python
python tqdm 实现滚动条不上下滚动代码(保持一行内滚动)
2020/02/19 Python
什么是python类属性
2020/06/10 Python
Python DES加密实现原理及实例解析
2020/07/17 Python
Python3如何使用tabulate打印数据
2020/09/25 Python
Python pickle模块常用方法代码实例
2020/10/10 Python
HTML5+CSS3模仿优酷视频截图功能示例
2017/01/05 HTML / CSS
舞会礼服和舞会鞋:PromGirl
2019/04/22 全球购物
2019年c语言经典面试题目
2016/08/17 面试题
GWT的应用有哪两种部署模式
2012/12/21 面试题
毕业生的求职信范文分享
2013/12/04 职场文书
理工学院学生自我鉴定
2014/02/23 职场文书
个人工作表现评价材料
2014/09/21 职场文书
2014乡党委副书记党建工作汇报材料
2014/11/02 职场文书
七一建党节慰问信
2015/02/14 职场文书
社区文明创建工作总结2015
2015/04/21 职场文书