大家好,我是每天分享AI應用的螢火君!
經常接觸機器學習的同學可能都接觸過Gradio這個框架,Gradio是一個基於Python的專門為機器學習專案建立的快速開發框架,可以讓開發者快速釋出自己的模型給使用者測試,目前Huggingface上的機器學習專案都是基於Gradio對外提供服務的。
不過Gradio的目標是機器學習模型的快速演示,真正為使用者提供服務時,我們還有很多需要關注的方面,比如使用者的鑑權授權、訊息通知、靜態頁面、SEO最佳化等等,這些使用Gradio有點捉襟見肘,我們還需要使用更加成熟的Web開發框架,比如Django這種。
但是我們初期可能已經用Gradio做了很多的功能,不想重寫這些東西,這時候就產生了整合Gradio到其它框架的需求。這篇文章就來分享如何將Gradio整合到成熟的Web框架Django,以方便後來者。
建立Django專案
這裡假設我們已經有了一個Gradio的專案,將在這個專案中繼續建立一個Django專案。
建立 Django 專案
首先透過 pip 安裝 Django:
pip install django
然後在程式的根目錄初始化Django專案的一些基礎檔案:
django-admin startproject myproject
cd myproject
這裡的 myproject 需要替換成你的 Django 專案名。
然後我們還要繼續建立 Django 應用,應用可以理解為模組,比如專案下有管理模組、使用者模組、支付模組和具體的業務單元模組。每個應用都有自己的模型、檢視、模板和 URL 路由。
python manage.py startapp myapp
請將myapp改為你的應用名稱。
執行完這些命令之後,專案中將會增加一些Django的框架指令碼。
建立 Django 頁面
有了Django的基礎指令碼,然後就可以開發Web頁面了。
1個頁面涉及三個方面:檢視、路由和HTML模板,還是以 myapp 為例:
在 myapp/views.py 中建立一個檢視:
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
在 myapp/urls.py 中設定 URL 路由到這個檢視:
from django.urls import path
from .views import index
urlpatterns = [
path('', index, name='index'),
]
在 myapp/templates/index.html 建立 HTML 模板:
<!DOCTYPE html>
<html>
<head>
<title>Gradio in Django</title>
</head>
<body>
<h1>Welcome to My App</h1>
</body>
</html>
然後我們就可以啟動程式,在瀏覽器訪問這個頁面了:
uvicorn myproject.wsgi:application --reload
啟動程式使用的是 uvicorn工具,myproject是專案的名稱,wsgi對應到myproject資料夾下的 wsgi.py。
整合Gradio到Django
準備一個Gradio專案
為了演示,這裡準備一個Gradio的程式。
假設檔案路徑為:gradio/app.py
import gradio as gr
def greet(name):
return f"Hello {name}!"
# 定義 Gradio 介面
demo = gr.Interface(fn=greet, inputs="text", outputs="text")
整合 Gradio 和 Django
現在我們把 Gradio 整合到 Django 中,它們將在同一個程序中執行,對外使用一個埠號。Django 預設透過根目錄 / 進行訪問,Gradio則透過 /gradio 進行訪問。
這裡走過一些彎路,有問題的方法就不講了,直接給出我的方案。
這裡還要引入一個框架 FastAPI,我們將使用 FastAPI 來代理對 Gradio 和 Django 的訪問,所以其實不是將Gradio整合到Django,這個方法本質上是將 Gradio 和 Django 整合到一起。
開啟 myproject/wsgi.py,這是 Django 專案的主檔案:
import os
from django.core.wsgi import get_wsgi_application
from fastapi import Request, Response
from starlette.middleware.wsgi import WSGIMiddleware
import gradio as gr
from gradio.app import demo
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# 建立 FastAPI 應用
app = FastAPI()
# 掛載 Gradio 到FastAPI,注意這個path要和下邊中介軟體中的一致
app = gr.mount_gradio_app(app, demo, path="/gradio")
# 獲取 Django 的 WSGI 應用
django_app = get_wsgi_application()
# 註冊一個FastAPI中介軟體,實現
@app.middleware("http")
async def route_middleware(request: Request, call_next):
# 如果路徑是 /gradio,則呼叫call_next,FastAPI框架會交給已經註冊的 Gradio程式 處理
if request.url.path.startswith("/gradio"):
return await call_next(request)
# 否則交給Django處理
response = Response()
async def send(message):
if message['type'] == 'http.response.start':
response.status_code = message['status']
response.headers.update({k.decode(): v.decode() for k, v in message['headers']})
elif message['type'] == 'http.response.body':
response.body += message.get('body', b'') # 注意這裡用 += 來累積響應體
await WSGIMiddleware(django_app)(request.scope, request.receive, send)
response.headers["content-length"] = str(len(response.body))
return response
這段程式碼的邏輯也比較簡單,先建立FastAPI應用,然後將Gradio程式掛載到FastAPI,這裡使用的是Gradio自帶的mount_gradio_app方法,然後建立了一個FastAPI的中介軟體,對不同的路由使用不同的處理。
重點就在這個FastAPI中介軟體,它可以保證透過 /gradio 訪問到Gradio程式,透過 / 訪問到 Django 程式。
如果我們使用下面的這種方式來代理 Django,實測將不能透過 /gradio 訪問到Gradio程式,無論 Gradio 和 Django 誰先註冊。如果你的環境可以,歡迎留下你的各個 package 的版本。
app.mount("/", WSGIMiddleware(django_app))
靜態檔案的訪問
因為靜態檔案是每個Web程式幾乎避不開的,比如圖片、css、js等,所以這裡特別提下。
在上邊的路由中介軟體中,除了 /gradio 會路由到Gradio程式,其它都會走Django進行處理,靜態檔案也不例外。
這裡假設靜態檔案放在 static 目錄下。
開啟 myproject/settings.py,這是 Django 專案的基礎設定檔案,修改其中靜態檔案的部分:
STATIC_URL = '/static/'
if DEBUG:
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
else:
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
開啟 myproject/urls.py,修改其中的路由定義,增加 re_path 這一行。
urlpatterns = [
re_path('^static/(?P<path>.*)', serve, {'document_root': settings.STATIC_ROOT}),
path('', include('myapp.urls')), # 包含 myapp 的 URL 配置
]
這樣可以在調測和生產環境都能正常訪問 static 目錄下的靜態檔案,而不用再進行不同的設定。
總結
本文分享了一種整合 Gradio 和 Django 程式的方法,在這種方法下,Gradio 和 Django 可以使用同一個程序,使用相同的埠號對外服務,同時Gradio程式使用子目錄 /gradio 進行訪問,Django 程式使用根目錄 / 進行訪問。
因本人對 Django 和 Gradio 的瞭解有限,文中介紹的方法可能存在瑕疵,請謹慎使用。
關注螢火架構,加速技術提升!