Metadata-Version: 2.1
Name: django-fastapi-bridge
Version: 0.2
Summary: Build FastAPI app top of Django
Author-email: Marcin Nowak <marcin.j.nowak@gmail.com>
License: Copyright 2023 Marcin Nowak <marcin.j.nowak.gmail.com>
        
        Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
        
        THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        
Project-URL: homepage, https://gitlab.com/marcinjn/django-fastapi
Project-URL: repository, https://gitlab.com/marcinjn/django-fastapi
Keywords: web,python,django,fastapi,wrapper,bridge
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 3.1
Classifier: Framework :: Django :: 3.2
Classifier: Framework :: Django :: 4.0
Classifier: Framework :: Django :: 4.1
Classifier: Framework :: Django :: 4.2
Classifier: License :: OSI Approved :: ISC License (ISCL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Intended Audience :: Developers
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: fastapi
Requires-Dist: django
Requires-Dist: a2wsgi
Provides-Extra: daphne
Requires-Dist: daphne; extra == "daphne"

# Django FastAPI Bridge

Allows to use [FastAPI](fastapi.tiangolo.com/) top of
[Django](https://www.djangoproject.com/)

## Rationale

* Use Django as a core for your app logic, admin panel and multiple
  addons available for years ("batteries included"), then create a web
  interface with FastAPI ("batteries also included")
* Add a web interface built with FastAPI to your existing Django
  project, until you migrate it to fully ASGI compatible application
* Automatically create documentation and OpenAPI schema with FastAPI
* Both Django and FastAPI are mature, stable, supported and well known products
  with large communities. No need to use other tools nor rely on less or more
  similar clones.
* Eventually run the same Django project in full ASGI mode to enable async
  features like long-polling (although you must carefully prepare your
  project for such cases)

## TODO

* [x] Initial ASGI support
* [x] WSGI support
* [ ] Authentication and authorization bridge
* [ ] Utils for serialization and updating ORM models
* [ ] Exception handler for DEBUG mode
* [ ] Replacement for most common shortcut / helpers
* [ ] Tests and stabilization

## Quickstart

Install FastAPI:

`pip install fastapi`

Install required packages:

`pip install django-fastapi-bridge`

Add `django_fastapi` to `INSTALLED_APPS` in your `settings.py` file:

```
INSTALLED_APPS = [
    # ...
    "django_fastapi",
    # ...
]
```

Set `ASGI_APPLICATION` and `WSGI_APPLICATION` in your `settings.py` file:

```
ASGI_APPLICATION = "django_fastapi.asgi.application"
WSGI_APPLICATION = "django_fastapi.wsgi.application"
```

### Development server (WSGI)

Use Django's `runserver` command as usual

Notes:
- ASGI app is wrapped to WSGI interface through `a2wsgi.ASGIMiddleware`
- Django's `runserver` will not print tracebacks to the stdout and tracebacks aren't generated by FastAPI/Bridge (this should be addressed later)


### Development server (ASGI w/Daphne)


Install `daphne`, then add `daphne` to the `INSTALLED_APPS` before
`django.core.staticfiles`.

Run development server:

```
python manage.py runserver
```

### Production server (ASGI w/Uvicorn)

Install Uvicorn:

```
pip install uvicorn
```

Run uvicorn:

```
DJANGO_SETTINGS_MODULE=yourproject.settings uvicorn django_fastapi.asgi:application
```

### Production server (WSGI w/Uvicorn, not recommended)

Install Uvicorn:

```
pip install uvicorn
```

Run uvicorn:

```
DJANGO_SETTINGS_MODULE=yourproject.settings uvicorn --interface wsgi django_fastapi.wsgi:application
```

*Running the application in WSGI mode will be safest than in ASGI mode, which relies on protections
built into Django, but the overall performance can be more than twice as low.*

## Configuration

### Base settings

To configure default `FastAPI` instance you can use these settings:

 * `FASTAPI_TITLE`: set's API title [`FastAPI(title=FASTAPI_TITLE)`]
 * `FASTAPI_VERSION`: set's API version [`FastAPI(version=FASTAPI_VERSION)`]
 * `FASTAPI_ROOT_PATH`: set's API root path [`FastAPI(root_path=FASTAPI_ROOT_PATH)`]

### CORS

 * `FASTAPI_CORS_ENABLED`: if True, adds CORS middleware to the default
   FastAPI instance (disabled by default)
 * `FASTAPI_CORS_ALLOW_ORIGINS`: defaults to `["*"]`
 * `FASTAPI_CORS_ALLOW_CREDENTIALS`: defaults to `True`
 * `FASTAPI_CORS_ALLOW_METHODS`: defaults to `["*"]`
 * `FASTAPI_CORS_ALLOW_HEADERS`: defaults to `["*"]`

### Autodiscover

 * `FASTAPI_AUTODISCOVER`: if True, Django FastAPI will automatically
   import `FASTAPI_AUTODISCOVER_MODULES` from your `INSTALLED_APPS`.
   Default: `True`
 * `FASTAPI_AUTODISCOVER_MODULES`: defaults to `["api"]`

## Examples and performance

*See [example/test_api/api.py](example/test_api/api.py) for more examples*

### Querying a database in sync mode

Write your handler as usual:

```python
@api.get("/permissions", response_model=PermissionListResource)
def permissions_list():
    queryset = Permission.objects.all()
    items = [{"pk": obj.pk, "name": obj.name} for obj in queryset]
    return PermissionListResource(items=items)
```

### Querying a database in async mode

Prepend your function with `async` keyword and query the database in
an async way. Because `QuerySet` is a lazy evaluated object, it will hit
the database during creating list of items. That's why you must use
`async for` in the list comprehension:

```python
@api.get("/async/permissions", response_model=PermissionListResource)
async def permissions_list():
    queryset = Permission.objects.all()
    items = [{"pk": obj.pk, "name": obj.name} async for obj in queryset]
    return PermissionListResource(items=items)
```

### Performance comparison

Sync mode:

```
Concurrency Level:      10
Time taken for tests:   15.217 seconds
Complete requests:      10000
Failed requests:        0
Requests per second:    657.16 [#/sec] (mean)
Time per request:       15.217 [ms] (mean)
```

Async mode:

```
Concurrency Level:      10
Time taken for tests:   6.374 seconds
Complete requests:      10000
Failed requests:        0
Requests per second:    1568.97 [#/sec] (mean)
```

Testing platform:

```
Machine:
  Type: Desktop Mobo: Micro-Star model: B550-A PRO (MS-7C56) v: 2.0
Memory:
  System RAM: total: 32 GiB available: 31.27 GiB used: 9.44 GiB (30.2%)
CPU:
  Info: 12-core model: AMD Ryzen 9 5900X bits: 64 type: MT MCP cache:
    L2: 6 MiB
  Speed (MHz): avg: 2425 min/max: 2200/4950 cores: 1: 3598 2: 2869 3: 2874
    4: 2200 5: 2819 6: 2200 7: 2200 8: 2200 9: 2200 10: 2200 11: 2199 12: 2199
    13: 3584 14: 2200 15: 2879 16: 2200 17: 2200 18: 2200 19: 2200 20: 2200
    21: 2200 22: 2200 23: 2200 24: 2200
```

Server command:

```
cd example
DJANGO_SETTINGS_MODULE=fastapi_bridge_test.settings uvicorn django_fastapi.asgi:application
```

Software versions:
- Python 3.11.3
- Django 4.2.5
- Linux 6.1.49-1-MANJARO

## Important notes

With the bridge a typical Django's server stack (middlewares, requests,
responses) will not be used. This causes some incompatibilities, but in
a this should not be an issue while building HTTP APIs top of Django:

* The interface of FastAPI callbacks will never be
  same as Django's views interface (i.e. no mandatory `request`
  argument)
* The `request` object used will be FastAPI/Starlette's `Request`
  object, not Django one
* Django middlewares can't be used directly
* Context processors based on request objects will mostly fail
* All helpers / shortcut functions based on Django's original request
  will not work
* Your application can run in synchronous mode with a
  potential performance cost, until you use fully async code
  (see "Why this works" notes below). NB: This is similar how all
  alternatives based on ASGI protocol works.

## Why this works

This bridge disables Django's HTTP server and uses FastAPI/Starlette directly.
At this moment Django has partial but stable support for the asynchonous mode,
and for a compatibility reasons it can switch between sync and asynchronous mode
automatically. In cases where you are mixing sync and async calls, you must
wrap them with `async_to_sync` or `sync_to_async` from `asgiref.sync` module.

Current versions of Django (4.1+) handles unsafe non-async calls very nicely, so
there are no errors known from the past. Django's async-unsafe parts are protected
from execution in async environment and will raise `SynchronousOnlyOperation`
exception. In case of third party apps/addons you must make sure they're async-safe
or protected.

*Please note that switching between sync and async modes comes at a cost.
Although Django tries to optimize the number of context switches, in some
configurations there may be more than one, which may result in a performance
penalty.*

More details can be found in the Django documentation:
https://docs.djangoproject.com/en/4.2/topics/async/

## Alternatives

* [Django Ninja](https://pypi.org/project/django-ninja/) - REST framework for Django inspired by FastAPI

## License

ISC

Copyright 2023 Marcin Nowak <marcin.j.nowak.gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
