是否可以将 FastAPI 与 Django 一起使用?

Is it possible to use FastAPI with Django?

我是一名 Django 开发人员,最近偶然发现了 FastAPI 框架。

然后我决定试一试。但通常当您谈论使用 Django 构建 RESTful API 时,您通常会使用 Django Rest Framework (DRF)。

是否有人知道是否可以使用 Django 特权(如其 ORM)将 DRF 替换为 FastAPI,并且仍然可以访问 FastAPI 的所有 async 功能?

到目前为止我只找到一篇这方面的文章。但是在集成的过程中作者失去了FastAPI的大部分特性。 You can find it here.

在 FastAPI 文档中,他们确实提到可以将某些请求重定向到 WSGI 应用程序 here

简答

是的,可能 WSGIMiddleware

例如,您可以使用所有 Django 功能 (也是管理员) 挂载,这个例子代码.

import os
from importlib.util import find_spec

from configurations.wsgi import get_wsgi_application
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from fastapi.staticfiles import StaticFiles

from api import router

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")
os.environ.setdefault("DJANGO_CONFIGURATIN", "Localdev")

application = get_wsgi_application()

app = FastAPI()
app.mount("/admin", WSGIMiddleware(application))
app.mount("/static"
    StaticFiles(
         directory=os.path.normpath(
              os.path.join(find_spec("django.contrib.admin").origin, "..", "static")
         )
   ),
   name="static",
)

这个也来自 WSGIMiddleware documentation,它是一个更 straight-forward 的例子(这个是针对 Flask 的,但它展示了相同的想法。)。

from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from flask import Flask, escape, request

flask_app = Flask(__name__)


@flask_app.route("/")
def flask_main():
    name = request.args.get("name", "World")
    return f"Hello, {escape(name)} from Flask!"


app = FastAPI()


@app.get("/v2")
def read_main():
    return {"message": "Hello World"}


app.mount("/v1", WSGIMiddleware(flask_app))

感谢您的精彩回答。这是一个稍微调整过的答案,我修复了一些导入,并使用了 Django 应用程序中的模型。

from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from django.core.wsgi import get_wsgi_application
import os
from importlib.util import find_spec
from fastapi.staticfiles import StaticFiles
from django.conf import settings


# Export Django settings env variable
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

# Get Django WSGI app
django_app = get_wsgi_application()

# Import a model
# And always import your models after you export settings
# and you get Django WSGI app
from accounts.models import Account

# Create FasatAPI instance
app = FastAPI()

# Serve Django static files
app.mount('/static',
    StaticFiles(
         directory=os.path.normpath(
              os.path.join(find_spec('django.contrib.admin').origin, '..', 'static')
         )
   ),
   name='static',
)

# Define a FastAPI route
@app.get('/fastapi-test')
def read_main():
    return {
        'total_accounts': Account.objects.count(),
        'is_debug': settings.DEBUG 
    }

# Mount Django app
app.mount('/django-test', WSGIMiddleware(django_app))

提示:我在我的 Django 项目的根目录中创建了一个名为 app.py 的文件并且它可以工作。这是我的目录结构:

.
├── accounts
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── app.py
├── db.sqlite3
├── project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

并且 运行 您的 FastAPI 应用程序:

(myvenv) ➜  project uvicorn --host 0.0.0.0 --port 8000 app:app --reload
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [48366] using statreload
INFO:     Started server process [48368]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

希望这对您有用。现在访问 /django-test 将为您的 Django 项目服务,/fastapi-test 将为 FastAPI 部分服务。

此配置也为 Django 静态文件提供服务,我们也可以在我们的 FastAPI 代码中使用我们的 Django 模型。我会进一步测试它,如果我发现任何改进的可能性,我会更新这个答案。

有好的资源如下:

Combine the power of FastAPI and Django

FastAPI 与 Django (WSGI) 的集成

https://github.com/jordaneremieff/django-fastapi-example.git

经过数小时的搜索,我终于在上面的 link 中找到了一个很好的实现。它对我来说工作得天衣无缝!

测试

为了便于测试,下面是我对上述参考资料所做的一些调整:

api/models.py

class Item(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()
    # owner = models.ForeignKey(
    #     settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="items"
    # )

api/schemas.py

class Item(ItemBase):
    # id: int
    # owner_id: int

    class Config:
        orm_mode = True

POST

curl -d "{\"title\":\"le titre\", \"description\":\"la description\"}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8000/api/items

GET

curl http://127.0.0.1:8000/api/items