Django模型序列化返回自然主鍵值示例代碼
場景
在設(shè)計表結(jié)構(gòu)時,難免需要建立一些外鍵關(guān)聯(lián)。例如這樣兩個模型:
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 實(shí)例序列化:
from django.core import serializers book_json = serializers.serialize("json", Book.objects.get(pk=1))
JSON 序列化結(jié)果如下:
{ "pk": 1, "model": "store.book", "fields": { "name": "Mostly Harmless", "author": 42 } }
這個 "author": 42 對用戶來說相當(dāng)于未知,我們需要的是 Person 表中主鍵為 42 的用戶姓名,即 username 的值。
解決方案
在 Django 官方文檔的「序列化」一節(jié)中提到了用 models.Manager 處理的方案;在搜索解決方案過程中,也接觸到 Django-REST-Framework(DRF) ,了解到 DRF 中的 Serializer 模塊也能解決這類問題。那我們不妨對比一下兩種解決方案。
方案一:models.Manager
根據(jù)文檔,要返回自然主鍵,我們需要定義一個模型管理器,創(chuàng)建一個 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 實(shí)例:
from django.core import serializers book_json = serializers.serialize("json", Book.objects.get(pk=1), use_natural_foreign_keys=True)
得到新的結(jié)果如下:
{
"pk": 1,
"model": "store.book",
"fields": {
"name": "Mostly Harmless",
"author": ["DouglasAdams"]
}
}
如果需要對其他應(yīng)用的數(shù)據(jù)模型做修改,例如使用了 django.auth.User(默認(rèn)認(rèn)證后端)作為 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 實(shí)例時,添加一個新的屬性 author_name,該值的來源為 source 參數(shù)定義的外鍵 author 實(shí)例的自然主鍵 username。
然后是執(zhí)行序列化的過程:
queryset = Book.objects.get(pk=1) BookSerializer(instance=queryset)
序列化結(jié)果:
{
"id": 1,
"name": "Mostly Harmless",
"author": 42,
"author_name": "DouglasAdams"
}
當(dāng)然,序列化一批 Book 實(shí)例也是可以的:
queryset = Book.objects.all() BookSerializer(instance=queryset, many=True)
序列化結(jié)果:
[
{
"id": 1,
"name": "Mostly Harmless",
"author": 42,
"author_name": "DouglasAdams"
},
{
"id": 2,
"name": "Harry Potter",
"author": 2,
"author_name": "JKRowling"
}
]
可以看到,使用 DRF 的序列化模塊返回自然主鍵,不僅代碼清晰改動少,而且效果也很不錯,序列化數(shù)據(jù)少了一個層級,對前端也是十分友好的。
方案三:手動修改序列化后的外鍵
當(dāng)然,還有一種最傻也是最容易想到的辦法,就是在序列化后,手動修改 JSON 串中對應(yīng)的外鍵值為自然主鍵值。
這種做法可以得到和方案一一樣的效果,但是遇到查詢結(jié)果為列表時我們需要遍歷替換。同時試想一下,如果我們在每個視圖中都這么處理,那代碼會變得十分糟糕。不建議使用該方案。
總結(jié)
對比兩種序列化方案,我個人更偏向于 DRF 優(yōu)雅的處理方式。當(dāng)然,除了序列化,DRF 還有很多功能,例如分頁等,強(qiáng)烈建議學(xué)習(xí)學(xué)習(xí)。
當(dāng)然,可能不存在最好的最好的技術(shù)方案,遇到這類問題選擇最合適自己的就好。也可能還有更多的方法可以解決標(biāo)題的問題,也歡迎留言探討!
參考
- docs.djangoproject.com/zh-hans/2.2…
- docs.djangoproject.com/en/2.2/topi…
- www.django-rest-framework.org/api-guide/f…
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
python腳本實(shí)現(xiàn)驗(yàn)證碼識別
這篇文章主要為大家詳細(xì)介紹了python腳本實(shí)現(xiàn)驗(yàn)證碼識別的實(shí)現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06python實(shí)現(xiàn)redis三種cas事務(wù)操作
本篇文章主要介紹了python實(shí)現(xiàn)redis三種cas事務(wù)操作,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12sklearn線性邏輯回歸和非線性邏輯回歸的實(shí)現(xiàn)
這篇文章主要介紹了sklearn線性邏輯回歸和非線性邏輯回歸的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06python3.5 tkinter實(shí)現(xiàn)頁面跳轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了python3.5 tkinter實(shí)現(xiàn)頁面跳轉(zhuǎn),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01菜鳥使用python實(shí)現(xiàn)正則檢測密碼合法性
本文給大家分享了2則使用Python實(shí)現(xiàn)正則表達(dá)式檢測密碼合法性的代碼,由于是新手,所以方法比較笨,不過還是分享給小伙伴,希望對大家能夠有所幫助。2016-01-01Python搭建代理IP池實(shí)現(xiàn)檢測IP的方法
這篇文章主要介紹了Python搭建代理IP池實(shí)現(xiàn)檢測IP的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10如何修改pycharm使用anaconda環(huán)境后的pip install安裝路徑問題
本文主要介紹了如何修改pycharm使用anaconda環(huán)境后的pip install安裝路徑問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02使用 Python 快速實(shí)現(xiàn) HTTP 和 FTP 服務(wù)器的方法
這篇文章主要介紹了使用 Python 快速實(shí)現(xiàn) HTTP 和 FTP 服務(wù)器 的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-07-07