django-fastapi-bridge


Namedjango-fastapi-bridge JSON
Version 0.2 PyPI version JSON
download
home_page
SummaryBuild FastAPI app top of Django
upload_time2023-09-10 23:48:25
maintainer
docs_urlNone
author
requires_python>=3.9
licenseCopyright 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.
keywords web python django fastapi wrapper bridge
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "django-fastapi-bridge",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "",
    "keywords": "web,python,django,fastapi,wrapper,bridge",
    "author": "",
    "author_email": "Marcin Nowak <marcin.j.nowak@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/b2/3e/f7276e848bbd4bd745e63cee30feba0911d25e15ccdaf15cf78539128d50/django-fastapi-bridge-0.2.tar.gz",
    "platform": null,
    "description": "# Django FastAPI Bridge\n\nAllows to use [FastAPI](fastapi.tiangolo.com/) top of\n[Django](https://www.djangoproject.com/)\n\n## Rationale\n\n* Use Django as a core for your app logic, admin panel and multiple\n  addons available for years (\"batteries included\"), then create a web\n  interface with FastAPI (\"batteries also included\")\n* Add a web interface built with FastAPI to your existing Django\n  project, until you migrate it to fully ASGI compatible application\n* Automatically create documentation and OpenAPI schema with FastAPI\n* Both Django and FastAPI are mature, stable, supported and well known products\n  with large communities. No need to use other tools nor rely on less or more\n  similar clones.\n* Eventually run the same Django project in full ASGI mode to enable async\n  features like long-polling (although you must carefully prepare your\n  project for such cases)\n\n## TODO\n\n* [x] Initial ASGI support\n* [x] WSGI support\n* [ ] Authentication and authorization bridge\n* [ ] Utils for serialization and updating ORM models\n* [ ] Exception handler for DEBUG mode\n* [ ] Replacement for most common shortcut / helpers\n* [ ] Tests and stabilization\n\n## Quickstart\n\nInstall FastAPI:\n\n`pip install fastapi`\n\nInstall required packages:\n\n`pip install django-fastapi-bridge`\n\nAdd `django_fastapi` to `INSTALLED_APPS` in your `settings.py` file:\n\n```\nINSTALLED_APPS = [\n    # ...\n    \"django_fastapi\",\n    # ...\n]\n```\n\nSet `ASGI_APPLICATION` and `WSGI_APPLICATION` in your `settings.py` file:\n\n```\nASGI_APPLICATION = \"django_fastapi.asgi.application\"\nWSGI_APPLICATION = \"django_fastapi.wsgi.application\"\n```\n\n### Development server (WSGI)\n\nUse Django's `runserver` command as usual\n\nNotes:\n- ASGI app is wrapped to WSGI interface through `a2wsgi.ASGIMiddleware`\n- Django's `runserver` will not print tracebacks to the stdout and tracebacks aren't generated by FastAPI/Bridge (this should be addressed later)\n\n\n### Development server (ASGI w/Daphne)\n\n\nInstall `daphne`, then add `daphne` to the `INSTALLED_APPS` before\n`django.core.staticfiles`.\n\nRun development server:\n\n```\npython manage.py runserver\n```\n\n### Production server (ASGI w/Uvicorn)\n\nInstall Uvicorn:\n\n```\npip install uvicorn\n```\n\nRun uvicorn:\n\n```\nDJANGO_SETTINGS_MODULE=yourproject.settings uvicorn django_fastapi.asgi:application\n```\n\n### Production server (WSGI w/Uvicorn, not recommended)\n\nInstall Uvicorn:\n\n```\npip install uvicorn\n```\n\nRun uvicorn:\n\n```\nDJANGO_SETTINGS_MODULE=yourproject.settings uvicorn --interface wsgi django_fastapi.wsgi:application\n```\n\n*Running the application in WSGI mode will be safest than in ASGI mode, which relies on protections\nbuilt into Django, but the overall performance can be more than twice as low.*\n\n## Configuration\n\n### Base settings\n\nTo configure default `FastAPI` instance you can use these settings:\n\n * `FASTAPI_TITLE`: set's API title [`FastAPI(title=FASTAPI_TITLE)`]\n * `FASTAPI_VERSION`: set's API version [`FastAPI(version=FASTAPI_VERSION)`]\n * `FASTAPI_ROOT_PATH`: set's API root path [`FastAPI(root_path=FASTAPI_ROOT_PATH)`]\n\n### CORS\n\n * `FASTAPI_CORS_ENABLED`: if True, adds CORS middleware to the default\n   FastAPI instance (disabled by default)\n * `FASTAPI_CORS_ALLOW_ORIGINS`: defaults to `[\"*\"]`\n * `FASTAPI_CORS_ALLOW_CREDENTIALS`: defaults to `True`\n * `FASTAPI_CORS_ALLOW_METHODS`: defaults to `[\"*\"]`\n * `FASTAPI_CORS_ALLOW_HEADERS`: defaults to `[\"*\"]`\n\n### Autodiscover\n\n * `FASTAPI_AUTODISCOVER`: if True, Django FastAPI will automatically\n   import `FASTAPI_AUTODISCOVER_MODULES` from your `INSTALLED_APPS`.\n   Default: `True`\n * `FASTAPI_AUTODISCOVER_MODULES`: defaults to `[\"api\"]`\n\n## Examples and performance\n\n*See [example/test_api/api.py](example/test_api/api.py) for more examples*\n\n### Querying a database in sync mode\n\nWrite your handler as usual:\n\n```python\n@api.get(\"/permissions\", response_model=PermissionListResource)\ndef permissions_list():\n    queryset = Permission.objects.all()\n    items = [{\"pk\": obj.pk, \"name\": obj.name} for obj in queryset]\n    return PermissionListResource(items=items)\n```\n\n### Querying a database in async mode\n\nPrepend your function with `async` keyword and query the database in\nan async way. Because `QuerySet` is a lazy evaluated object, it will hit\nthe database during creating list of items. That's why you must use\n`async for` in the list comprehension:\n\n```python\n@api.get(\"/async/permissions\", response_model=PermissionListResource)\nasync def permissions_list():\n    queryset = Permission.objects.all()\n    items = [{\"pk\": obj.pk, \"name\": obj.name} async for obj in queryset]\n    return PermissionListResource(items=items)\n```\n\n### Performance comparison\n\nSync mode:\n\n```\nConcurrency Level:      10\nTime taken for tests:   15.217 seconds\nComplete requests:      10000\nFailed requests:        0\nRequests per second:    657.16 [#/sec] (mean)\nTime per request:       15.217 [ms] (mean)\n```\n\nAsync mode:\n\n```\nConcurrency Level:      10\nTime taken for tests:   6.374 seconds\nComplete requests:      10000\nFailed requests:        0\nRequests per second:    1568.97 [#/sec] (mean)\n```\n\nTesting platform:\n\n```\nMachine:\n  Type: Desktop Mobo: Micro-Star model: B550-A PRO (MS-7C56) v: 2.0\nMemory:\n  System RAM: total: 32 GiB available: 31.27 GiB used: 9.44 GiB (30.2%)\nCPU:\n  Info: 12-core model: AMD Ryzen 9 5900X bits: 64 type: MT MCP cache:\n    L2: 6 MiB\n  Speed (MHz): avg: 2425 min/max: 2200/4950 cores: 1: 3598 2: 2869 3: 2874\n    4: 2200 5: 2819 6: 2200 7: 2200 8: 2200 9: 2200 10: 2200 11: 2199 12: 2199\n    13: 3584 14: 2200 15: 2879 16: 2200 17: 2200 18: 2200 19: 2200 20: 2200\n    21: 2200 22: 2200 23: 2200 24: 2200\n```\n\nServer command:\n\n```\ncd example\nDJANGO_SETTINGS_MODULE=fastapi_bridge_test.settings uvicorn django_fastapi.asgi:application\n```\n\nSoftware versions:\n- Python 3.11.3\n- Django 4.2.5\n- Linux 6.1.49-1-MANJARO\n\n## Important notes\n\nWith the bridge a typical Django's server stack (middlewares, requests,\nresponses) will not be used. This causes some incompatibilities, but in\na this should not be an issue while building HTTP APIs top of Django:\n\n* The interface of FastAPI callbacks will never be\n  same as Django's views interface (i.e. no mandatory `request`\n  argument)\n* The `request` object used will be FastAPI/Starlette's `Request`\n  object, not Django one\n* Django middlewares can't be used directly\n* Context processors based on request objects will mostly fail\n* All helpers / shortcut functions based on Django's original request\n  will not work\n* Your application can run in synchronous mode with a\n  potential performance cost, until you use fully async code\n  (see \"Why this works\" notes below). NB: This is similar how all\n  alternatives based on ASGI protocol works.\n\n## Why this works\n\nThis bridge disables Django's HTTP server and uses FastAPI/Starlette directly.\nAt this moment Django has partial but stable support for the asynchonous mode,\nand for a compatibility reasons it can switch between sync and asynchronous mode\nautomatically. In cases where you are mixing sync and async calls, you must\nwrap them with `async_to_sync` or `sync_to_async` from `asgiref.sync` module.\n\nCurrent versions of Django (4.1+) handles unsafe non-async calls very nicely, so\nthere are no errors known from the past. Django's async-unsafe parts are protected\nfrom execution in async environment and will raise `SynchronousOnlyOperation`\nexception. In case of third party apps/addons you must make sure they're async-safe\nor protected.\n\n*Please note that switching between sync and async modes comes at a cost.\nAlthough Django tries to optimize the number of context switches, in some\nconfigurations there may be more than one, which may result in a performance\npenalty.*\n\nMore details can be found in the Django documentation:\nhttps://docs.djangoproject.com/en/4.2/topics/async/\n\n## Alternatives\n\n* [Django Ninja](https://pypi.org/project/django-ninja/) - REST framework for Django inspired by FastAPI\n\n## License\n\nISC\n\nCopyright 2023 Marcin Nowak <marcin.j.nowak.gmail.com>\n\nPermission 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.\n\nTHE SOFTWARE IS PROVIDED \u201cAS IS\u201d 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.\n",
    "bugtrack_url": null,
    "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 \u201cAS IS\u201d 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. ",
    "summary": "Build FastAPI app top of Django",
    "version": "0.2",
    "project_urls": {
        "homepage": "https://gitlab.com/marcinjn/django-fastapi",
        "repository": "https://gitlab.com/marcinjn/django-fastapi"
    },
    "split_keywords": [
        "web",
        "python",
        "django",
        "fastapi",
        "wrapper",
        "bridge"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f5b28019d7def8446a460ab381fc38e7948758e74f9daa8f58edc73469506425",
                "md5": "de5d882f9beb2ee917e54afb30ebb5c7",
                "sha256": "bdd5ae651cc1164ec42d00ed8f859e662977ce6666e68ced5bd5c7298840f32a"
            },
            "downloads": -1,
            "filename": "django_fastapi_bridge-0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "de5d882f9beb2ee917e54afb30ebb5c7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 8991,
            "upload_time": "2023-09-10T23:48:24",
            "upload_time_iso_8601": "2023-09-10T23:48:24.042431Z",
            "url": "https://files.pythonhosted.org/packages/f5/b2/8019d7def8446a460ab381fc38e7948758e74f9daa8f58edc73469506425/django_fastapi_bridge-0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b23ef7276e848bbd4bd745e63cee30feba0911d25e15ccdaf15cf78539128d50",
                "md5": "5a3a456bb071ab70eb006ff6fdb7aa4f",
                "sha256": "21c82c68c557f0c986383889429e01ee105ed98a452e73dce4c0bda2e446d0b2"
            },
            "downloads": -1,
            "filename": "django-fastapi-bridge-0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "5a3a456bb071ab70eb006ff6fdb7aa4f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 10659,
            "upload_time": "2023-09-10T23:48:25",
            "upload_time_iso_8601": "2023-09-10T23:48:25.860596Z",
            "url": "https://files.pythonhosted.org/packages/b2/3e/f7276e848bbd4bd745e63cee30feba0911d25e15ccdaf15cf78539128d50/django-fastapi-bridge-0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-09-10 23:48:25",
    "github": false,
    "gitlab": true,
    "bitbucket": false,
    "codeberg": false,
    "gitlab_user": "marcinjn",
    "gitlab_project": "django-fastapi",
    "lcname": "django-fastapi-bridge"
}
        
Elapsed time: 0.22851s