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 相关文章推荐
pyqt4教程之实现windows窗口小示例分享
Mar 07 Python
Python中使用haystack实现django全文检索搜索引擎功能
Aug 26 Python
Python序列化基础知识(json/pickle)
Oct 19 Python
python的Tqdm模块的使用
Jan 10 Python
Python操作mongodb的9个步骤
Jun 04 Python
Python 多线程不加锁分块读取文件的方法
Dec 11 Python
使用PyQtGraph绘制精美的股票行情K线图的示例代码
Mar 14 Python
PyQt4编程之让状态栏显示信息的方法
Jun 18 Python
Django 静态文件配置过程详解
Jul 23 Python
Django文件存储 自己定制存储系统解析
Aug 02 Python
Python 字符串类型列表转换成真正列表类型过程解析
Aug 26 Python
python3 sorted 如何实现自定义排序标准
Mar 12 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实现生成透明背景的PNG缩略图函数分享
2014/07/08 PHP
ThinkPHP的SAE开发相关注意事项详解
2016/10/09 PHP
PHP使用 Pear 进行安装和卸载包的方法详解
2019/07/08 PHP
js 覆盖和重载 函数
2009/09/25 Javascript
Javascript 面向对象 重载
2010/05/13 Javascript
javascript 函数及作用域总结介绍
2013/11/12 Javascript
javascript模拟实现C# String.format函数功能代码
2013/11/25 Javascript
指定区域的图片自动按比例缩小的js代码(防止页面被图片撑破)
2014/02/21 Javascript
jquery ajax 如何向jsp提交表单数据
2015/08/23 Javascript
js 获取站点应用名的简单实例
2016/08/18 Javascript
jQuery实现单击按钮遮罩弹出对话框效果(2)
2017/02/20 Javascript
jQuery.form.js的使用详解
2017/06/14 jQuery
基于Bootstrap的标签页组件及bootstrap-tab使用说明
2017/07/25 Javascript
animate.css在vue项目中的使用教程
2018/08/05 Javascript
vue-cli项目中使用echarts图表实例
2018/10/22 Javascript
微信小程序实现获取小程序码和二维码java接口开发
2019/03/29 Javascript
Vue触发input选取文件点击事件操作
2020/08/07 Javascript
jdk1.8+vue elementui实现多级菜单功能
2020/09/24 Javascript
[02:16]DOTA2英雄基础教程 干扰者
2014/01/15 DOTA
python fabric使用笔记
2015/05/09 Python
PyTorch线性回归和逻辑回归实战示例
2018/05/22 Python
解决tensorflow模型参数保存和加载的问题
2018/07/26 Python
python2与python3共存问题的解决方法
2018/09/18 Python
python3下载抖音视频的完整代码
2019/06/05 Python
python卸载后再次安装遇到的问题解决
2019/07/10 Python
Django model update的多种用法介绍
2020/03/28 Python
Python常用模块sys,os,time,random功能与用法实例分析
2020/01/07 Python
parser.add_argument中的action使用
2020/04/20 Python
一个入门级python爬虫教程详解
2021/01/27 Python
HTML5 中新的全局属性(整理)
2013/07/31 HTML / CSS
日本最大美瞳直送网:Morecontact(中文)
2019/04/03 全球购物
2013年保送生自荐信格式
2013/11/20 职场文书
市委常委班子党的群众路线教育实践活动整改措施
2014/10/02 职场文书
Java多条件判断场景中规则执行器的设计
2021/06/26 Java/Android
mysql聚集索引、辅助索引、覆盖索引、联合索引的使用
2022/02/12 MySQL
Python使用pyecharts控件绘制图表
2022/06/05 Python