前言
不知不覺已經九月了,又到了一年的開學季,我每年都想做的專案牆甚至連個影子都沒有…😂
最近生活中的瑣事太多了,導致完全沒有想寫文章的動力,不過再怎麼拖還是得記錄,隨便寫寫吧~
這次是7月份的一個小專案,談不上什麼技術含量,算是友情開發了。後端 DjangoStarter v3,前端使用 Taro 做微信小程式。
事實上後端基本沒啥好說的,基本有什麼坑能記錄的都在小程式這塊,不得不說微信真是史中史,不僅程式碼寫得跟答辯一樣,文件更是一坨😂
之前看網上有個說法,正因為微信做得跟答辯一樣,才給了小程式開發者一道護城河,現在想起來還蠻有道理的,哈哈哈😃
後端部分
先來說說後端部分吧
自從有了 DjangoStarter ,這類小專案的後端開發已經直接秒了
這個專案裡用到幾個新的,我之前有寫文章介紹了,直接放連結吧~
- Django裡整合騰訊COS物件儲存
- 使用 django-treebeard 編輯類別
使用 django-filer 管理檔案
忘了這個還沒寫文章,那麼簡單介紹一下吧,這個元件可以實現檔案(包括圖片)的集中管理,還提供了 admin 介面,不過UI風格就比較原始了,而且中文locale還有bug,得手動修改才能正常切換到中文。不過對於這個專案來說是夠用的
專案地址: https://github.com/django-cms/django-filer
安裝: pdm add django-filer
把 filer
新增到 INSTALLED_APPS
裡
新增 src/config/settings/components/filer.py
配置
THUMBNAIL_PROCESSORS = (
'easy_thumbnails.processors.colorspace',
'easy_thumbnails.processors.autocrop',
'easy_thumbnails.processors.scale_and_crop',
# Subject location aware cropping
# 'filer.thumbnail_processors.scale_and_crop_with_subject_location',
'easy_thumbnails.processors.filters',
)
FILER_ENABLE_LOGGING = True
最後修改一下需要用到附件的欄位
from filer.fields.image import FilerImageField
class Brand(ModelExt):
name = models.CharField('名稱', max_length=100)
# logo = models.ImageField(upload_to='brand_logos/')
logo = FilerImageField(
verbose_name='Logo', null=True, blank=True,
related_name='+', on_delete=models.SET_NULL,
)
大概就這樣,具體用法看官方文件吧~
使用 ninja 來編寫API
DjangoStarter v3 開始從drf 切換到 ninja
這個 ninja 和 FastAPI 非常像,寫起來比 drf 舒服多了,雖然不能像 drf 一樣自動生成 crud 介面,不過寫起來也多不了多少程式碼,而且更加靈活。
(PS:DjangoStarter v3 依然支援自動生成 crud 介面,同時還可以使用第三方庫來實現自動生成基於 ninja 的 crud 介面,不過我還沒用過,可以參考下面的擴充套件部分)
比如上面說的 django-filer 庫,圖片欄位其實是指向 filer.fields.models.File
模型的外來鍵,所以介面如果沒做處理,生成出來的資料就只是一個外來鍵ID而已,所以要修改一下 schema
class CaseOut(ModelSchema):
cover_image: str
@staticmethod
def resolve_cover_image(obj: Case):
if not obj.cover_image:
return f'https://starblog/Api/PicLib/Random/{obj.id}/250/150'
return obj.cover_image.url
class Meta:
model = Case
fields = [
'id', 'is_deleted', 'created_time', 'updated_time', 'name', 'description',
'category', 'car_model', 'part', 'build_time',
]
以上程式碼會把 cover_image
欄位渲染為一個圖片地址,如果 cover_image
不存在的話,則返回一個隨機圖片地址。
schema 實現了邏輯和資料渲染分開,程式碼結構更清晰。
還可以實現更復雜的邏輯,只需要實現 resolve_欄位名
方法就行。
ninja 生態擴充套件
我也是最近才發現的,原來 django-ninja 還有個中文網,裡面寫了幾個 ninja 生態的擴充套件,感覺都挺不錯的,以後有空可以試試。
網址: https://django-ninja.cn/
- Ninja JWT - 一個用於 Django Ninja REST 框架的 JSON Web 令牌認證外掛。
- Django Ninja Extra - Django Ninja Extra 提供了一種 基於類 的方法以及額外的功能,這將使用 Django Ninja 加速您的 RESTful API 開發。
- Django Ninja CRUD - Django Ninja CRUD 是一個強大的、宣告式的、但又有點固執己見的框架,它簡化了使用 Django Ninja 開發 CRUD(建立、讀取、更新、刪除)端點的過程,並且還提供了一種宣告式的基於場景的方法,用於使用 Django REST Testing(這個包的小弟)測試這些端點。
小程式部分
這次依然使用 Taro 來開發移動端,上次是做 H5(公眾號),這次試試 Taro 做出來的小程式咋樣,實際效果還行。
狀態管理依然選擇了 mobx ,用習慣了,前端輪子太多,懶得去試用其他的了~
相比起後端部分,小程式能寫的東西會多一點點(但也不多,都很簡單)
在Taro裡使用tailwindcss
最近我開始使用 tailwindcss ,一下就喜歡上這種高效的樣式工具(雖然會有很長的一串class)不過瑕不掩瑜,使用 tailwindcss 可以很方便在網路上 copy 各種樣式,還能讓 LLM 幫我寫各種樣式,生產力拉滿了~
Taro 官方提供了 tailwindcss 的支援,這點非常好,跟著官方文件來就行
詳見官方文件: https://docs.taro.zone/docs/tailwindcss
分享小程式
如果不主動呼叫,那麼只有註冊了 onShareAppMessage
事件,才能在點右上角三個點的時候,顯示轉發按鈕;同樣的,註冊了 onShareTimeline
事件才能分享到朋友圈。
相應的,Taro 裡提供了這倆事件的 hook ,直接看程式碼
import Taro, {useShareAppMessage, useShareTimeline} from '@tarojs/taro'
const CasePage = () => {
const getCase = async () => {
if (!id) return
const data = await CaseService.get(Number(id))
console.log('get case', data)
setCaseData(data.data)
return {
title: `案例:${data.data.name}`,
path: RouterMap.car.case(data.data.id),
imageUrl: data.data.cover_image
}
}
useShareAppMessage(res => {
console.log('執行分享操作', res)
return {
title: '案例分享',
path: RouterMap.index,
promise: new Promise(resolve => getCase().then((data) => {
// @ts-ignore
resolve(data)
})),
}
})
useShareTimeline(() => {
console.log('執行分享朋友圈操作')
return {
title: `案例:${caseData.name}`,
query: `id=${caseData.id}`,
imageUrl: caseData.cover_image
}
})
}
跟小程式文件裡說的一樣的,需要返回的引數啥的也一樣,所以直接看文件吧~
主動分享
主動分享的話,只要把按鈕設定成 share
型別就行,Taro 同樣做了包裝。
<Button type='info' icon={<Share/>} openType='share'>分享</Button>
參考資料
- https://juejin.cn/post/7261774602481369147
- https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
- https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object
使用者登入
本專案中我是做一個單獨的頁面來處理登入,也可以用 Modal 的形式,不過一開始我還對獲取使用者資訊抱有幻想,因為文件介紹獲取使用者頭像、暱稱等資訊需要主動觸發才行,所以放在一個單獨的頁面,提供一個登入按鈕,讓使用者去點選。
不過後面發現新版本的小程式已經不能用這種方式獲取資訊了……
登入這塊 Taro 也封裝好了,其實就是把 wx.login
的 wx 改成 Taro 而已
const LoginPage = observer(() => {
const handleLogin = async (data: LoginToken) => {
UserStore.login({
token: data.token,
exp: String(data.exp),
})
const userInfo = await AccountService.getCurrentUser()
console.log('獲取使用者資訊', userInfo)
UserStore.userInfo = userInfo.data
Taro.navigateBack()
}
const autoLogin = async () => {
Taro.showToast({title: '正在登入', icon: 'loading'})
Taro.login({
success: async (res) => {
if (res.code) {
console.log('小程式登入,獲取code', res.code)
const resp = await AccountService.loginWeApp(res.code)
console.log('小程式登入,請求後端', resp)
await handleLogin(resp.data)
} else {
console.log('登入失敗!', res.errMsg)
}
}
})
}
}
拿到小程式 OAuth 之後的 code,呼叫後端介面登入,搞定。
而且這部分 DjangoStarter v3 也整合了🕶
點選圖片開啟大圖
在圖片元件的 onTap
事件裡呼叫 Taro.previewImage
就行,記得把大圖的地址傳入(如果小圖是單獨的地址的話)
<Image
key={e.id} src={e.url}
onTap={() => {
Taro.previewImage({
current: e.url,
urls: caseData.images.map(i => i.url)
})
}}
className="w-full rounded shadow"
mode="aspectFill"
/>
部署
這次的部署也是船新版本
這個專案也算是新版 DjangoStarter 的第一次實踐,所以遇到一些坑,我都寫了部落格記錄了
- 在python專案的docker映象裡使用pdm管理依賴
- 新版的Django Docker部署方案,多階段構建、自動處理前端依賴
- 使用python-slim映象遇到無法使用PostgreSQL的問題
總得來說 daphne 伺服器用著還不錯。
小結
開頭就說了,本次專案的算是比較簡單的,時間主要花在前端的互動和一些細節的調整上
Taro 用來開發小程式還是絲滑的,意料中打包和釋出可能遇到的問題,實際上都沒有
這篇小結真的拖了太久了,我也好久沒寫程式碼了… 得抓緊時間整理思緒,然後把想做的東西都搞起來了。