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字符串处理之count()方法的使用
May 18 Python
django通过ajax发起请求返回JSON格式数据的方法
Jun 04 Python
python中urllib.unquote乱码的原因与解决方法
Apr 24 Python
python处理multipart/form-data的请求方法
Dec 26 Python
python中使用 xlwt 操作excel的常见方法与问题
Jan 13 Python
python使用matplotlib绘制雷达图
Oct 18 Python
Pycharm创建项目时如何自动添加头部信息
Nov 14 Python
妙用itchat! python实现久坐提醒功能
Nov 25 Python
如何写python的配置文件
Jun 07 Python
python3实现飞机大战
Nov 29 Python
pycharm远程连接服务器并配置python interpreter的方法
Dec 23 Python
python 使用openpyxl读取excel数据
Feb 18 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中使用Oracle数据库(3)
2006/10/09 PHP
php通过修改header强制图片下载的方法
2015/03/24 PHP
PHP设计模式之装饰者模式代码实例
2015/05/11 PHP
网页中的图片的处理方法与代码
2009/11/26 Javascript
JS下高效拼装字符串的几种方法比较与测试代码
2010/04/15 Javascript
为radio类型的INPUT添加客户端脚本(附加实现JS来禁用onClick事件思路代码)
2010/11/11 Javascript
仿百度输入框智能提示的js代码
2013/08/22 Javascript
JS控制图片翻转示例代码(兼容firefox,ie,chrome)
2013/12/19 Javascript
sliderToggle在写jquery的计时器setTimeouter中不生效
2014/05/26 Javascript
基于jQuery实现网页进度显示插件
2015/03/04 Javascript
JQuery选择器、过滤器大整理
2015/05/26 Javascript
jQuery实现网页顶部固定导航效果代码
2015/12/24 Javascript
WebGL利用FBO完成立方体贴图效果完整实例(附demo源码下载)
2016/01/26 Javascript
微信小程序 PHP后端form表单提交实例详解
2017/01/12 Javascript
原生js实现可拖拽效果
2017/02/28 Javascript
VueJs组件之父子通讯的方式
2018/05/06 Javascript
Vue.js 无限滚动列表性能优化方案
2019/12/02 Javascript
jQuery实现聊天对话框
2020/02/08 jQuery
Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)
2020/03/06 Javascript
[02:07]DOTA2新英雄展现中国元素,完美“圣典”亮相央视
2016/12/19 DOTA
[01:19]DOTA2城市挑战赛报名开始 开启你的城市传奇
2018/03/23 DOTA
从零学python系列之数据处理编程实例(一)
2014/05/22 Python
Python中的字典与成员运算符初步探究
2015/10/13 Python
1 行 Python 代码快速实现 FTP 服务器
2018/01/25 Python
利用Python进行数据可视化常见的9种方法!超实用!
2018/07/11 Python
在flask中使用python-dotenv+flask-cli自定义命令(推荐)
2020/01/05 Python
python第三方库学习笔记
2020/02/07 Python
Selenium元素定位的30种方式(史上最全)
2020/05/11 Python
django 利用Q对象与F对象进行查询的实现
2020/05/15 Python
opencv 图像加法与图像融合的实现代码
2020/07/08 Python
荷兰睡眠专家:Beter Bed
2020/11/23 全球购物
文明家庭先进事迹材料
2014/05/14 职场文书
2015年银行个人工作总结
2015/05/14 职场文书
运输公司工作总结
2015/08/11 职场文书
2016抗战胜利71周年红领巾广播稿
2015/12/18 职场文书
详解Android中的TimePickerView(时间选择器)的用法
2022/04/30 Java/Android