Django模型序列化返回自然主键值示例代码
场景
在设计表结构时,难免需要建立一些外键关联。例如这样两个模型:
fromdjango.dbimportmodels classPerson(models.Model): username=models.CharField(max_length=100) birthdate=models.DateField() classBook(models.Model): name=models.CharField(max_length=100) author=models.ForeignKey(Person,on_delete=models.CASCADE)
表Book的字段author是表Person的外键,我们试用Django原生的Serializer模块来对Book实例序列化:
fromdjango.coreimportserializers book_json=serializers.serialize("json",Book.objects.get(pk=1))
JSON序列化结果如下:
{ "pk":1, "model":"store.book", "fields":{ "name":"MostlyHarmless", "author":42 } }
这个"author":42对用户来说相当于未知,我们需要的是Person表中主键为42的用户姓名,即username的值。
解决方案
在Django官方文档的「序列化」一节中提到了用models.Manager处理的方案;在搜索解决方案过程中,也接触到Django-REST-Framework(DRF),了解到DRF中的Serializer模块也能解决这类问题。那我们不妨对比一下两种解决方案。
方案一:models.Manager
根据文档,要返回自然主键,我们需要定义一个模型管理器,创建一个get_by_natural_key方法,如下:
fromdjango.dbimportmodels
fromdjango.dbimportmodels classPersonManager(models.Manager): defget_by_natural_key(self,username): returnself.get(username=username) classPerson(models.Model): username=models.CharField(max_length=100) birthdate=models.DateField() objects=PersonManager()
然后再次序列化Book实例:
fromdjango.coreimportserializers book_json=serializers.serialize("json",Book.objects.get(pk=1),use_natural_foreign_keys=True)
得到新的结果如下:
{
"pk":1,
"model":"store.book",
"fields":{
"name":"MostlyHarmless",
"author":["DouglasAdams"]
}
}
如果需要对其他应用的数据模型做修改,例如使用了django.auth.User(默认认证后端)作为Book 的外键,要想不修改User模型又使用新的模型管理器,可以使用代理模式完成:
fromdjango.dbimportmodels classNewManager(models.Manager): #... pass classMyPerson(Person): objects=NewManager() classMeta: proxy=True
总的来说,这个方案可以完美解决我所遇到的问题,代码量稍微大一些,但是也更灵活。
方案二:DRF的Serializer
下面我们试试用Django-REST-Framework的序列化模块:
fromrest_frameworkimportserializers from.modelsimportBook classBookSerializer(serializers.ModelSerializer): author_name=serializers.CharField(source='author.username') classMeta: model=Book fields='__all__'
这段代码表示,在序列化Book实例时,添加一个新的属性author_name,该值的来源为source参数定义的外键author实例的自然主键username。
然后是执行序列化的过程:
queryset=Book.objects.get(pk=1) BookSerializer(instance=queryset)
序列化结果:
{
"id":1,
"name":"MostlyHarmless",
"author":42,
"author_name":"DouglasAdams"
}
当然,序列化一批Book实例也是可以的:
queryset=Book.objects.all() BookSerializer(instance=queryset,many=True)
序列化结果:
[
{
"id":1,
"name":"MostlyHarmless",
"author":42,
"author_name":"DouglasAdams"
},
{
"id":2,
"name":"HarryPotter",
"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…
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。