我獨自 Django:快速打造天氣 API 服務(下篇)

更新 發佈閱讀 14 分鐘
Generating by ChatGPT-4o

Generating by ChatGPT-4o

在上一篇《我獨自 Django:快速打造天氣 API 服務(中篇)》中,我們探討了使用 API 的不同方式,一是即時取得資料並顯示;另一則是將資料儲存進資料庫。在理解了如何串接 API 與儲存之後,接下來的重點,就是要逐步的累積資料,以備未來分析時使用。


說到「排程」,相信不少讀者對 Windows 內建的排程系統並不陌生,簡單來說,就是在指定時間點自動執行預先設定的任務。而在 Django 中,我們可以透過適合的工具去設定與執行,今天要介紹 Celery,逐步的帶大家了解設定過程與實際的執行。

Celery 是一個處理非同步應用的 Python 套件,支援分散式系統架構,能有效處理大量背景任務,像是將耗時的操作,如發送郵件、數據處理與多次 API 請求等操作,都可以設定在背景執行,避免阻塞主程式。更進階的應用也可以使用 RabbitMQ 或是 Redis 來做中介系統整合,進行排程管理任務。

本篇也會使用到 Redis 作為 Celery 的中介(Message Broker);而對這個工具有興趣的讀者,也可以在熟悉之後做更深入的研究。
*部分的程式因為 blog 的編譯器會有點跑版,可以到我的 Medium 觀看更好複製的版本


那我們就從程式碼開始吧!回到 VSCode 介面,我們先安裝需要的工具

cd backend # 切換到 backend

./django-backend/Scripts/activate # 記得切換虛擬環境

pip install celery
pip install redis

另外 Windows 中 Redis 可能不支援,要另外安裝 Memurai,其應用與 Redis 幾乎一樣,直接使用開發版本就好 :

Memurai 安裝

Memurai 安裝

安裝完之後開啟任意的 cmd 測試是否有執行,預設的 Port 為 6379

memurai-cli # 檢查是否有進入 127.0.0.1:6379

net start memurai # 若沒有啟動則使用這個指令

一切就緒之後回到程式,建立新的 .py 檔 weather/tasks.py ,輸入以下程式碼 :

from celery import shared_task
import requests
from .models import Weather
from datetime import datetime

@shared_task
def fetch_and_store_weather():
cities = {
"Taipei": {"lat": 25.05, "lon": 121.53},
"Taichung": {"lat": 24.15, "lon": 120.67},
"Kaohsiung": {"lat": 22.63, "lon": 120.30},
}

for city, coord in cities.items():
url = f"https://api.open-meteo.com/v1/forecast?latitude={coord['lat']}&longitude={coord['lon']}&current_weather=true"
response = requests.get(url)
data = response.json()

current = data.get("current_weather", {})
if current:

Weather.objects.create(
city=city,
temperature=current.get("temperature"),
windspeed=current.get("windspeed"),
time=datetime.fromisoformat(current.get("time"))
)

然後再建立新的 .py 檔 config/celery.py ,輸入以下程式碼 :

import os
from celery import Celery
from celery.schedules import crontab

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

app = Celery('config')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.conf.worker_pool = 'solo' # 可先這行註解後執行看看
app.conf.broker_url = 'redis://localhost:6379/0'
app.autodiscover_tasks(['weather'])
app.conf.timezone = 'Asia/Taipei'
app.conf.beat_schedule = {
'fetch-weather-every-day': {
'task': 'weather.tasks.fetch_and_store_weather',
'schedule': crontab(hour=10, minute=10), # 該時區的時間,這裡是 10:10
},}

* worker_pool = 'solo' 是為了解決 Windows 環境中多處理程序可能導致的序列化問題。在正式開發階段,建議將此設定移除,讓 Celery 自行使用最適合的處理方式,以獲得更高效的執行效能。

再來到設定 config/settings.py

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'weather',
'rest_framework',
'django_celery_beat' # 添加這行
]

開啟兩個 Terminal 執行程式

cd backend # 記得切換到後端專案內

首先第一個 Terminal 執行 Celery 的 worker

celery -A config worker --loglevel=info

此時可以看到連接資訊,以及 Redis 連線成功

Celery worker

Celery worker

再到第二個 Terminal 上執行 beat

celery -A config beat --loglevel=info

連接資訊如下

Celery beat

Celery beat

等到設定的時間(這裡是設定為10:10),就會把昨日的氣象資訊匯入資料庫了!

執行排程後,成功匯入資料庫

執行排程後,成功匯入資料庫

以上就是使用 Celery 設定排程的基本流程。有實務經驗的讀者可能會發現,還有許多可以優化的地方,例如資料庫想再多加一些欄位,或是 Call API 的頻率與時機。這些問題往往要經過實際操作與測試後,才能更有感的體會與改善。希望各位也能找到自己實作的樂趣和成就感!


在理解了 Celery 的基礎應用之後,下一步我們要來探討:Django 中的 API 架構應該如何設計,才能兼顧可讀性、擴展性與維護性?

當專案逐漸成長,功能變得複雜時,良好的 API 結構將會是每位後端工程師不可或缺的技能。這也是區分 Junior 與 Senior 的關鍵之一。

在傳統的 Django 架構中,後端邏輯通常集中在 views.py 中,再將完成的邏輯使用諸如 JsonResponse 等用法回傳資料,並由 urls.py 負責對應 URL 路徑,構成基本的 API。

而隨著專案功能的擴展,將所有的業務邏輯都堆疊在views.py 會變得難以維護,也很難測試。所以適當的做法是將主要邏輯抽離到 Service Layer,讓 views.py 僅負責接收請求與回應結果,使整體架構更清晰易懂。這樣做的好處是:當你看到某隻 API 時,可以一眼了解其用途與結構,並能在 services/ 中集中維護商業邏輯。


以下簡單實作一部分讓讀者更深刻一些,以我們已建立的 App weather 來做說明,這是目前的架構

weather 架構

weather 架構

我們先新增未來要擴展邏輯的 services,並且新增一個 .py 檔

weather 架構

weather 架構

回到我們的 weather/views.py ,把程式改成 :

from rest_framework.decorators import api_view
from rest_framework.response import Response
from .services.weather_service import get_weather_summary

@api_view(['GET']) # 添加該 API 的簡易註解
def get_weather(request):
data = get_weather_summary()
return Response(data)

再到 weather/services/weather_service.py ,新增以下程式 :

import requests

def get_weather_summary():
cities = {
"Taipei": {"lat": 25.05, "lon": 121.53},
"Taichung": {"lat": 24.15, "lon": 120.67},
"Kaohsiung": {"lat": 22.63, "lon": 120.30},
}

weather_data = []

for city, coord in cities.items():
url = f"https://api.open-meteo.com/v1/forecast?latitude={coord['lat']}&longitude={coord['lon']}&current_weather=true"
response = requests.get(url)
data = response.json()
current = data.get("current_weather", {})

if current:
weather_data.append({
"city": city,
"temperature": current.get("temperature"),
"windspeed": current.get("windspeed"),
"time": current.get("time")
})

return weather_data

在這邊可以看到,我們將大部分的邏輯抽離到 Service Layer 中,views.py 就是做為每個 API 的邏輯入口,會讓整個架構變得非常清晰且專業。

當然也是有其它做法,但這個是我在實務上最常用、也覺得最直覺的。讀者也可以發展成適合自己團隊的作法,只要大家都遵循統一的 Rules,合作起來才能更加順暢。


以上就是《我獨自 Django》系列的尾聲啦!希望這個系列能對剛踏入後端開發的你有所幫助,讓你在實作中累積信心與經驗。

接下來的系列,我會著手補上這個專案的前端部分,嘗試將我們匯入的天氣資料視覺化呈現在網頁上,讓這個 side project 更加完整。如果你也喜歡這種一步步實作、穩紮穩打的開發方式(雖然節奏有點慢 XD),歡迎持續關注。那我們就下次見囉!

留言
avatar-img
留言分享你的想法!
avatar-img
Alan的沙龍
0會員
12內容數
不定期技術文章、旅遊、人生見解分享
Alan的沙龍的其他內容
2025/08/23
本篇會介紹方法二 : 建立屬於自己的資料庫 。我們會先將外部 API 的天氣數據儲存下來,再透過設計 API 介面來實現歷史資料的查詢與運用。
Thumbnail
2025/08/23
本篇會介紹方法二 : 建立屬於自己的資料庫 。我們會先將外部 API 的天氣數據儲存下來,再透過設計 API 介面來實現歷史資料的查詢與運用。
Thumbnail
2025/08/23
本篇要深入 API 的世界,以常見的天氣資訊應用為例,介紹兩種實作方式 : 1. 直接串接第三方 API : 整合開源天氣數據,重組為符合需求的 Response 2. 建立屬於自己的資料庫 : 先將外部 API 數據儲存後,再設計 API 介面
Thumbnail
2025/08/23
本篇要深入 API 的世界,以常見的天氣資訊應用為例,介紹兩種實作方式 : 1. 直接串接第三方 API : 整合開源天氣數據,重組為符合需求的 Response 2. 建立屬於自己的資料庫 : 先將外部 API 數據儲存後,再設計 API 介面
Thumbnail
2025/08/23
這是一個全新的系列文章,我會從頭開始,獨自搭建一套完整的 Django 後端系統。本篇將專注於專案初始化,包括資料夾結構設計、虛擬環境建立、資料庫串接,並最終啟動本地後端伺服器,完成第一步實作。 作為一名軟體工程師,良好的專案架構能讓開發過程更加清晰,不僅有助於初期的工作分配,也為未來的擴展與維護
Thumbnail
2025/08/23
這是一個全新的系列文章,我會從頭開始,獨自搭建一套完整的 Django 後端系統。本篇將專注於專案初始化,包括資料夾結構設計、虛擬環境建立、資料庫串接,並最終啟動本地後端伺服器,完成第一步實作。 作為一名軟體工程師,良好的專案架構能讓開發過程更加清晰,不僅有助於初期的工作分配,也為未來的擴展與維護
Thumbnail
看更多
你可能也想看
Thumbnail
Python資料視覺化在數據分析中扮演關鍵角色,透過視覺化捕捉數據模式、趨勢和異常,透過Matplotlib等工具創建專業圖表變相對簡單和高效。
Thumbnail
Python資料視覺化在數據分析中扮演關鍵角色,透過視覺化捕捉數據模式、趨勢和異常,透過Matplotlib等工具創建專業圖表變相對簡單和高效。
Thumbnail
介紹如何用assign函數在Python中建立新欄位
Thumbnail
介紹如何用assign函數在Python中建立新欄位
Thumbnail
你知道IG是用Django開發的嗎? 正在學習或使用Django、Flask框架開發後端的你,是否也常在享受Python語法的舒適之餘,仍然煩惱著是否該學習效率更好的GO或Laravel。
Thumbnail
你知道IG是用Django開發的嗎? 正在學習或使用Django、Flask框架開發後端的你,是否也常在享受Python語法的舒適之餘,仍然煩惱著是否該學習效率更好的GO或Laravel。
Thumbnail
本文在介紹如何用Python繪製各點大小不同的散布圖及用箭頭標註特殊點
Thumbnail
本文在介紹如何用Python繪製各點大小不同的散布圖及用箭頭標註特殊點
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有分享 FastAPI 這套API框架, 那麼當我們想要在應用程式剛執行時就註冊一些事件或者共享GPU運算模型、變數…等,當整個應用程式關閉時也進行釋放作業, 這樣的一個週期循環就是所謂的生命週期, 而在FastAPI這
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有分享 FastAPI 這套API框架, 那麼當我們想要在應用程式剛執行時就註冊一些事件或者共享GPU運算模型、變數…等,當整個應用程式關閉時也進行釋放作業, 這樣的一個週期循環就是所謂的生命週期, 而在FastAPI這
Thumbnail
當我們在開發一個AI應用服務時, 常常會需要載入大模型, But… 我們總不可能每一次的請求就載入一次模型吧! 這樣太沒有效率了, 也非常的浪費資源, 因此我們通常會希望應用程式啟動時就能夠載入模型, 之後每一次的請求只要讓模型進行運算即可, 那麼在FastAPI的框架中究竟要如何使用呢? 首
Thumbnail
當我們在開發一個AI應用服務時, 常常會需要載入大模型, But… 我們總不可能每一次的請求就載入一次模型吧! 這樣太沒有效率了, 也非常的浪費資源, 因此我們通常會希望應用程式啟動時就能夠載入模型, 之後每一次的請求只要讓模型進行運算即可, 那麼在FastAPI的框架中究竟要如何使用呢? 首
Thumbnail
本文介紹如何用Python繪製散布圖與迴歸線
Thumbnail
本文介紹如何用Python繪製散布圖與迴歸線
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News