在設計表結構時,不免須要創建一些外鍵關聯。例如這樣兩個模型:前端
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 還有不少功能,例如分頁等,強烈建議學習學習。
固然,可能不存在最好的最好的技術方案,遇到這類問題選擇最合適本身的就好。也可能還有更多的方法能夠解決標題的問題,也歡迎留言探討!