前言
最近做的這個專案大量使用了 python 及其相關的生態,因此自然而然選擇了我的 DjangoStarter 作為後端框架
之前 v2 版本是用 RestFramework 做介面的,後面我試用了一次 django-ninja 之後就喜歡這種類似 FastApi 的寫介面方式
正所謂天下苦 drf 久矣,在新的 v3 版本框架中,我決定直接把整個 RestFramework 替換成 ninja
目前大部分功能都完成了,程式碼在主專案的 v3-alpha 分支裡,等開發完成我會合併到 master 裡 (現在基本可用了)
另外還做了很多新功能和改進,接下來會介紹一下
關於 DjangoStarter
這個開發腳手架,最開始還是叫 DjangoRails ,名字模仿的 Ruby on Rails
後面經過重構才改成 DjangoStarter
這個腳手架起源於2020年開始的專案,當時使用 Django + RestFramework 作為後端框架,為了滿足安全部門的要求,又做了很多魔改,再加上其他一些配置啥的,慢慢的就積累出了 DjangoStarter ~
這裡貼一下專案介紹吧~
DjangoStarter v3 是下一代 Django 專案快速開發模板,專為提升開發效率和效能而設計。
結合了 Django 的豐富功能和 Django-Ninja 的效能、靈活、簡潔特性,v3 版本旨在為開發者提供一個更加強大、簡潔和高速的開發體驗。
透過這個全新的框架版本,開發者能夠迅速搭建起符合現代 web 應用標準的專案基礎架構。
核心特性
- Django Ninja 整合:採用 Django Ninja 替代傳統的 Django Rest Framework,為 API 開發帶來了效能最佳化和更簡潔的編碼體驗。利用 Python 型別提示,自動生成互動式 API 文件,不再需要 drf-yasg 那一堆繁瑣的手動配置文件,同時提升了程式碼的可讀性和維護性。
- 增強的安全性:內建了多項安全功能,包括但不限於 Admin 登入驗證碼、IP 限制等,確保應用的安全性。
- 程式碼自動生成:v3 版本進一步最佳化了程式碼生成器,丟掉了 DRF 這個包袱,只需要定義模型,就可以生成 schema 以及 RESTFul API,還能根據定義自動建立測試用例,大大提高開發效率。
- 隨機種子資料生成:v3 版本內建 seed 模組,支援為已有模型自動填充假資料,方便開發測試。
- 模組化專案結構:推出了更加模組化的專案結構設計,方便開發者根據需要新增或移除功能模組,使專案維護更為簡單。
- 現代化前端整合:提供了對現代化前端技術的整合,以及利用 NPM 和 gulp 管理前端資源,幫助開發者打造富互動式的使用者介面。
- 容器化支援:內建 Dockerfile 和 docker-compose.yml 配置,簡化了容器化部署的過程,支援一鍵部署到任何支援 Docker 的環境。
- 詳盡的文件與社群生態:提供全面的文件和指南,覆蓋從專案啟動到部署的每一個步驟。同時,基於活躍的 Django 開源社群,開發者可以輕鬆獲取支援和反饋。
關於v3版本
OK,終於說回正題
這次重構v3版本最主要的原因是把 RestFramework 替換成 ninja
然後也做了一些新的功能
比如:
- 新的自動程式碼生成功能
- 完善了單元測試和整合測試,搭配程式碼生成,可以為每個應用自動生成 crud 的測試用例
- 隨機種子資料,目前使用 faker 實現假資料,打算進一步實現類似 EFCore 的種子資料機制,使假資料更自然
- 新的登入介面
- 多種第三方登入接入(目前接了微信、小程式、企微)
- 使用 tailwindcss 替換 bootstrap 實現前端(只是一些簡單的後臺展示,還是以 API 為主)
- 拆分 settings 配置,像 AspNetCore 那樣支援多個環境配置
- 更換了包管理器
目前大概就這些吧
後面有用到什麼新的再一步步加入
程式碼生成
一直沒有好好介紹一下 DjangoStarter 框架的具體實現
程式碼生成這塊其實也不復雜,包名是 django_starter.contrib.code_generator
主要就兩個部分
- 分析器 -
src/django_starter/contrib/code_generator/analyzer.py
- 生成器 -
src/django_starter/contrib/code_generator/generator.py
分析器使用 django.apps.apps
提供的 get_app_config
和 get_models
來掃描已註冊的所有 App
然後搭配反射(或者在 Python 中應該叫自省 inspect)來獲取各個欄位的資訊,把蒐集到的資訊儲存到我定義的幾個物件中
然後在生成器部分,根據特定的規則,使用 jinja2 模板進行渲染~
注意要把欄位的 primary_key
, is_relation
這些屬性拿出來,後面有用。如果是關係欄位(如外來鍵)的話,還需要把 target_field
拿出來。
大概思路就是這樣,其中有很多細節的地方,本文的篇幅受限,後續我寫篇文章來介紹吧。
API
使用 ninja 來寫 API
不同於之前的 RestFramework ,ninja 用的是裝飾器來定義路徑,這個對於不喜歡 Django 配置式路由的人來說很友好
專案結構我也做了一些調整
以 demo 應用為例
每個 model 都在 apis 下單獨建立一個 package,單獨有 apis.py 和 schemas.py 程式碼,這樣不會把所有程式碼邏輯混在一起
PS:後續如果我轉向使用 FastApi,也可以用這個思路來組織專案
demo
├─ tests
│ ├─ __init__.py
│ ├─ test_music_album.py
│ ├─ test_music.py
│ ├─ test_movie.py
│ └─ test_actor.py
├─ migrations
│ ├─ __init__.py
│ └─ 0001_initial.py
├─ apis
│ ├─ music_album
│ │ ├─ __init__.py
│ │ ├─ schemas.py
│ │ └─ apis.py
│ ├─ music
│ │ ├─ __init__.py
│ │ ├─ schemas.py
│ │ └─ apis.py
│ ├─ movie
│ │ ├─ __init__.py
│ │ ├─ schemas.py
│ │ └─ apis.py
│ ├─ actor
│ │ ├─ __init__.py
│ │ ├─ schemas.py
│ │ └─ apis.py
│ └─ __init__.py
├─ __init__.py
├─ views.py
├─ models.py
├─ apps.py
└─ admin.py
坑點
要說的話,ninja 這種比較新的庫,還是有一點點坑的地方的
- 有些文件不夠詳細
- URL reverse 功能不夠好用,只能在
NinjaAPI
物件配置urls_namespace
,下面的各級 router 都不能配置urls_namespace
,我只能對下面的介面用url_name='demo/movie/list'
這種形式的命名 - ModelSchema 對外來鍵的支援有限,對於輸入的 schema ,不能在 Meta.fields 裡配置這個外來鍵欄位,需要自己單獨寫出來,這點對於自動生成程式碼來說有點麻煩,不過我已經解決了
種子資料/假資料
這個可以叫 seed data ,也可以叫 mock data
在開發測試中很有用,不用手動去新增各種資料
包名是 django_starter.contrib.seed
一開始我是找到了一個叫 django-seed 的庫,可以實現種子資料的生成
不過這個包已經年久失修,好幾年沒更新了
我試了一下,執行起來居然還依賴 PostgreSql 的庫?!
就離譜,不應該和資料庫無關的嗎……
算了,我自己寫得了,又不難
Python 生態就是好,Faker 庫用來生成隨機假資料很好用
主要程式碼在 src/django_starter/contrib/seed/seeder.py
檔案裡
就是根據不同的欄位型別,使用不同的假資料方法
坑點:外來鍵
其中外來鍵欄位會比較坑,需要做一些特殊處理
related_model = field.related_model
# Ensure there is at least one instance of the related model
related_instance = related_model.objects.order_by('?').first()
if not related_instance:
related_instance = related_model.objects.create(**self.seed(related_model))
# Set the foreign key ID field
fake_data[field.attname] = related_instance.pk
settings 拆分
早就對 Django 的配置 settings.py 不爽了
專案一大,這個檔案就亂七八糟又臭又長
而且還不支援多環境切換,得自己寫一堆邏輯去判斷不同環境
之前版本中,我是把幾個主要的配置拆分成不同檔案,然後在 settings.py
裡引用
現在我用上了 django-split-settings 這個包,瞬間舒服了
來看看現在的 config 目錄
config
├─ settings
│ ├─ environments
│ │ ├─ __init__.py
│ │ ├─ testing.py
│ │ ├─ production.py
│ │ ├─ local.py.template
│ │ └─ development.py
│ ├─ components
│ │ ├─ __init__.py
│ │ ├─ simpleui.py
│ │ ├─ rq.py
│ │ ├─ ninja.py
│ │ ├─ logging.py
│ │ ├─ django_starter.py
│ │ ├─ database.py
│ │ ├─ csp.py
│ │ ├─ cors.py
│ │ ├─ common.py
│ │ ├─ captcha.py
│ │ └─ caches.py
│ └─ __init__.py
├─ __init__.py
├─ wsgi.py
├─ urls_root.py
├─ urls.py
├─ env_init.py
├─ asgi.py
└─ apis.py
可以看到現在 settings 變成了一個 package
各種配置拆分出來分散到 components 下面
然後不同的環境又放到 environments 下面,可以覆蓋前面定義的配置
很好的解決了之前的幾個痛點
更換包管理器
原本就直接使用 pip ,搭配 requirements.txt 來管理依賴
這個方式的優缺點我就不多說了
這次換成 pdm ,總算有點現代包管理器的感覺了
PS:其實我之前還用過 poetry ,不過偶爾會遇到一些奇奇怪怪的問題,棄了~
小結
大概就這些吧,後面有什麼新的想法我再來更新
還有其中幾個關鍵的更新我可能會單獨寫文章來詳細介紹~