在设计表结构时,不免须要创建一些外键关联。例如这样两个模型:前端
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
实例序列化:python
from django.core import serializers
book_json = serializers.serialize("json", Book.objects.get(pk=1))
复制代码
JSON 序列化结果以下:django
{
"pk": 1,
"model": "store.book",
"fields": {
"name": "Mostly Harmless",
"author": 42
}
}
复制代码
这个 "author": 42
对用户来讲至关于未知,咱们须要的是 Person
表中主键为 42 的用户姓名,即 username
的值。json
在 Django 官方文档的「序列化」一节中提到了用 models.Manager
处理的方案;在搜索解决方案过程当中,也接触到 Django-REST-Framework
(DRF) ,了解到 DRF 中的 Serializer
模块也能解决这类问题。那咱们不妨对比一下两种解决方案。后端
根据文档,要返回天然主键,咱们须要定义一个模型管理器,建立一个 get_by_natural_key
方法,以下:api
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
实例:less
from django.core import serializers
book_json = serializers.serialize("json", Book.objects.get(pk=1), use_natural_foreign_keys=True)
复制代码
获得新的结果以下:ide
{
"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
复制代码
总的来讲,这个方案能够完美解决我所遇到的问题,代码量稍微大一些,可是也更灵活。ui
下面咱们试试用 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 还有不少功能,例如分页等,强烈建议学习学习。
固然,可能不存在最好的最好的技术方案,遇到这类问题选择最合适本身的就好。也可能还有更多的方法能够解决标题的问题,也欢迎留言探讨!